Chromium Code Reviews| 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 provided FileSystem object to operate on |
|
nodir
2015/06/03 19:30:27
a provided
Vadim Sh.
2015/06/03 22:35:08
Done.
| |
| 247 func NewFileSystemDestination(dir string) Destination { | 249 // files if given, otherwise use a default one. If FileSystem is provided, dir |
| 250 // must by in a subdirectory of the given FileSystem root. | |
|
nodir
2015/06/03 19:30:27
must be
Vadim Sh.
2015/06/03 22:35:08
Done.
| |
| 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 |