Jiri Simsa | b04fbb2 | 2014-06-27 17:38:04 -0700 | [diff] [blame] | 1 | // The implementation of the binary repository interface stores |
Bogdan Caprita | d9281a3 | 2014-07-02 14:40:39 -0700 | [diff] [blame] | 2 | // objects identified by object name suffixes using the local file |
| 3 | // system. Given an object name suffix, the implementation computes an |
Jiri Simsa | b04fbb2 | 2014-06-27 17:38:04 -0700 | [diff] [blame] | 4 | // MD5 hash of the suffix and generates the following path in the |
Jiri Simsa | 432cc2e | 2014-12-08 15:53:38 -0800 | [diff] [blame] | 5 | // local filesystem: /<root_dir>/<dir_1>/.../<dir_n>/<hash>. The root |
| 6 | // directory and the directory depth are parameters of the |
| 7 | // implementation. The contents of the directory include the checksum |
| 8 | // and data for each of the individual parts of the binary, and the |
| 9 | // name of the object: |
Jiri Simsa | b04fbb2 | 2014-06-27 17:38:04 -0700 | [diff] [blame] | 10 | // |
Robin Thellend | c5856f6 | 2014-11-17 10:30:54 -0800 | [diff] [blame] | 11 | // name |
Jiri Simsa | b04fbb2 | 2014-06-27 17:38:04 -0700 | [diff] [blame] | 12 | // <part_1>/checksum |
| 13 | // <part_1>/data |
| 14 | // ... |
| 15 | // <part_n>/checksum |
| 16 | // <part_n>/data |
| 17 | // |
| 18 | // TODO(jsimsa): Add an "fsck" method that cleans up existing on-disk |
| 19 | // repository and provide a command-line flag that identifies whether |
| 20 | // fsck should run when new repository server process starts up. |
| 21 | package impl |
| 22 | |
| 23 | import ( |
| 24 | "crypto/md5" |
| 25 | "encoding/hex" |
Robin Thellend | e262789 | 2014-11-26 09:34:37 -0800 | [diff] [blame] | 26 | "encoding/json" |
Jiri Simsa | b04fbb2 | 2014-06-27 17:38:04 -0700 | [diff] [blame] | 27 | "io" |
| 28 | "io/ioutil" |
| 29 | "os" |
| 30 | "path/filepath" |
Robin Thellend | c5856f6 | 2014-11-17 10:30:54 -0800 | [diff] [blame] | 31 | "strings" |
Jiri Simsa | b04fbb2 | 2014-06-27 17:38:04 -0700 | [diff] [blame] | 32 | "syscall" |
| 33 | |
Jiri Simsa | 519c507 | 2014-09-17 21:37:57 -0700 | [diff] [blame] | 34 | "veyron.io/veyron/veyron2/ipc" |
| 35 | "veyron.io/veyron/veyron2/services/mgmt/binary" |
| 36 | "veyron.io/veyron/veyron2/services/mgmt/repository" |
| 37 | "veyron.io/veyron/veyron2/verror" |
| 38 | "veyron.io/veyron/veyron2/vlog" |
Jiri Simsa | b04fbb2 | 2014-06-27 17:38:04 -0700 | [diff] [blame] | 39 | ) |
| 40 | |
Robin Thellend | 9bc8fcb | 2014-11-17 10:23:04 -0800 | [diff] [blame] | 41 | // binaryService implements the Binary server interface. |
| 42 | type binaryService struct { |
Jiri Simsa | b04fbb2 | 2014-06-27 17:38:04 -0700 | [diff] [blame] | 43 | // path is the local filesystem path to the object identified by the |
Bogdan Caprita | d9281a3 | 2014-07-02 14:40:39 -0700 | [diff] [blame] | 44 | // object name suffix. |
Jiri Simsa | b04fbb2 | 2014-06-27 17:38:04 -0700 | [diff] [blame] | 45 | path string |
| 46 | // state holds the state shared across different binary repository |
| 47 | // invocations. |
| 48 | state *state |
Robin Thellend | 9bc8fcb | 2014-11-17 10:23:04 -0800 | [diff] [blame] | 49 | // suffix is the name of the binary object. |
Jiri Simsa | b04fbb2 | 2014-06-27 17:38:04 -0700 | [diff] [blame] | 50 | suffix string |
| 51 | } |
| 52 | |
Jiri Simsa | b04fbb2 | 2014-06-27 17:38:04 -0700 | [diff] [blame] | 53 | var ( |
Jiri Simsa | 518ef15 | 2014-08-08 18:07:55 -0700 | [diff] [blame] | 54 | errExists = verror.Existsf("binary already exists") |
Tilak Sharma | 492e8e9 | 2014-09-18 10:58:14 -0700 | [diff] [blame] | 55 | errNotFound = verror.NoExistf("binary not found") |
Jiri Simsa | 518ef15 | 2014-08-08 18:07:55 -0700 | [diff] [blame] | 56 | errInProgress = verror.Internalf("identical upload already in progress") |
| 57 | errInvalidParts = verror.BadArgf("invalid number of binary parts") |
Bogdan Caprita | e783dcc | 2014-11-04 14:16:55 -0800 | [diff] [blame] | 58 | errInvalidPart = verror.BadArgf("invalid binary part number") |
Jiri Simsa | 518ef15 | 2014-08-08 18:07:55 -0700 | [diff] [blame] | 59 | errOperationFailed = verror.Internalf("operation failed") |
Jiri Simsa | b04fbb2 | 2014-06-27 17:38:04 -0700 | [diff] [blame] | 60 | ) |
| 61 | |
| 62 | // TODO(jsimsa): When VDL supports composite literal constants, remove |
| 63 | // this definition. |
| 64 | var MissingPart = binary.PartInfo{ |
| 65 | Checksum: binary.MissingChecksum, |
| 66 | Size: binary.MissingSize, |
| 67 | } |
| 68 | |
Robin Thellend | 9bc8fcb | 2014-11-17 10:23:04 -0800 | [diff] [blame] | 69 | // newBinaryService returns a new Binary service implementation. |
| 70 | func newBinaryService(state *state, suffix string) *binaryService { |
| 71 | return &binaryService{ |
Bogdan Caprita | 1c62625 | 2014-11-05 14:29:14 -0800 | [diff] [blame] | 72 | path: state.dir(suffix), |
Jiri Simsa | b04fbb2 | 2014-06-27 17:38:04 -0700 | [diff] [blame] | 73 | state: state, |
| 74 | suffix: suffix, |
| 75 | } |
| 76 | } |
| 77 | |
Jiri Simsa | b04fbb2 | 2014-06-27 17:38:04 -0700 | [diff] [blame] | 78 | const bufferLength = 4096 |
| 79 | |
Robin Thellend | e262789 | 2014-11-26 09:34:37 -0800 | [diff] [blame] | 80 | func (i *binaryService) Create(_ ipc.ServerContext, nparts int32, mediaInfo repository.MediaInfo) error { |
| 81 | vlog.Infof("%v.Create(%v, %v)", i.suffix, nparts, mediaInfo) |
Jiri Simsa | b04fbb2 | 2014-06-27 17:38:04 -0700 | [diff] [blame] | 82 | if nparts < 1 { |
| 83 | return errInvalidParts |
| 84 | } |
| 85 | parent, perm := filepath.Dir(i.path), os.FileMode(0700) |
| 86 | if err := os.MkdirAll(parent, perm); err != nil { |
| 87 | vlog.Errorf("MkdirAll(%v, %v) failed: %v", parent, perm, err) |
| 88 | return errOperationFailed |
| 89 | } |
| 90 | prefix := "creating-" |
| 91 | tmpDir, err := ioutil.TempDir(parent, prefix) |
| 92 | if err != nil { |
| 93 | vlog.Errorf("TempDir(%v, %v) failed: %v", parent, prefix, err) |
| 94 | return errOperationFailed |
| 95 | } |
Robin Thellend | c5856f6 | 2014-11-17 10:30:54 -0800 | [diff] [blame] | 96 | nameFile := filepath.Join(tmpDir, "name") |
| 97 | if err := ioutil.WriteFile(nameFile, []byte(i.suffix), os.FileMode(0600)); err != nil { |
| 98 | vlog.Errorf("WriteFile(%q) failed: %v", nameFile) |
| 99 | return errOperationFailed |
| 100 | } |
Robin Thellend | e262789 | 2014-11-26 09:34:37 -0800 | [diff] [blame] | 101 | infoFile := filepath.Join(tmpDir, "mediainfo") |
| 102 | jInfo, err := json.Marshal(mediaInfo) |
| 103 | if err != nil { |
| 104 | vlog.Errorf("json.Marshal(%v) failed: %v", mediaInfo, err) |
| 105 | return errOperationFailed |
| 106 | } |
| 107 | if err := ioutil.WriteFile(infoFile, jInfo, os.FileMode(0600)); err != nil { |
| 108 | vlog.Errorf("WriteFile(%q) failed: %v", infoFile, err) |
| 109 | return errOperationFailed |
| 110 | } |
Jiri Simsa | b04fbb2 | 2014-06-27 17:38:04 -0700 | [diff] [blame] | 111 | for j := 0; j < int(nparts); j++ { |
Bogdan Caprita | e783dcc | 2014-11-04 14:16:55 -0800 | [diff] [blame] | 112 | partPath, partPerm := generatePartPath(tmpDir, j), os.FileMode(0700) |
Jiri Simsa | b04fbb2 | 2014-06-27 17:38:04 -0700 | [diff] [blame] | 113 | if err := os.MkdirAll(partPath, partPerm); err != nil { |
| 114 | vlog.Errorf("MkdirAll(%v, %v) failed: %v", partPath, partPerm, err) |
| 115 | if err := os.RemoveAll(tmpDir); err != nil { |
| 116 | vlog.Errorf("RemoveAll(%v) failed: %v", tmpDir, err) |
| 117 | } |
| 118 | return errOperationFailed |
| 119 | } |
| 120 | } |
| 121 | // Use os.Rename() to atomically create the binary directory |
| 122 | // structure. |
| 123 | if err := os.Rename(tmpDir, i.path); err != nil { |
| 124 | defer func() { |
| 125 | if err := os.RemoveAll(tmpDir); err != nil { |
| 126 | vlog.Errorf("RemoveAll(%v) failed: %v", tmpDir, err) |
| 127 | } |
| 128 | }() |
Jiri Simsa | 518ef15 | 2014-08-08 18:07:55 -0700 | [diff] [blame] | 129 | if linkErr, ok := err.(*os.LinkError); ok && linkErr.Err == syscall.ENOTEMPTY { |
| 130 | return errExists |
Jiri Simsa | b04fbb2 | 2014-06-27 17:38:04 -0700 | [diff] [blame] | 131 | } |
| 132 | vlog.Errorf("Rename(%v, %v) failed: %v", tmpDir, i.path, err) |
| 133 | return errOperationFailed |
| 134 | } |
| 135 | return nil |
| 136 | } |
| 137 | |
Robin Thellend | 9bc8fcb | 2014-11-17 10:23:04 -0800 | [diff] [blame] | 138 | func (i *binaryService) Delete(context ipc.ServerContext) error { |
Jiri Simsa | b04fbb2 | 2014-06-27 17:38:04 -0700 | [diff] [blame] | 139 | vlog.Infof("%v.Delete()", i.suffix) |
| 140 | if _, err := os.Stat(i.path); err != nil { |
| 141 | if os.IsNotExist(err) { |
Jiri Simsa | 518ef15 | 2014-08-08 18:07:55 -0700 | [diff] [blame] | 142 | return errNotFound |
Jiri Simsa | b04fbb2 | 2014-06-27 17:38:04 -0700 | [diff] [blame] | 143 | } |
| 144 | vlog.Errorf("Stat(%v) failed: %v", i.path, err) |
| 145 | return errOperationFailed |
| 146 | } |
| 147 | // Use os.Rename() to atomically remove the binary directory |
| 148 | // structure. |
| 149 | path := filepath.Join(filepath.Dir(i.path), "removing-"+filepath.Base(i.path)) |
Jiri Simsa | b04fbb2 | 2014-06-27 17:38:04 -0700 | [diff] [blame] | 150 | if err := os.Rename(i.path, path); err != nil { |
| 151 | vlog.Errorf("Rename(%v, %v) failed: %v", i.path, path, err) |
| 152 | return errOperationFailed |
| 153 | } |
| 154 | if err := os.RemoveAll(path); err != nil { |
| 155 | vlog.Errorf("Remove(%v) failed: %v", path, err) |
| 156 | return errOperationFailed |
| 157 | } |
| 158 | for { |
| 159 | // Remove the binary and all directories on the path back to the |
Jiri Simsa | 432cc2e | 2014-12-08 15:53:38 -0800 | [diff] [blame] | 160 | // root directory that are left empty after the binary is removed. |
Jiri Simsa | b04fbb2 | 2014-06-27 17:38:04 -0700 | [diff] [blame] | 161 | path = filepath.Dir(path) |
Jiri Simsa | 432cc2e | 2014-12-08 15:53:38 -0800 | [diff] [blame] | 162 | if i.state.rootDir == path { |
Jiri Simsa | b04fbb2 | 2014-06-27 17:38:04 -0700 | [diff] [blame] | 163 | break |
| 164 | } |
| 165 | if err := os.Remove(path); err != nil { |
| 166 | if err.(*os.PathError).Err.Error() == syscall.ENOTEMPTY.Error() { |
| 167 | break |
| 168 | } |
| 169 | vlog.Errorf("Remove(%v) failed: %v", path, err) |
| 170 | return errOperationFailed |
| 171 | } |
| 172 | } |
| 173 | return nil |
| 174 | } |
| 175 | |
Robin Thellend | 9bc8fcb | 2014-11-17 10:23:04 -0800 | [diff] [blame] | 176 | func (i *binaryService) Download(context repository.BinaryDownloadContext, part int32) error { |
Jiri Simsa | b04fbb2 | 2014-06-27 17:38:04 -0700 | [diff] [blame] | 177 | vlog.Infof("%v.Download(%v)", i.suffix, part) |
| 178 | path := i.generatePartPath(int(part)) |
Bogdan Caprita | e783dcc | 2014-11-04 14:16:55 -0800 | [diff] [blame] | 179 | if err := checksumExists(path); err != nil { |
Jiri Simsa | b04fbb2 | 2014-06-27 17:38:04 -0700 | [diff] [blame] | 180 | return err |
| 181 | } |
| 182 | dataPath := filepath.Join(path, data) |
| 183 | file, err := os.Open(dataPath) |
| 184 | if err != nil { |
Bogdan Caprita | e783dcc | 2014-11-04 14:16:55 -0800 | [diff] [blame] | 185 | vlog.Errorf("Open(%v) failed: %v", dataPath, err) |
Jiri Simsa | b04fbb2 | 2014-06-27 17:38:04 -0700 | [diff] [blame] | 186 | return errOperationFailed |
| 187 | } |
| 188 | defer file.Close() |
| 189 | buffer := make([]byte, bufferLength) |
Todd Wang | 702385a | 2014-11-07 01:54:08 -0800 | [diff] [blame] | 190 | sender := context.SendStream() |
Jiri Simsa | b04fbb2 | 2014-06-27 17:38:04 -0700 | [diff] [blame] | 191 | for { |
| 192 | n, err := file.Read(buffer) |
| 193 | if err != nil && err != io.EOF { |
| 194 | vlog.Errorf("Read() failed: %v", err) |
| 195 | return errOperationFailed |
| 196 | } |
| 197 | if n == 0 { |
| 198 | break |
| 199 | } |
Shyam Jayaraman | 97b9dca | 2014-07-31 13:30:46 -0700 | [diff] [blame] | 200 | if err := sender.Send(buffer[:n]); err != nil { |
Jiri Simsa | b04fbb2 | 2014-06-27 17:38:04 -0700 | [diff] [blame] | 201 | vlog.Errorf("Send() failed: %v", err) |
| 202 | return errOperationFailed |
| 203 | } |
| 204 | } |
| 205 | return nil |
| 206 | } |
| 207 | |
Jiri Simsa | 432cc2e | 2014-12-08 15:53:38 -0800 | [diff] [blame] | 208 | // TODO(jsimsa): Design and implement an access control mechanism for |
| 209 | // the URL-based downloads. |
Robin Thellend | 9bc8fcb | 2014-11-17 10:23:04 -0800 | [diff] [blame] | 210 | func (i *binaryService) DownloadURL(ipc.ServerContext) (string, int64, error) { |
Jiri Simsa | b04fbb2 | 2014-06-27 17:38:04 -0700 | [diff] [blame] | 211 | vlog.Infof("%v.DownloadURL()", i.suffix) |
Jiri Simsa | 432cc2e | 2014-12-08 15:53:38 -0800 | [diff] [blame] | 212 | return i.state.rootURL + "/" + i.suffix, 0, nil |
Jiri Simsa | b04fbb2 | 2014-06-27 17:38:04 -0700 | [diff] [blame] | 213 | } |
| 214 | |
Robin Thellend | e262789 | 2014-11-26 09:34:37 -0800 | [diff] [blame] | 215 | func (i *binaryService) Stat(ipc.ServerContext) ([]binary.PartInfo, repository.MediaInfo, error) { |
Jiri Simsa | b04fbb2 | 2014-06-27 17:38:04 -0700 | [diff] [blame] | 216 | vlog.Infof("%v.Stat()", i.suffix) |
| 217 | result := make([]binary.PartInfo, 0) |
Bogdan Caprita | e783dcc | 2014-11-04 14:16:55 -0800 | [diff] [blame] | 218 | parts, err := getParts(i.path) |
Jiri Simsa | b04fbb2 | 2014-06-27 17:38:04 -0700 | [diff] [blame] | 219 | if err != nil { |
Robin Thellend | e262789 | 2014-11-26 09:34:37 -0800 | [diff] [blame] | 220 | return []binary.PartInfo{}, repository.MediaInfo{}, err |
Jiri Simsa | b04fbb2 | 2014-06-27 17:38:04 -0700 | [diff] [blame] | 221 | } |
| 222 | for _, part := range parts { |
| 223 | checksumFile := filepath.Join(part, checksum) |
| 224 | bytes, err := ioutil.ReadFile(checksumFile) |
| 225 | if err != nil { |
| 226 | if os.IsNotExist(err) { |
| 227 | result = append(result, MissingPart) |
| 228 | continue |
| 229 | } |
| 230 | vlog.Errorf("ReadFile(%v) failed: %v", checksumFile, err) |
Robin Thellend | e262789 | 2014-11-26 09:34:37 -0800 | [diff] [blame] | 231 | return []binary.PartInfo{}, repository.MediaInfo{}, errOperationFailed |
Jiri Simsa | b04fbb2 | 2014-06-27 17:38:04 -0700 | [diff] [blame] | 232 | } |
| 233 | dataFile := filepath.Join(part, data) |
| 234 | fi, err := os.Stat(dataFile) |
| 235 | if err != nil { |
| 236 | if os.IsNotExist(err) { |
| 237 | result = append(result, MissingPart) |
| 238 | continue |
| 239 | } |
| 240 | vlog.Errorf("Stat(%v) failed: %v", dataFile, err) |
Robin Thellend | e262789 | 2014-11-26 09:34:37 -0800 | [diff] [blame] | 241 | return []binary.PartInfo{}, repository.MediaInfo{}, errOperationFailed |
Jiri Simsa | b04fbb2 | 2014-06-27 17:38:04 -0700 | [diff] [blame] | 242 | } |
| 243 | result = append(result, binary.PartInfo{Checksum: string(bytes), Size: fi.Size()}) |
| 244 | } |
Robin Thellend | e262789 | 2014-11-26 09:34:37 -0800 | [diff] [blame] | 245 | infoFile := filepath.Join(i.path, "mediainfo") |
| 246 | jInfo, err := ioutil.ReadFile(infoFile) |
| 247 | if err != nil { |
| 248 | vlog.Errorf("ReadFile(%q) failed: %v", infoFile) |
| 249 | return []binary.PartInfo{}, repository.MediaInfo{}, errOperationFailed |
| 250 | } |
| 251 | var mediaInfo repository.MediaInfo |
| 252 | if err := json.Unmarshal(jInfo, &mediaInfo); err != nil { |
| 253 | vlog.Errorf("json.Unmarshal(%v) failed: %v", jInfo, err) |
| 254 | return []binary.PartInfo{}, repository.MediaInfo{}, errOperationFailed |
| 255 | } |
| 256 | return result, mediaInfo, nil |
Jiri Simsa | b04fbb2 | 2014-06-27 17:38:04 -0700 | [diff] [blame] | 257 | } |
| 258 | |
Robin Thellend | 9bc8fcb | 2014-11-17 10:23:04 -0800 | [diff] [blame] | 259 | func (i *binaryService) Upload(context repository.BinaryUploadContext, part int32) error { |
Jiri Simsa | b04fbb2 | 2014-06-27 17:38:04 -0700 | [diff] [blame] | 260 | vlog.Infof("%v.Upload(%v)", i.suffix, part) |
| 261 | path, suffix := i.generatePartPath(int(part)), "" |
Bogdan Caprita | e783dcc | 2014-11-04 14:16:55 -0800 | [diff] [blame] | 262 | err := checksumExists(path) |
Jiri Simsa | b04fbb2 | 2014-06-27 17:38:04 -0700 | [diff] [blame] | 263 | switch err { |
| 264 | case nil: |
Jiri Simsa | 518ef15 | 2014-08-08 18:07:55 -0700 | [diff] [blame] | 265 | return errExists |
| 266 | case errNotFound: |
Jiri Simsa | b04fbb2 | 2014-06-27 17:38:04 -0700 | [diff] [blame] | 267 | default: |
| 268 | return err |
| 269 | } |
| 270 | // Use os.OpenFile() to resolve races. |
| 271 | lockPath, flags, perm := filepath.Join(path, lock), os.O_CREATE|os.O_WRONLY|os.O_EXCL, os.FileMode(0600) |
| 272 | lockFile, err := os.OpenFile(lockPath, flags, perm) |
| 273 | if err != nil { |
| 274 | if os.IsExist(err) { |
| 275 | return errInProgress |
| 276 | } |
| 277 | vlog.Errorf("OpenFile(%v, %v, %v) failed: %v", lockPath, flags, suffix, err) |
| 278 | return errOperationFailed |
| 279 | } |
| 280 | defer os.Remove(lockFile.Name()) |
| 281 | defer lockFile.Close() |
| 282 | file, err := ioutil.TempFile(path, suffix) |
| 283 | if err != nil { |
| 284 | vlog.Errorf("TempFile(%v, %v) failed: %v", path, suffix, err) |
| 285 | return errOperationFailed |
| 286 | } |
| 287 | defer file.Close() |
| 288 | h := md5.New() |
Todd Wang | 702385a | 2014-11-07 01:54:08 -0800 | [diff] [blame] | 289 | rStream := context.RecvStream() |
Shyam Jayaraman | 97b9dca | 2014-07-31 13:30:46 -0700 | [diff] [blame] | 290 | for rStream.Advance() { |
| 291 | bytes := rStream.Value() |
Jiri Simsa | b04fbb2 | 2014-06-27 17:38:04 -0700 | [diff] [blame] | 292 | if _, err := file.Write(bytes); err != nil { |
| 293 | vlog.Errorf("Write() failed: %v", err) |
| 294 | if err := os.Remove(file.Name()); err != nil { |
| 295 | vlog.Errorf("Remove(%v) failed: %v", file.Name(), err) |
| 296 | } |
| 297 | return errOperationFailed |
| 298 | } |
| 299 | h.Write(bytes) |
| 300 | } |
Shyam Jayaraman | c4aed6e | 2014-07-22 14:25:06 -0700 | [diff] [blame] | 301 | |
Shyam Jayaraman | 97b9dca | 2014-07-31 13:30:46 -0700 | [diff] [blame] | 302 | if err := rStream.Err(); err != nil { |
| 303 | vlog.Errorf("Advance() failed: %v", err) |
Shyam Jayaraman | c4aed6e | 2014-07-22 14:25:06 -0700 | [diff] [blame] | 304 | if err := os.Remove(file.Name()); err != nil { |
| 305 | vlog.Errorf("Remove(%v) failed: %v", file.Name(), err) |
| 306 | } |
| 307 | return errOperationFailed |
| 308 | } |
| 309 | |
Jiri Simsa | b04fbb2 | 2014-06-27 17:38:04 -0700 | [diff] [blame] | 310 | hash := hex.EncodeToString(h.Sum(nil)) |
| 311 | checksumFile, perm := filepath.Join(path, checksum), os.FileMode(0600) |
| 312 | if err := ioutil.WriteFile(checksumFile, []byte(hash), perm); err != nil { |
| 313 | vlog.Errorf("WriteFile(%v, %v, %v) failed: %v", checksumFile, hash, perm, err) |
| 314 | if err := os.Remove(file.Name()); err != nil { |
| 315 | vlog.Errorf("Remove(%v) failed: %v", file.Name(), err) |
| 316 | } |
| 317 | return errOperationFailed |
| 318 | } |
Jiri Simsa | b04fbb2 | 2014-06-27 17:38:04 -0700 | [diff] [blame] | 319 | dataFile := filepath.Join(path, data) |
| 320 | if err := os.Rename(file.Name(), dataFile); err != nil { |
| 321 | vlog.Errorf("Rename(%v, %v) failed: %v", file.Name(), dataFile, err) |
| 322 | if err := os.Remove(file.Name()); err != nil { |
| 323 | vlog.Errorf("Remove(%v) failed: %v", file.Name(), err) |
| 324 | } |
| 325 | return errOperationFailed |
| 326 | } |
Jiri Simsa | b04fbb2 | 2014-06-27 17:38:04 -0700 | [diff] [blame] | 327 | return nil |
| 328 | } |
Robin Thellend | c5856f6 | 2014-11-17 10:30:54 -0800 | [diff] [blame] | 329 | |
Robin Thellend | 39ac323 | 2014-12-02 09:50:41 -0800 | [diff] [blame] | 330 | func (i *binaryService) GlobChildren__(ipc.ServerContext) (<-chan string, error) { |
Robin Thellend | c5856f6 | 2014-11-17 10:30:54 -0800 | [diff] [blame] | 331 | elems := strings.Split(i.suffix, "/") |
| 332 | if len(elems) == 1 && elems[0] == "" { |
| 333 | elems = nil |
| 334 | } |
| 335 | n := i.createObjectNameTree().find(elems, false) |
| 336 | if n == nil { |
| 337 | return nil, errOperationFailed |
| 338 | } |
Robin Thellend | 8e9cc24 | 2014-11-26 09:43:10 -0800 | [diff] [blame] | 339 | ch := make(chan string, 100) |
| 340 | go func() { |
| 341 | for k, _ := range n.children { |
| 342 | ch <- k |
| 343 | } |
| 344 | close(ch) |
| 345 | }() |
| 346 | return ch, nil |
Robin Thellend | c5856f6 | 2014-11-17 10:30:54 -0800 | [diff] [blame] | 347 | } |