diff --git a/backend/oracleobjectstorage/oracleobjectstorage.go b/backend/oracleobjectstorage/oracleobjectstorage.go index 2e8c06581..5204b2214 100644 --- a/backend/oracleobjectstorage/oracleobjectstorage.go +++ b/backend/oracleobjectstorage/oracleobjectstorage.go @@ -12,6 +12,7 @@ import ( "strings" "time" + "github.com/ncw/swift/v2" "github.com/oracle/oci-go-sdk/v65/common" "github.com/oracle/oci-go-sdk/v65/objectstorage" "github.com/rclone/rclone/fs" @@ -33,9 +34,46 @@ func init() { NewFs: NewFs, CommandHelp: commandHelp, Options: newOptions(), + MetadataInfo: &fs.MetadataInfo{ + System: systemMetadataInfo, + Help: `User metadata is stored as opc-meta- keys.`, + }, }) } +var systemMetadataInfo = map[string]fs.MetadataHelp{ + "opc-meta-mode": { + Help: "File type and mode", + Type: "octal, unix style", + Example: "0100664", + }, + "opc-meta-uid": { + Help: "User ID of owner", + Type: "decimal number", + Example: "500", + }, + "opc-meta-gid": { + Help: "Group ID of owner", + Type: "decimal number", + Example: "500", + }, + "opc-meta-atime": { + Help: "Time of last access", + Type: "ISO 8601", + Example: "2025-06-30T22:27:43-04:00", + }, + "opc-meta-mtime": { + Help: "Time of last modification", + Type: "ISO 8601", + Example: "2025-06-30T22:27:43-04:00", + }, + "opc-meta-btime": { + Help: "Time of file birth (creation)", + Type: "ISO 8601", + Example: "2025-06-30T22:27:43-04:00", + }, +} + // Fs represents a remote object storage server type Fs struct { name string // name of this remote @@ -82,6 +120,7 @@ func NewFs(ctx context.Context, name, root string, m configmap.Mapper) (fs.Fs, e } f.setRoot(root) f.features = (&fs.Features{ + ReadMetadata: true, ReadMimeType: true, WriteMimeType: true, BucketBased: true, @@ -688,6 +727,38 @@ func (f *Fs) ListR(ctx context.Context, dir string, callback fs.ListRCallback) ( return list.Flush() } +// Metadata returns metadata for an object +// +// It should return nil if there is no Metadata +func (o *Object) Metadata(ctx context.Context) (metadata fs.Metadata, err error) { + err = o.readMetaData(ctx) + if err != nil { + return nil, err + } + metadata = make(fs.Metadata, len(o.meta)+7) + for k, v := range o.meta { + switch k { + case metaMtime: + if modTime, err := swift.FloatStringToTime(v); err == nil { + metadata["mtime"] = modTime.Format(time.RFC3339Nano) + } + case metaMD5Hash: + // don't write hash metadata + default: + metadata[k] = v + } + } + if o.mimeType != "" { + metadata["content-type"] = o.mimeType + } + + if !o.lastModified.IsZero() { + metadata["btime"] = o.lastModified.Format(time.RFC3339Nano) + } + + return metadata, nil +} + // Check the interfaces are satisfied var ( _ fs.Fs = &Fs{}