OLD | NEW |
1 // Copyright 2014 The Chromium Authors. All rights reserved. | 1 // Copyright 2014 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 package local | 5 package local |
6 | 6 |
7 import ( | 7 import ( |
8 "fmt" | 8 "fmt" |
9 "io" | 9 "io" |
10 "io/ioutil" | 10 "io/ioutil" |
(...skipping 216 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
227 } | 227 } |
228 return strings.HasPrefix(path, root) | 228 return strings.HasPrefix(path, root) |
229 } | 229 } |
230 | 230 |
231 //////////////////////////////////////////////////////////////////////////////// | 231 //////////////////////////////////////////////////////////////////////////////// |
232 // FileSystemDestination implementation. | 232 // FileSystemDestination implementation. |
233 | 233 |
234 type fileSystemDestination struct { | 234 type fileSystemDestination struct { |
235 // Destination directory. | 235 // Destination directory. |
236 dir string | 236 dir string |
| 237 // FileSystem implementation to use. |
| 238 fs FileSystem |
237 // Root temporary directory. | 239 // Root temporary directory. |
238 tempDir string | 240 tempDir string |
239 // Where to extract all temp files, subdirectory of tempDir. | 241 // Where to extract all temp files, subdirectory of tempDir. |
240 outDir string | 242 outDir string |
241 // Currently open files. | 243 // Currently open files. |
242 openFiles map[string]*os.File | 244 openFiles map[string]*os.File |
243 } | 245 } |
244 | 246 |
245 // NewFileSystemDestination returns a destination in the file system (directory) | 247 // NewFileSystemDestination returns a destination in the file system (directory) |
246 // to extract a package to. | 248 // to extract a package to. Will use a provided FileSystem object to operate on |
247 func NewFileSystemDestination(dir string) Destination { | 249 // files if given, otherwise use a default one. If FileSystem is provided, dir |
| 250 // must be in a subdirectory of the given FileSystem root. |
| 251 func NewFileSystemDestination(dir string, fs FileSystem) Destination { |
| 252 » if fs == nil { |
| 253 » » fs = NewFileSystem(filepath.Dir(dir), nil) |
| 254 » } |
248 return &fileSystemDestination{ | 255 return &fileSystemDestination{ |
249 dir: dir, | 256 dir: dir, |
| 257 fs: fs, |
250 openFiles: map[string]*os.File{}, | 258 openFiles: map[string]*os.File{}, |
251 } | 259 } |
252 } | 260 } |
253 | 261 |
254 func (d *fileSystemDestination) Begin() (err error) { | 262 func (d *fileSystemDestination) Begin() error { |
255 if d.tempDir != "" { | 263 if d.tempDir != "" { |
256 return fmt.Errorf("Destination is already open") | 264 return fmt.Errorf("Destination is already open") |
257 } | 265 } |
258 | 266 |
259 » // Ensure parent directory of destination directory exists. | 267 » // Ensure a parent directory of the destination directory exists. |
260 » d.dir, err = filepath.Abs(filepath.Clean(d.dir)) | 268 » var err error |
261 » if err != nil { | 269 » if d.dir, err = d.fs.ToAbsPath(d.dir); err != nil { |
262 return err | 270 return err |
263 } | 271 } |
264 » err = os.MkdirAll(filepath.Dir(d.dir), 0777) | 272 » if _, err := d.fs.EnsureDirectory(filepath.Dir(d.dir)); err != nil { |
265 » if err != nil { | |
266 return err | 273 return err |
267 } | 274 } |
268 | 275 |
269 // Called in case something below fails. | 276 // Called in case something below fails. |
270 cleanup := func() { | 277 cleanup := func() { |
271 if d.tempDir != "" { | 278 if d.tempDir != "" { |
272 » » » os.RemoveAll(d.tempDir) | 279 » » » d.fs.EnsureDirectoryGone(d.tempDir) |
273 } | 280 } |
274 d.tempDir = "" | 281 d.tempDir = "" |
275 d.outDir = "" | 282 d.outDir = "" |
276 } | 283 } |
277 | 284 |
278 » // Create root temp dir, on the same level as destination directory. | 285 » // Create root temp dir, on the same level as the destination directory. |
279 d.tempDir, err = ioutil.TempDir(filepath.Dir(d.dir), filepath.Base(d.dir
)+"_") | 286 d.tempDir, err = ioutil.TempDir(filepath.Dir(d.dir), filepath.Base(d.dir
)+"_") |
280 if err != nil { | 287 if err != nil { |
281 cleanup() | 288 cleanup() |
282 return err | 289 return err |
283 } | 290 } |
284 | 291 |
285 // Create a staging output directory where everything will be extracted. | 292 // Create a staging output directory where everything will be extracted. |
286 » d.outDir = filepath.Join(d.tempDir, "out") | 293 » d.outDir, err = d.fs.EnsureDirectory(filepath.Join(d.tempDir, "out")) |
287 » err = os.MkdirAll(d.outDir, 0777) | |
288 if err != nil { | 294 if err != nil { |
289 cleanup() | 295 cleanup() |
290 return err | 296 return err |
291 } | 297 } |
292 | 298 |
293 return nil | 299 return nil |
294 } | 300 } |
295 | 301 |
296 func (d *fileSystemDestination) CreateFile(name string, executable bool) (io.Wri
teCloser, error) { | 302 func (d *fileSystemDestination) CreateFile(name string, executable bool) (io.Wri
teCloser, error) { |
297 » _, ok := d.openFiles[name] | 303 » if _, ok := d.openFiles[name]; ok { |
298 » if ok { | |
299 return nil, fmt.Errorf("File %s is already open", name) | 304 return nil, fmt.Errorf("File %s is already open", name) |
300 } | 305 } |
301 | 306 |
302 path, err := d.prepareFilePath(name) | 307 path, err := d.prepareFilePath(name) |
303 if err != nil { | 308 if err != nil { |
304 return nil, err | 309 return nil, err |
305 } | 310 } |
306 | 311 |
307 // Let the umask trim the file mode. Do not set 'writable' bit though. | 312 // Let the umask trim the file mode. Do not set 'writable' bit though. |
308 var mode os.FileMode | 313 var mode os.FileMode |
(...skipping 25 matching lines...) Expand all Loading... |
334 | 339 |
335 // Forbid relative symlinks to files outside of the destination root. | 340 // Forbid relative symlinks to files outside of the destination root. |
336 target = filepath.FromSlash(target) | 341 target = filepath.FromSlash(target) |
337 if !filepath.IsAbs(target) { | 342 if !filepath.IsAbs(target) { |
338 targetAbs := filepath.Clean(filepath.Join(filepath.Dir(path), ta
rget)) | 343 targetAbs := filepath.Clean(filepath.Join(filepath.Dir(path), ta
rget)) |
339 if !isSubpath(targetAbs, d.outDir) { | 344 if !isSubpath(targetAbs, d.outDir) { |
340 return fmt.Errorf("Relative symlink is pointing outside
of the destination dir: %s", name) | 345 return fmt.Errorf("Relative symlink is pointing outside
of the destination dir: %s", name) |
341 } | 346 } |
342 } | 347 } |
343 | 348 |
344 » return os.Symlink(target, path) | 349 » return d.fs.EnsureSymlink(path, target) |
345 } | 350 } |
346 | 351 |
347 func (d *fileSystemDestination) End(success bool) error { | 352 func (d *fileSystemDestination) End(success bool) error { |
348 if d.tempDir == "" { | 353 if d.tempDir == "" { |
349 return fmt.Errorf("Destination is not open") | 354 return fmt.Errorf("Destination is not open") |
350 } | 355 } |
351 if len(d.openFiles) != 0 { | 356 if len(d.openFiles) != 0 { |
352 return fmt.Errorf("Not all files were closed. Leaking.") | 357 return fmt.Errorf("Not all files were closed. Leaking.") |
353 } | 358 } |
354 | 359 |
355 // Clean up temp dir and the state no matter what. | 360 // Clean up temp dir and the state no matter what. |
356 defer func() { | 361 defer func() { |
357 » » os.RemoveAll(d.tempDir) | 362 » » d.fs.EnsureDirectoryGone(d.tempDir) |
358 d.tempDir = "" | 363 d.tempDir = "" |
359 d.outDir = "" | 364 d.outDir = "" |
360 }() | 365 }() |
361 | 366 |
362 if success { | 367 if success { |
363 » » // Move existing directory away, if it is there. | 368 » » return d.fs.Replace(d.outDir, d.dir) |
364 » » old := filepath.Join(d.tempDir, "old") | |
365 » » if os.Rename(d.dir, old) != nil { | |
366 » » » old = "" | |
367 » » } | |
368 | |
369 » » // Move new directory in place. | |
370 » » err := os.Rename(d.outDir, d.dir) | |
371 » » if err != nil { | |
372 » » » // Try to return the original directory back... | |
373 » » » if old != "" { | |
374 » » » » os.Rename(old, d.dir) | |
375 » » » } | |
376 » » » return err | |
377 » » } | |
378 } | 369 } |
379 | 370 » // Let the defer to clean the garbage in tempDir. |
380 return nil | 371 return nil |
381 } | 372 } |
382 | 373 |
383 // prepareFilePath performs steps common to CreateFile and CreateSymlink: it | 374 // prepareFilePath performs steps common to CreateFile and CreateSymlink: it |
384 // does some validation, expands "name" to an absolute path and creates parent | 375 // does some validation, expands "name" to an absolute path and creates parent |
385 // directories for a future file. Returns absolute path where the file should | 376 // directories for a future file. Returns absolute path where the file should |
386 // be put. | 377 // be put. |
387 func (d *fileSystemDestination) prepareFilePath(name string) (string, error) { | 378 func (d *fileSystemDestination) prepareFilePath(name string) (string, error) { |
388 if d.tempDir == "" { | 379 if d.tempDir == "" { |
389 return "", fmt.Errorf("Destination is not open") | 380 return "", fmt.Errorf("Destination is not open") |
390 } | 381 } |
391 path := filepath.Clean(filepath.Join(d.outDir, filepath.FromSlash(name))
) | 382 path := filepath.Clean(filepath.Join(d.outDir, filepath.FromSlash(name))
) |
392 if !isSubpath(path, d.outDir) { | 383 if !isSubpath(path, d.outDir) { |
393 return "", fmt.Errorf("Invalid relative file name: %s", name) | 384 return "", fmt.Errorf("Invalid relative file name: %s", name) |
394 } | 385 } |
395 » err := os.MkdirAll(filepath.Dir(path), 0777) | 386 » if _, err := d.fs.EnsureDirectory(filepath.Dir(path)); err != nil { |
396 » if err != nil { | |
397 return "", err | 387 return "", err |
398 } | 388 } |
399 return path, nil | 389 return path, nil |
400 } | 390 } |
401 | 391 |
402 type fileSystemDestinationFile struct { | 392 type fileSystemDestinationFile struct { |
403 nested io.WriteCloser | 393 nested io.WriteCloser |
404 parent *fileSystemDestination | 394 parent *fileSystemDestination |
405 closeCallback func() | 395 closeCallback func() |
406 } | 396 } |
407 | 397 |
408 func (f *fileSystemDestinationFile) Write(p []byte) (n int, err error) { | 398 func (f *fileSystemDestinationFile) Write(p []byte) (n int, err error) { |
409 return f.nested.Write(p) | 399 return f.nested.Write(p) |
410 } | 400 } |
411 | 401 |
412 func (f *fileSystemDestinationFile) Close() error { | 402 func (f *fileSystemDestinationFile) Close() error { |
413 f.closeCallback() | 403 f.closeCallback() |
414 return f.nested.Close() | 404 return f.nested.Close() |
415 } | 405 } |
OLD | NEW |