Repository: rclone/rclone
File: backend/googlecloudstorage/googlecloudstorage.go (Go)

Code before:
				remote = path.Join(bucket, remote)
			}
			// is this a directory marker?
			if isDirectory {
				continue // skip directory marker
			}
			err = fn(remote, object, false)
			if err != nil {
				return err
			}
		}
		if objects.NextPageToken == "" {
			break
		}
		list.PageToken(objects.NextPageToken)
	}
	return nil
}

// Convert a list item into a DirEntry
func (f *Fs) itemToDirEntry(ctx context.Context, remote string, object *storage.Object, isDirectory bool) (fs.DirEntry, error) {
	if isDirectory {
		d := fs.NewDir(remote, time.Time{}).SetSize(int64(object.Size))
		return d, nil
	}
	o, err := f.newObjectWithInfo(ctx, remote, object)
	if err != nil {
		return nil, err
	}
	return o, nil
}

// listDir lists a single directory
func (f *Fs) listDir(ctx context.Context, bucket, directory, prefix string, addBucket bool) (entries fs.DirEntries, err error) {
	// List the objects
	err = f.list(ctx, bucket, directory, prefix, addBucket, false, func(remote string, object *storage.Object, isDirectory bool) error {
		entry, err := f.itemToDirEntry(ctx, remote, object, isDirectory)
		if err != nil {
			return err
		}
		if entry != nil {
			entries = append(entries, entry)
		}
		return nil
	})
	if err != nil {
		return nil, err
	}
	// bucket must be present if listing succeeded
	f.cache.MarkOK(bucket)
	return entries, err

Review comment (performance):
@ncw  This is causing `rclone lsd -R` to throw out the following errors when encountering directory markers:
```
2023/05/26 22:02:24 ERROR : foo: Entry doesn't belong in directory "foo" (too short) - ignoring
           0 2023-05-26 22:02:24        -1 foo/bar
           0 2023-05-26 22:02:24        -1 foo/baz
2023/05/26 22:02:24 ERROR : httpdocs: Entry doesn't belong in directory "httpdocs" (too short) - ignoring
           0 2023-05-26 22:02:24        -1 httpdocs/admin
           0 2023-05-26 22:02:24        -1 httpdocs/bar
           0 2023-05-26 22:02:24        -1 httpdocs/cache
```

In the above example, `foo/` and `httpdocs/` are both GCS directory markers. They fall into https://github.com/rclone/rclone/blob/e43b5ce/fs/list/list.go#L79 : `prefix` is set to `foo/` when the `remote` is `foo`. 

Also, if the root directory is a directory marker, it falls into https://github.com/rclone/rclone/blob/e43b5ce/fs/list/list.go#L82 ,  triggering an error like this:
```
2023/05/26 22:08:30 ERROR : : Entry doesn't belong in directory "" (same as directory) - ignoring
```

I think we need to handle directory markers properly in `filterAndSortDir()`. 

A possible solution is to prepend `/` to every entry, including directory markers, e.g.
```
/httpdocs (directory-marker)
/httpdocs/admin (non directory-marker subdir)
/httpdocs/cache (non directory-marker subdir)
```
When running `rclone lsd -r httpdocs`, the output would be:
```
/ (directory-marker)
/admin (non directory-marker subdir)
/cache (non directory-marker subdir)
```

Alternatively, we could show the entries as follows:
```
httpdocs/
httpdocs/admin/
httpdocs/cache/
```
and
```
/
admin/
cache/
```

Thoughts or suggestions? 🙏 

Note: I added this comment to the original commit but I realised that it's probably not visible. So moving it here. Hope it's okay.

Code after:
				err = fn(remote, &object, true)
				if err != nil {
					return err
				}
			}
		}
		foundItems += len(objects.Items)
		for _, object := range objects.Items {
			remote := f.opt.Enc.ToStandardPath(object.Name)
			if !strings.HasPrefix(remote, prefix) {
				fs.Logf(f, "Odd name received %q", object.Name)
				continue
			}
			remote = remote[len(prefix):]
			isDirectory := remote == "" || strings.HasSuffix(remote, "/")
			if addBucket {
				remote = path.Join(bucket, remote)
			}
			// is this a directory marker?
			if isDirectory {
				// Don't insert the root directory
				if remote == directory {
					continue
				}
				// process directory markers as directories
				remote = strings.TrimRight(remote, "/")
			}

			err = fn(remote, object, isDirectory)
			if err != nil {
				return err
			}
		}
		if objects.NextPageToken == "" {
			break
		}
		list.PageToken(objects.NextPageToken)
	}
	if f.opt.DirectoryMarkers && foundItems == 0 && directory != "" {
		// Determine whether the directory exists or not by whether it has a marker
		_, err := f.readObjectInfo(ctx, bucket, directory)
		if err != nil {
			if err == fs.ErrorObjectNotFound {
				return fs.ErrorDirNotFound
			}
			return err
		}
	}

	return nil
}