@@ -20,10 +20,13 @@ package ocis
20
20
21
21
import (
22
22
"context"
23
+ "crypto/md5"
23
24
"fmt"
25
+ "io"
24
26
"os"
25
27
"path/filepath"
26
28
"strings"
29
+ "time"
27
30
28
31
userpb "github.com/cs3org/go-cs3apis/cs3/identity/user/v1beta1"
29
32
provider "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1"
@@ -47,17 +50,17 @@ type Node struct {
47
50
48
51
func (n * Node ) writeMetadata (owner * userpb.UserId ) (err error ) {
49
52
nodePath := filepath .Join (n .pw .Root , "nodes" , n .ID )
50
- if err = xattr .Set (nodePath , "user.ocis.parentid" , []byte (n .ParentID )); err != nil {
53
+ if err = xattr .Set (nodePath , parentidAttr , []byte (n .ParentID )); err != nil {
51
54
return errors .Wrap (err , "ocisfs: could not set parentid attribute" )
52
55
}
53
- if err = xattr .Set (nodePath , "user.ocis.name" , []byte (n .Name )); err != nil {
56
+ if err = xattr .Set (nodePath , nameAttr , []byte (n .Name )); err != nil {
54
57
return errors .Wrap (err , "ocisfs: could not set name attribute" )
55
58
}
56
59
if owner != nil {
57
- if err = xattr .Set (nodePath , "user.ocis.owner.id" , []byte (owner .OpaqueId )); err != nil {
60
+ if err = xattr .Set (nodePath , ownerIDAttr , []byte (owner .OpaqueId )); err != nil {
58
61
return errors .Wrap (err , "ocisfs: could not set owner id attribute" )
59
62
}
60
- if err = xattr .Set (nodePath , "user.ocis.owner.idp" , []byte (owner .Idp )); err != nil {
63
+ if err = xattr .Set (nodePath , ownerIDPAttr , []byte (owner .Idp )); err != nil {
61
64
return errors .Wrap (err , "ocisfs: could not set owner idp attribute" )
62
65
}
63
66
}
@@ -75,13 +78,13 @@ func ReadNode(ctx context.Context, pw *Path, id string) (n *Node, err error) {
75
78
76
79
// lookup parent id in extended attributes
77
80
var attrBytes []byte
78
- if attrBytes , err = xattr .Get (nodePath , "user.ocis.parentid" ); err == nil {
81
+ if attrBytes , err = xattr .Get (nodePath , parentidAttr ); err == nil {
79
82
n .ParentID = string (attrBytes )
80
83
} else {
81
84
return
82
85
}
83
86
// lookup name in extended attributes
84
- if attrBytes , err = xattr .Get (nodePath , "user.ocis.name" ); err == nil {
87
+ if attrBytes , err = xattr .Get (nodePath , nameAttr ); err == nil {
85
88
n .Name = string (attrBytes )
86
89
} else {
87
90
return
@@ -99,7 +102,7 @@ func ReadNode(ctx context.Context, pw *Path, id string) (n *Node, err error) {
99
102
// walk to root to check node is not part of a deleted subtree
100
103
parentPath := filepath .Join (n .pw .Root , "nodes" , parentID )
101
104
102
- if attrBytes , err = xattr .Get (parentPath , "user.ocis.parentid" ); err == nil {
105
+ if attrBytes , err = xattr .Get (parentPath , parentidAttr ); err == nil {
103
106
parentID = string (attrBytes )
104
107
log .Debug ().Interface ("node" , n ).Str ("root.ID" , root .ID ).Str ("parentID" , parentID ).Msg ("ReadNode() found parent" )
105
108
} else {
@@ -156,13 +159,13 @@ func (n *Node) Parent() (p *Node, err error) {
156
159
157
160
// lookup parent id in extended attributes
158
161
var attrBytes []byte
159
- if attrBytes , err = xattr .Get (parentPath , "user.ocis.parentid" ); err == nil {
162
+ if attrBytes , err = xattr .Get (parentPath , parentidAttr ); err == nil {
160
163
p .ParentID = string (attrBytes )
161
164
} else {
162
165
return
163
166
}
164
167
// lookup name in extended attributes
165
- if attrBytes , err = xattr .Get (parentPath , "user.ocis.name" ); err == nil {
168
+ if attrBytes , err = xattr .Get (parentPath , nameAttr ); err == nil {
166
169
p .Name = string (attrBytes )
167
170
} else {
168
171
return
@@ -186,13 +189,13 @@ func (n *Node) Owner() (id string, idp string, err error) {
186
189
// lookup parent id in extended attributes
187
190
var attrBytes []byte
188
191
// lookup name in extended attributes
189
- if attrBytes , err = xattr .Get (nodePath , "user.ocis.owner.id" ); err == nil {
192
+ if attrBytes , err = xattr .Get (nodePath , ownerIDAttr ); err == nil {
190
193
n .ownerID = string (attrBytes )
191
194
} else {
192
195
return
193
196
}
194
197
// lookup name in extended attributes
195
- if attrBytes , err = xattr .Get (nodePath , "user.ocis.owner.idp" ); err == nil {
198
+ if attrBytes , err = xattr .Get (nodePath , ownerIDPAttr ); err == nil {
196
199
n .ownerIDP = string (attrBytes )
197
200
} else {
198
201
return
@@ -230,32 +233,22 @@ func (n *Node) AsResourceInfo(ctx context.Context) (ri *provider.ResourceInfo, e
230
233
// nodeType = provider.ResourceType_RESOURCE_TYPE_REFERENCE
231
234
}
232
235
233
- var etag []byte
234
- // TODO optionally store etag in new `root/attributes/<uuid>` file
235
- if etag , err = xattr .Get (nodePath , "user.ocis.etag" ); err != nil {
236
- log .Error ().Err (err ).Interface ("node" , n ).Msg ("could not read etag" )
237
- }
238
-
239
236
id := & provider.ResourceId {OpaqueId : n .ID }
240
237
241
238
fn , err = n .pw .Path (ctx , n )
242
239
if err != nil {
243
240
return nil , err
244
241
}
242
+
245
243
ri = & provider.ResourceInfo {
246
244
Id : id ,
247
245
Path : fn ,
248
246
Type : nodeType ,
249
- Etag : string (etag ),
250
247
MimeType : mime .Detect (nodeType == provider .ResourceType_RESOURCE_TYPE_CONTAINER , fn ),
251
248
Size : uint64 (fi .Size ()),
252
249
// TODO fix permissions
253
250
PermissionSet : & provider.ResourcePermissions {ListContainer : true , CreateContainer : true },
254
- Mtime : & types.Timestamp {
255
- Seconds : uint64 (fi .ModTime ().Unix ()),
256
- // TODO read nanos from where? Nanos: fi.MTimeNanos,
257
- },
258
- Target : string (target ),
251
+ Target : string (target ),
259
252
}
260
253
261
254
if owner , idp , err := n .Owner (); err == nil {
@@ -265,6 +258,35 @@ func (n *Node) AsResourceInfo(ctx context.Context) (ri *provider.ResourceInfo, e
265
258
}
266
259
}
267
260
261
+ // etag currently is a hash of fileid + stime (or mtime)
262
+ // TODO make etag of files use fileid and checksum
263
+ // TODO optionally store etag in an attribute to restore backups
264
+ h := md5 .New ()
265
+ io .WriteString (h , n .ID )
266
+ var b []byte
267
+ var stime time.Time
268
+ if b , err = xattr .Get (nodePath , stimeAttr ); err == nil {
269
+ if stime , err = time .Parse (time .RFC3339Nano , string (b )); err != nil {
270
+ // invalid format, overwrite
271
+ log .Error ().Err (err ).Interface ("node" , n ).Str ("stime" , string (b )).Msg ("invalid format, ignoring" )
272
+ stime = fi .ModTime ()
273
+ }
274
+ } else {
275
+ // no stime, use mtime
276
+ stime = fi .ModTime ()
277
+ }
278
+ tb , _ := stime .UTC ().MarshalBinary ()
279
+ h .Write (tb )
280
+ ri .Etag = fmt .Sprintf ("%x" , h .Sum (nil ))
281
+
282
+ // mtime uses stime if present
283
+ // TODO expose mtime and stime seperately?
284
+ un := stime .UnixNano ()
285
+ ri .Mtime = & types.Timestamp {
286
+ Seconds : uint64 (un / 1000000000 ),
287
+ Nanos : uint32 (un % 1000000000 ),
288
+ }
289
+
268
290
// TODO only read the requested metadata attributes
269
291
if attrs , err := xattr .List (nodePath ); err == nil {
270
292
ri .ArbitraryMetadata = & provider.ArbitraryMetadata {
0 commit comments