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  }