@@ -318,6 +318,13 @@ func copyFile(src, dst string) (err error) {
318
318
return
319
319
}
320
320
321
+ // Temporary fix for Go < 1.9
322
+ //
323
+ // See: https://github.com/golang/dep/issues/774
324
+ // and https://github.com/golang/go/issues/20829
325
+ if runtime .GOOS == "windows" {
326
+ dst = fixLongPath (dst )
327
+ }
321
328
err = os .Chmod (dst , si .Mode ())
322
329
323
330
return
@@ -400,3 +407,133 @@ func IsSymlink(path string) (bool, error) {
400
407
401
408
return l .Mode ()& os .ModeSymlink == os .ModeSymlink , nil
402
409
}
410
+
411
+ // fixLongPath returns the extended-length (\\?\-prefixed) form of
412
+ // path when needed, in order to avoid the default 260 character file
413
+ // path limit imposed by Windows. If path is not easily converted to
414
+ // the extended-length form (for example, if path is a relative path
415
+ // or contains .. elements), or is short enough, fixLongPath returns
416
+ // path unmodified.
417
+ //
418
+ // See https://msdn.microsoft.com/en-us/library/windows/desktop/aa365247(v=vs.85).aspx#maxpath
419
+ func fixLongPath (path string ) string {
420
+ // Do nothing (and don't allocate) if the path is "short".
421
+ // Empirically (at least on the Windows Server 2013 builder),
422
+ // the kernel is arbitrarily okay with < 248 bytes. That
423
+ // matches what the docs above say:
424
+ // "When using an API to create a directory, the specified
425
+ // path cannot be so long that you cannot append an 8.3 file
426
+ // name (that is, the directory name cannot exceed MAX_PATH
427
+ // minus 12)." Since MAX_PATH is 260, 260 - 12 = 248.
428
+ //
429
+ // The MSDN docs appear to say that a normal path that is 248 bytes long
430
+ // will work; empirically the path must be less then 248 bytes long.
431
+ if len (path ) < 248 {
432
+ // Don't fix. (This is how Go 1.7 and earlier worked,
433
+ // not automatically generating the \\?\ form)
434
+ return path
435
+ }
436
+
437
+ // The extended form begins with \\?\, as in
438
+ // \\?\c:\windows\foo.txt or \\?\UNC\server\share\foo.txt.
439
+ // The extended form disables evaluation of . and .. path
440
+ // elements and disables the interpretation of / as equivalent
441
+ // to \. The conversion here rewrites / to \ and elides
442
+ // . elements as well as trailing or duplicate separators. For
443
+ // simplicity it avoids the conversion entirely for relative
444
+ // paths or paths containing .. elements. For now,
445
+ // \\server\share paths are not converted to
446
+ // \\?\UNC\server\share paths because the rules for doing so
447
+ // are less well-specified.
448
+ if len (path ) >= 2 && path [:2 ] == `\\` {
449
+ // Don't canonicalize UNC paths.
450
+ return path
451
+ }
452
+ if ! isAbs (path ) {
453
+ // Relative path
454
+ return path
455
+ }
456
+
457
+ const prefix = `\\?`
458
+
459
+ pathbuf := make ([]byte , len (prefix )+ len (path )+ len (`\` ))
460
+ copy (pathbuf , prefix )
461
+ n := len (path )
462
+ r , w := 0 , len (prefix )
463
+ for r < n {
464
+ switch {
465
+ case os .IsPathSeparator (path [r ]):
466
+ // empty block
467
+ r ++
468
+ case path [r ] == '.' && (r + 1 == n || os .IsPathSeparator (path [r + 1 ])):
469
+ // /./
470
+ r ++
471
+ case r + 1 < n && path [r ] == '.' && path [r + 1 ] == '.' && (r + 2 == n || os .IsPathSeparator (path [r + 2 ])):
472
+ // /../ is currently unhandled
473
+ return path
474
+ default :
475
+ pathbuf [w ] = '\\'
476
+ w ++
477
+ for ; r < n && ! os .IsPathSeparator (path [r ]); r ++ {
478
+ pathbuf [w ] = path [r ]
479
+ w ++
480
+ }
481
+ }
482
+ }
483
+ // A drive's root directory needs a trailing \
484
+ if w == len (`\\?\c:` ) {
485
+ pathbuf [w ] = '\\'
486
+ w ++
487
+ }
488
+ return string (pathbuf [:w ])
489
+ }
490
+
491
+ func isAbs (path string ) (b bool ) {
492
+ v := volumeName (path )
493
+ if v == "" {
494
+ return false
495
+ }
496
+ path = path [len (v ):]
497
+ if path == "" {
498
+ return false
499
+ }
500
+ return os .IsPathSeparator (path [0 ])
501
+ }
502
+
503
+ func volumeName (path string ) (v string ) {
504
+ if len (path ) < 2 {
505
+ return ""
506
+ }
507
+ // with drive letter
508
+ c := path [0 ]
509
+ if path [1 ] == ':' &&
510
+ ('0' <= c && c <= '9' || 'a' <= c && c <= 'z' ||
511
+ 'A' <= c && c <= 'Z' ) {
512
+ return path [:2 ]
513
+ }
514
+ // is it UNC
515
+ if l := len (path ); l >= 5 && os .IsPathSeparator (path [0 ]) && os .IsPathSeparator (path [1 ]) &&
516
+ ! os .IsPathSeparator (path [2 ]) && path [2 ] != '.' {
517
+ // first, leading `\\` and next shouldn't be `\`. its server name.
518
+ for n := 3 ; n < l - 1 ; n ++ {
519
+ // second, next '\' shouldn't be repeated.
520
+ if os .IsPathSeparator (path [n ]) {
521
+ n ++
522
+ // third, following something characters. its share name.
523
+ if ! os .IsPathSeparator (path [n ]) {
524
+ if path [n ] == '.' {
525
+ break
526
+ }
527
+ for ; n < l ; n ++ {
528
+ if os .IsPathSeparator (path [n ]) {
529
+ break
530
+ }
531
+ }
532
+ return path [:n ]
533
+ }
534
+ break
535
+ }
536
+ }
537
+ }
538
+ return ""
539
+ }
0 commit comments