Skip to content

Commit

Permalink
Be more selective about file metdata in listdir
Browse files Browse the repository at this point in the history
The directory iterator we use is configurable about what file
metdata to retrieve. This can in some situations significantly
improve performance of generating directory listing.
  • Loading branch information
anodos325 committed Sep 6, 2024
1 parent fa423b8 commit 0155796
Showing 1 changed file with 40 additions and 2 deletions.
42 changes: 40 additions & 2 deletions src/middlewared/middlewared/plugins/filesystem.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
from middlewared.utils.filesystem import attrs, stat_x
from middlewared.utils.filesystem.acl import acl_is_present
from middlewared.utils.filesystem.constants import FileType
from middlewared.utils.filesystem.directory import DirectoryIterator
from middlewared.utils.filesystem.directory import DirectoryIterator, DirectoryRequestMask
from middlewared.utils.filesystem.utils import timespec_convert
from middlewared.utils.mount import getmntinfo
from middlewared.utils.nss import pwd, grp
Expand Down Expand Up @@ -214,6 +214,31 @@ def mkdir(self, data):
'zfs_attrs': ['ARCHIVE']
}

@private
def listdir_request_mask(self, select):
""" create request mask for directory listing """
if select is None:
# request_mask=None means ALL in the directory iterator
return None

request_mask = 0
for i in select:
selected = i[0] if isinstance(i, list) else i

match selected:
case 'realpath':
request_mask |= DirectoryRequestMask.REALPATH
case 'acl':
request_mask |= DirectoryRequestMask.ACL
case 'zfs_attrs':
request_mask |= DirectoryRequestMask.ZFS_ATTRS
case 'is_ctldir':
request_mask |= DirectoryRequestMask.CTLDIR
case 'xattrs':
request_mask |= DirectoryRequestMask.XATTRS

return request_mask

@accepts(
Str('path', required=True),
Ref('query-filters'),
Expand Down Expand Up @@ -274,6 +299,19 @@ def listdir(self, path, filters, options):
if not path.is_dir():
raise CallError(f'Path {path} is not a directory', errno.ENOTDIR)

if options.get('count') is True:
# We're just getting count, drop any unnecessary info
request_mask = 0
else:
request_mask = self.listdir_request_mask(options.get('select', None))

if request_mask and (request_mask & DirectoryRequestMask.ZFS_ATTRS):
# Make sure this is actually ZFS before issuing FS ioctls
try:
self.get_zfs_attributes(path)
except Exception:
raise CallError(f'{path}: ZFS attributes are not supported.')

file_type = None
for filter_ in filters:
if filter_[0] not in ['type']:
Expand All @@ -300,7 +338,7 @@ def listdir(self, path, filters, options):
# filter these here.
filters.extend([['is_mountpoint', '=', True], ['name', '!=', IX_APPS_DIR_NAME]])

with DirectoryIterator(path, file_type=file_type) as d_iter:
with DirectoryIterator(path, file_type=file_type, request_mask=request_mask) as d_iter:
return filter_list(d_iter, filters, options)

@accepts(Str('path'), roles=['FILESYSTEM_ATTRS_READ'])
Expand Down

0 comments on commit 0155796

Please sign in to comment.