Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(434)

Side by Side Diff: go/src/infra/tools/cipd/local/files.go

Issue 1154423015: cipd: Extract high level file system operations into the separate interface. (Closed) Base URL: https://chromium.googlesource.com/infra/infra.git@master
Patch Set: Created 5 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
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
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
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 }
OLDNEW
« no previous file with comments | « go/src/infra/tools/cipd/local/deployer_test.go ('k') | go/src/infra/tools/cipd/local/files_test.go » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698