2022-05-24 19:06:16 +02:00
|
|
|
package local
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
2024-08-17 15:53:09 +02:00
|
|
|
"math"
|
2022-05-24 19:06:16 +02:00
|
|
|
"os"
|
|
|
|
"runtime"
|
|
|
|
"strconv"
|
|
|
|
"time"
|
|
|
|
|
|
|
|
"github.com/rclone/rclone/fs"
|
|
|
|
)
|
|
|
|
|
|
|
|
const metadataTimeFormat = time.RFC3339Nano
|
|
|
|
|
|
|
|
// system metadata keys which this backend owns
|
|
|
|
//
|
|
|
|
// not all values supported on all OSes
|
|
|
|
var systemMetadataInfo = map[string]fs.MetadataHelp{
|
|
|
|
"mode": {
|
|
|
|
Help: "File type and mode",
|
|
|
|
Type: "octal, unix style",
|
|
|
|
Example: "0100664",
|
|
|
|
},
|
|
|
|
"uid": {
|
|
|
|
Help: "User ID of owner",
|
|
|
|
Type: "decimal number",
|
|
|
|
Example: "500",
|
|
|
|
},
|
|
|
|
"gid": {
|
|
|
|
Help: "Group ID of owner",
|
|
|
|
Type: "decimal number",
|
|
|
|
Example: "500",
|
|
|
|
},
|
|
|
|
"rdev": {
|
|
|
|
Help: "Device ID (if special file)",
|
|
|
|
Type: "hexadecimal",
|
|
|
|
Example: "1abc",
|
|
|
|
},
|
|
|
|
"atime": {
|
|
|
|
Help: "Time of last access",
|
|
|
|
Type: "RFC 3339",
|
|
|
|
Example: "2006-01-02T15:04:05.999999999Z07:00",
|
|
|
|
},
|
|
|
|
"mtime": {
|
|
|
|
Help: "Time of last modification",
|
|
|
|
Type: "RFC 3339",
|
|
|
|
Example: "2006-01-02T15:04:05.999999999Z07:00",
|
|
|
|
},
|
|
|
|
"btime": {
|
|
|
|
Help: "Time of file birth (creation)",
|
|
|
|
Type: "RFC 3339",
|
|
|
|
Example: "2006-01-02T15:04:05.999999999Z07:00",
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
// parse a time string from metadata with key
|
|
|
|
func (o *Object) parseMetadataTime(m fs.Metadata, key string) (t time.Time, ok bool) {
|
|
|
|
value, ok := m[key]
|
|
|
|
if ok {
|
|
|
|
var err error
|
|
|
|
t, err = time.Parse(metadataTimeFormat, value)
|
|
|
|
if err != nil {
|
|
|
|
fs.Debugf(o, "failed to parse metadata %s: %q: %v", key, value, err)
|
|
|
|
ok = false
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return t, ok
|
|
|
|
}
|
|
|
|
|
|
|
|
// parse am int from metadata with key and base
|
|
|
|
func (o *Object) parseMetadataInt(m fs.Metadata, key string, base int) (result int, ok bool) {
|
|
|
|
value, ok := m[key]
|
|
|
|
if ok {
|
|
|
|
var err error
|
2024-08-15 20:23:57 +02:00
|
|
|
parsed, err := strconv.ParseInt(value, base, 0)
|
2022-05-24 19:06:16 +02:00
|
|
|
if err != nil {
|
|
|
|
fs.Debugf(o, "failed to parse metadata %s: %q: %v", key, value, err)
|
|
|
|
ok = false
|
|
|
|
}
|
2024-08-15 20:23:57 +02:00
|
|
|
result = int(parsed)
|
2022-05-24 19:06:16 +02:00
|
|
|
}
|
|
|
|
return result, ok
|
|
|
|
}
|
|
|
|
|
|
|
|
// Write the metadata into the file
|
|
|
|
//
|
|
|
|
// It isn't possible to set the ctime and btime under Unix
|
|
|
|
func (o *Object) writeMetadataToFile(m fs.Metadata) (outErr error) {
|
|
|
|
var err error
|
|
|
|
atime, atimeOK := o.parseMetadataTime(m, "atime")
|
|
|
|
mtime, mtimeOK := o.parseMetadataTime(m, "mtime")
|
|
|
|
btime, btimeOK := o.parseMetadataTime(m, "btime")
|
|
|
|
if atimeOK || mtimeOK {
|
|
|
|
if atimeOK && !mtimeOK {
|
|
|
|
mtime = atime
|
|
|
|
}
|
|
|
|
if !atimeOK && mtimeOK {
|
|
|
|
atime = mtime
|
|
|
|
}
|
|
|
|
err = o.setTimes(atime, mtime)
|
|
|
|
if err != nil {
|
|
|
|
outErr = fmt.Errorf("failed to set times: %w", err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if haveSetBTime {
|
|
|
|
if btimeOK {
|
2024-11-13 12:55:28 +01:00
|
|
|
if o.translatedLink {
|
|
|
|
err = lsetBTime(o.path, btime)
|
|
|
|
} else {
|
|
|
|
err = setBTime(o.path, btime)
|
|
|
|
}
|
2022-05-24 19:06:16 +02:00
|
|
|
if err != nil {
|
|
|
|
outErr = fmt.Errorf("failed to set birth (creation) time: %w", err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
uid, hasUID := o.parseMetadataInt(m, "uid", 10)
|
|
|
|
gid, hasGID := o.parseMetadataInt(m, "gid", 10)
|
|
|
|
if hasUID {
|
|
|
|
// FIXME should read UID and GID of current user and only attempt to set it if different
|
|
|
|
if !hasGID {
|
|
|
|
gid = uid
|
|
|
|
}
|
|
|
|
if runtime.GOOS == "windows" || runtime.GOOS == "plan9" {
|
|
|
|
fs.Debugf(o, "Ignoring request to set ownership %o.%o on this OS", gid, uid)
|
|
|
|
} else {
|
2024-11-13 12:55:28 +01:00
|
|
|
if o.translatedLink {
|
|
|
|
err = os.Lchown(o.path, uid, gid)
|
|
|
|
} else {
|
|
|
|
err = os.Chown(o.path, uid, gid)
|
|
|
|
}
|
2022-05-24 19:06:16 +02:00
|
|
|
if err != nil {
|
|
|
|
outErr = fmt.Errorf("failed to change ownership: %w", err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
mode, hasMode := o.parseMetadataInt(m, "mode", 8)
|
|
|
|
if hasMode {
|
2024-08-17 18:01:30 +02:00
|
|
|
if mode >= 0 {
|
|
|
|
umode := uint(mode)
|
|
|
|
if umode <= math.MaxUint32 {
|
2024-11-13 12:55:28 +01:00
|
|
|
if o.translatedLink {
|
|
|
|
if haveLChmod {
|
|
|
|
err = lChmod(o.path, os.FileMode(umode))
|
|
|
|
} else {
|
|
|
|
fs.Debugf(o, "Unable to set mode %v on a symlink on this OS", os.FileMode(umode))
|
|
|
|
err = nil
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
err = os.Chmod(o.path, os.FileMode(umode))
|
|
|
|
}
|
2024-08-17 18:01:30 +02:00
|
|
|
if err != nil {
|
|
|
|
outErr = fmt.Errorf("failed to change permissions: %w", err)
|
|
|
|
}
|
2024-08-17 15:53:09 +02:00
|
|
|
}
|
2022-05-24 19:06:16 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
// FIXME not parsing rdev yet
|
|
|
|
return outErr
|
|
|
|
}
|