diff --git a/pkg/storage/fs/owncloudsql/filecache/filecache.go b/pkg/storage/fs/owncloudsql/filecache/filecache.go index 8a7dc6cdd12..d0a0c486c7f 100644 --- a/pkg/storage/fs/owncloudsql/filecache/filecache.go +++ b/pkg/storage/fs/owncloudsql/filecache/filecache.go @@ -253,6 +253,38 @@ func (c *Cache) Path(id interface{}) (string, error) { return path, nil } +// List returns the list of entries below the given path +func (c *Cache) List(storage interface{}, p string) ([]*File, error) { + storageID, err := toIntID(storage) + if err != nil { + return nil, err + } + + rows, err := c.db.Query(` + SELECT + fc.fileid, fc.storage, fc.path, fc.parent, fc.permissions, fc.mimetype, fc.mimepart, + mt.mimetype, fc.size, fc.mtime, fc.storage_mtime, fc.encrypted, fc.unencrypted_size, + fc.name, fc.etag, fc.checksum + FROM oc_filecache fc + LEFT JOIN oc_mimetypes mt ON fc.mimetype = mt.id + WHERE path != '' AND path LIKE ? AND PATH NOT LIKE ? AND storage = ? + `, p+"%", p+"%/%", storageID) + if err != nil { + return nil, err + } + defer rows.Close() + entries := []*File{} + for rows.Next() { + entry, err := c.rowToFile(rows) + if err != nil { + return nil, err + } + entries = append(entries, entry) + } + + return entries, nil +} + // Permissions returns the permissions for the specified storage/path func (c *Cache) Permissions(storage interface{}, p string) (*provider.ResourcePermissions, error) { entry, err := c.Get(storage, p) @@ -277,6 +309,9 @@ func (c *Cache) InsertOrUpdate(storage interface{}, data map[string]interface{}) defer func() { _ = tx.Rollback() }() id, err := c.doInsertOrUpdate(tx, storage, data, false) + if err != nil { + return -1, err + } err = tx.Commit() if err != nil { diff --git a/pkg/storage/fs/owncloudsql/filecache/filecache_test.go b/pkg/storage/fs/owncloudsql/filecache/filecache_test.go index 55e461061a1..35518fd3df5 100644 --- a/pkg/storage/fs/owncloudsql/filecache/filecache_test.go +++ b/pkg/storage/fs/owncloudsql/filecache/filecache_test.go @@ -126,6 +126,26 @@ var _ = Describe("Filecache", func() { }) }) + Describe("List", func() { + It("lists all entries", func() { + list, err := cache.List(1, "") + Expect(err).ToNot(HaveOccurred()) + Expect(len(list)).To(Equal(3)) + }) + + It("filters", func() { + list, err := cache.List(1, "files_trashbin/") + Expect(err).ToNot(HaveOccurred()) + Expect(len(list)).To(Equal(3)) + }) + + It("filters deep", func() { + list, err := cache.List(1, "files/Photos/") + Expect(err).ToNot(HaveOccurred()) + Expect(len(list)).To(Equal(3)) + }) + }) + Describe("Path", func() { It("returns the path", func() { path, err := cache.Path(10) diff --git a/pkg/storage/fs/owncloudsql/owncloudsql.go b/pkg/storage/fs/owncloudsql/owncloudsql.go index e26f92b57b0..e89d7423ee8 100644 --- a/pkg/storage/fs/owncloudsql/owncloudsql.go +++ b/pkg/storage/fs/owncloudsql/owncloudsql.go @@ -564,18 +564,7 @@ func (fs *ocfs) getUserStorage(ctx context.Context) (int, error) { return id, err } -func (fs *ocfs) convertToResourceInfo(ctx context.Context, fi os.FileInfo, ip string, sp string, mdKeys []string) (*provider.ResourceInfo, error) { - storage, err := fs.getStorage(ip) - if err != nil { - return nil, err - } - - p := fs.toDatabasePath(ip) - cacheEntry, err := fs.filecache.Get(storage, p) - if err != nil { - return nil, err - } - +func (fs *ocfs) convertToResourceInfo(ctx context.Context, entry *filecache.File, ip string, mdKeys []string) (*provider.ResourceInfo, error) { mdKeysMap := make(map[string]struct{}) for _, k := range mdKeys { mdKeysMap[k] = struct{}{} @@ -586,16 +575,23 @@ func (fs *ocfs) convertToResourceInfo(ctx context.Context, fi os.FileInfo, ip st returnAllKeys = true } + var resourceType provider.ResourceType + isDir := entry.MimeTypeString == "httpd/unix-directory" + if isDir { + resourceType = provider.ResourceType_RESOURCE_TYPE_CONTAINER + } else { + resourceType = provider.ResourceType_RESOURCE_TYPE_FILE + } + ri := &provider.ResourceInfo{ - Id: &provider.ResourceId{OpaqueId: strconv.Itoa(cacheEntry.ID)}, - Path: sp, - Type: getResourceType(fi.IsDir()), - Etag: cacheEntry.Etag, - MimeType: mime.Detect(fi.IsDir(), ip), - Size: uint64(fi.Size()), + Id: &provider.ResourceId{OpaqueId: strconv.Itoa(entry.ID)}, + Path: strings.TrimPrefix(entry.Path, "files/"), + Type: resourceType, + Etag: entry.Etag, + MimeType: entry.MimeTypeString, + Size: uint64(entry.Size), Mtime: &types.Timestamp{ - Seconds: uint64(fi.ModTime().Unix()), - // TODO read nanos from where? Nanos: fi.MTimeNanos, + Seconds: uint64(entry.MTime), }, ArbitraryMetadata: &provider.ArbitraryMetadata{ Metadata: map[string]string{}, // TODO aduffeck: which metadata needs to go in here? @@ -611,23 +607,17 @@ func (fs *ocfs) convertToResourceInfo(ctx context.Context, fi os.FileInfo, ip st ri.PermissionSet = fs.permissionSet(ctx, ri.Owner) // checksums - if !fi.IsDir() { + if !isDir { if _, checksumRequested := mdKeysMap[checksumsKey]; returnAllKeys || checksumRequested { // TODO which checksum was requested? sha1 adler32 or md5? for now hardcode sha1? - readChecksumIntoResourceChecksum(ctx, cacheEntry.Checksum, storageprovider.XSSHA1, ri) - readChecksumIntoOpaque(ctx, cacheEntry.Checksum, storageprovider.XSMD5, ri) + readChecksumIntoResourceChecksum(ctx, entry.Checksum, storageprovider.XSSHA1, ri) + readChecksumIntoOpaque(ctx, entry.Checksum, storageprovider.XSMD5, ri) readChecksumIntoOpaque(ctx, ip, storageprovider.XSAdler32, ri) } } return ri, nil } -func getResourceType(isDir bool) provider.ResourceType { - if isDir { - return provider.ResourceType_RESOURCE_TYPE_CONTAINER - } - return provider.ResourceType_RESOURCE_TYPE_FILE -} // GetPathByID returns the storage relative path for the file id, without the internal namespace func (fs *ocfs) GetPathByID(ctx context.Context, id *provider.ResourceId) (string, error) { @@ -1493,14 +1483,22 @@ func (fs *ocfs) listWithNominalHome(ctx context.Context, ip string, mdKeys []str return nil, errors.Wrap(err, "owncloudsql: error reading permissions") } - mds, err := ioutil.ReadDir(ip) + storage, err := fs.getStorage(ip) + if err != nil { + return nil, err + } + entries, err := fs.filecache.List(storage, fs.toDatabasePath(ip)+"/") if err != nil { return nil, errors.Wrapf(err, "owncloudsql: error listing %s", ip) } + owner := fs.getOwner(ip) finfos := []*provider.ResourceInfo{} - for _, md := range mds { - cp := filepath.Join(ip, md.Name()) - m, err := fs.convertToResourceInfo(ctx, md, cp, fs.toStoragePath(ctx, cp), mdKeys) + for _, entry := range entries { + cp := filepath.Join(fs.c.DataDirectory, owner, entry.Path) + if err != nil { + return nil, err + } + m, err := fs.convertToResourceInfo(ctx, entry, cp, mdKeys) if err != nil { appctx.GetLogger(ctx).Error().Err(err).Str("path", cp).Msg("error converting to a resource info") } @@ -1536,15 +1534,19 @@ func (fs *ocfs) listHome(ctx context.Context, home string, mdKeys []string) ([]* return nil, errors.Wrap(err, "owncloudsql: error reading permissions") } - mds, err := ioutil.ReadDir(ip) + storage, err := fs.getStorage(ip) if err != nil { - return nil, errors.Wrap(err, "owncloudsql: error listing files") + return nil, err } - + entries, err := fs.filecache.List(storage, fs.toDatabasePath(ip)+"/") + if err != nil { + return nil, errors.Wrapf(err, "owncloudsql: error listing %s", ip) + } + owner := fs.getOwner(ip) finfos := []*provider.ResourceInfo{} - for _, md := range mds { - cp := filepath.Join(ip, md.Name()) - m, err := fs.convertToResourceInfo(ctx, md, cp, fs.toStoragePath(ctx, cp), mdKeys) + for _, entry := range entries { + cp := filepath.Join(fs.c.DataDirectory, owner, entry.Path) + m, err := fs.convertToResourceInfo2(ctx, entry, cp, mdKeys) if err != nil { appctx.GetLogger(ctx).Error().Err(err).Str("path", cp).Msg("error converting to a resource info") } @@ -1553,7 +1555,7 @@ func (fs *ocfs) listHome(ctx context.Context, home string, mdKeys []string) ([]* // list shadow_files ip = fs.toInternalShadowPath(ctx, home) - mds, err = ioutil.ReadDir(ip) + mds, err := ioutil.ReadDir(ip) if err != nil { return nil, errors.Wrap(err, "owncloudsql: error listing shadow_files") }