@@ -253,7 +253,10 @@ func (d *driver) CreateDir(ctx context.Context, ref *provider.Reference) (*stora
253253}
254254
255255func (d * driver ) Delete (ctx context.Context , ref * provider.Reference ) (* storage.DeleteResult , error ) {
256- client , _ , rel , err := d .webdavClient (ctx , nil , ref )
256+ // The coordinator rolls back a failed upload from the tusd finish path, which
257+ // carries no request auth token, so the share must be resolved as the service
258+ // account on the executant's behalf.
259+ client , _ , rel , err := d .serviceWebdavClient (ctx , ref )
257260 if err != nil {
258261 return nil , err
259262 }
@@ -264,14 +267,28 @@ func (d *driver) Delete(ctx context.Context, ref *provider.Reference) (*storage.
264267}
265268
266269func (d * driver ) TouchFile (ctx context.Context , ref * provider.Reference , markprocessing bool , mtime string ) (* storage.TouchFileResult , error ) {
267- client , _ , rel , err := d .webdavClient (ctx , nil , ref )
270+ // The coordinator calls this from the upload finish path, which runs under
271+ // tusd with no request auth token, so the share must be resolved as the
272+ // service account on the executant's behalf.
273+ client , _ , rel , err := d .serviceWebdavClient (ctx , ref )
268274 if err != nil {
269275 return nil , err
270276 }
271277 if err := client .Write (rel , []byte {}, 0 ); err != nil {
272278 return nil , err
273279 }
274- return & storage.TouchFileResult {}, nil
280+ // The coordinator threads ResourceID into the session so the following
281+ // CommitUpload can address the file, so return the real ids rather than an
282+ // empty result. rel is the remote path, encoded the way GetMD encodes it.
283+ shareID , _ := shareInfoFromReference (ref )
284+ return & storage.TouchFileResult {
285+ SpaceID : shareID .GetOpaqueId (),
286+ ResourceID : & provider.ResourceId {
287+ StorageId : utils .OCMStorageProviderID ,
288+ SpaceId : shareID .GetOpaqueId (),
289+ OpaqueId : base64 .StdEncoding .EncodeToString ([]byte (filepath .Join ("/" , rel ))),
290+ },
291+ }, nil
275292}
276293
277294func (d * driver ) Move (ctx context.Context , oldRef , newRef * provider.Reference ) (* storage.MoveResult , error ) {
@@ -347,6 +364,44 @@ func convertStatToResourceInfo(ref *provider.Reference, f fs.FileInfo, share *oc
347364 return & ri , nil
348365}
349366
367+ // extractLock reads the DAV:lockdiscovery property and returns the active lock,
368+ // or nil when the resource carries no usable lock.
369+ //
370+ // gowebdav's Props.GetString formats a missing key as the string "<nil>", so an
371+ // absent lockdiscovery cannot be told apart by emptiness. Instead we require the
372+ // value to parse as <d:activelock> and to carry a lock token: a lock we cannot
373+ // name is one the caller could never match against its own, so treating it as
374+ // absent is the only useful reading.
375+ func extractLock (props gowebdav.Props ) * provider.Lock {
376+ raw := props .GetString (xml.Name {Space : "DAV:" , Local : "lockdiscovery" })
377+
378+ // Element names are matched on their local part only: the value is the raw
379+ // innerxml of lockdiscovery, so the xmlns:d declaration that bound the "d"
380+ // prefix stayed behind on the enclosing element and the prefix is unbound
381+ // here.
382+ var al struct {
383+ LockScope struct {
384+ Exclusive * struct {} `xml:"exclusive"`
385+ Shared * struct {} `xml:"shared"`
386+ } `xml:"lockscope"`
387+ LockToken struct {
388+ Href string `xml:"href"`
389+ } `xml:"locktoken"`
390+ }
391+ if err := xml .Unmarshal ([]byte (raw ), & al ); err != nil {
392+ return nil
393+ }
394+ if al .LockToken .Href == "" {
395+ return nil
396+ }
397+
398+ lockType := provider .LockType_LOCK_TYPE_EXCL
399+ if al .LockScope .Shared != nil && al .LockScope .Exclusive == nil {
400+ lockType = provider .LockType_LOCK_TYPE_SHARED
401+ }
402+ return & provider.Lock {LockId : al .LockToken .Href , Type : lockType }
403+ }
404+
350405func extractChecksum (props gowebdav.Props ) * provider.ResourceChecksum {
351406 checksums := props .GetString (xml.Name {Space : "http://owncloud.org/ns" , Local : "checksums" })
352407 if checksums == "" {
@@ -534,12 +589,29 @@ func (d *driver) GetLock(ctx context.Context, ref *provider.Reference) (*provide
534589 return nil , err
535590 }
536591
537- token , err := client .GetLock (rel )
592+ // gowebdav's GetLock cannot be used: it stats with a fixed property set that
593+ // omits lockdiscovery, so it reports the same value whatever the lock state.
594+ // getetag is requested alongside because an unlocked resource returns
595+ // lockdiscovery in a 404 propstat, and gowebdav only reads the 200 one — with
596+ // no other property to carry it, that propstat would be empty and the stat
597+ // would fail as if the file were missing.
598+ info , err := client .StatWithProps (rel , []string {"lockdiscovery" , "getetag" })
538599 if err != nil {
539600 return nil , err
540601 }
541602
542- return & provider.Lock {LockId : token , Type : provider .LockType_LOCK_TYPE_EXCL }, nil
603+ props , ok := info .Sys ().(gowebdav.Props )
604+ if ! ok {
605+ return nil , errtypes .InternalError ("ocm: unexpected stat result for " + ref .GetPath ())
606+ }
607+
608+ lock := extractLock (props )
609+ if lock == nil {
610+ // Callers distinguish "locked" from "not locked" by testing the returned
611+ // lock against nil, so a lock must never be invented here.
612+ return nil , errtypes .NotFound ("no lock found" )
613+ }
614+ return lock , nil
543615}
544616
545617func (d * driver ) RefreshLock (ctx context.Context , ref * provider.Reference , lock * provider.Lock , existingLockID string ) error {
0 commit comments