github.com/rclone/rclone@v1.66.1-0.20240517100346-7b89735ae726/backend/local/metadata.go (about) 1 package local 2 3 import ( 4 "fmt" 5 "os" 6 "runtime" 7 "strconv" 8 "time" 9 10 "github.com/rclone/rclone/fs" 11 ) 12 13 const metadataTimeFormat = time.RFC3339Nano 14 15 // system metadata keys which this backend owns 16 // 17 // not all values supported on all OSes 18 var systemMetadataInfo = map[string]fs.MetadataHelp{ 19 "mode": { 20 Help: "File type and mode", 21 Type: "octal, unix style", 22 Example: "0100664", 23 }, 24 "uid": { 25 Help: "User ID of owner", 26 Type: "decimal number", 27 Example: "500", 28 }, 29 "gid": { 30 Help: "Group ID of owner", 31 Type: "decimal number", 32 Example: "500", 33 }, 34 "rdev": { 35 Help: "Device ID (if special file)", 36 Type: "hexadecimal", 37 Example: "1abc", 38 }, 39 "atime": { 40 Help: "Time of last access", 41 Type: "RFC 3339", 42 Example: "2006-01-02T15:04:05.999999999Z07:00", 43 }, 44 "mtime": { 45 Help: "Time of last modification", 46 Type: "RFC 3339", 47 Example: "2006-01-02T15:04:05.999999999Z07:00", 48 }, 49 "btime": { 50 Help: "Time of file birth (creation)", 51 Type: "RFC 3339", 52 Example: "2006-01-02T15:04:05.999999999Z07:00", 53 }, 54 } 55 56 // parse a time string from metadata with key 57 func (o *Object) parseMetadataTime(m fs.Metadata, key string) (t time.Time, ok bool) { 58 value, ok := m[key] 59 if ok { 60 var err error 61 t, err = time.Parse(metadataTimeFormat, value) 62 if err != nil { 63 fs.Debugf(o, "failed to parse metadata %s: %q: %v", key, value, err) 64 ok = false 65 } 66 } 67 return t, ok 68 } 69 70 // parse am int from metadata with key and base 71 func (o *Object) parseMetadataInt(m fs.Metadata, key string, base int) (result int, ok bool) { 72 value, ok := m[key] 73 if ok { 74 var err error 75 result64, err := strconv.ParseInt(value, base, 64) 76 if err != nil { 77 fs.Debugf(o, "failed to parse metadata %s: %q: %v", key, value, err) 78 ok = false 79 } 80 result = int(result64) 81 } 82 return result, ok 83 } 84 85 // Write the metadata into the file 86 // 87 // It isn't possible to set the ctime and btime under Unix 88 func (o *Object) writeMetadataToFile(m fs.Metadata) (outErr error) { 89 var err error 90 atime, atimeOK := o.parseMetadataTime(m, "atime") 91 mtime, mtimeOK := o.parseMetadataTime(m, "mtime") 92 btime, btimeOK := o.parseMetadataTime(m, "btime") 93 if atimeOK || mtimeOK { 94 if atimeOK && !mtimeOK { 95 mtime = atime 96 } 97 if !atimeOK && mtimeOK { 98 atime = mtime 99 } 100 err = o.setTimes(atime, mtime) 101 if err != nil { 102 outErr = fmt.Errorf("failed to set times: %w", err) 103 } 104 } 105 if haveSetBTime { 106 if btimeOK { 107 err = setBTime(o.path, btime) 108 if err != nil { 109 outErr = fmt.Errorf("failed to set birth (creation) time: %w", err) 110 } 111 } 112 } 113 uid, hasUID := o.parseMetadataInt(m, "uid", 10) 114 gid, hasGID := o.parseMetadataInt(m, "gid", 10) 115 if hasUID { 116 // FIXME should read UID and GID of current user and only attempt to set it if different 117 if !hasGID { 118 gid = uid 119 } 120 if runtime.GOOS == "windows" || runtime.GOOS == "plan9" { 121 fs.Debugf(o, "Ignoring request to set ownership %o.%o on this OS", gid, uid) 122 } else { 123 err = os.Chown(o.path, uid, gid) 124 if err != nil { 125 outErr = fmt.Errorf("failed to change ownership: %w", err) 126 } 127 } 128 } 129 mode, hasMode := o.parseMetadataInt(m, "mode", 8) 130 if hasMode { 131 err = os.Chmod(o.path, os.FileMode(mode)) 132 if err != nil { 133 outErr = fmt.Errorf("failed to change permissions: %w", err) 134 } 135 } 136 // FIXME not parsing rdev yet 137 return outErr 138 }