diff --git a/daemon/nfs41.h b/daemon/nfs41.h index c4280e0..057488f 100644 --- a/daemon/nfs41.h +++ b/daemon/nfs41.h @@ -39,11 +39,23 @@ typedef struct __nfs41_superblock { nfstime4 time_delta; uint64_t maxread; uint64_t maxwrite; - uint32_t layout_types; - bool_t cansettime; - uint32_t aclsupport; struct list_entry entry; /* position in nfs41_server.superblocks */ + /* constant filesystem attributes */ + unsigned int layout_types : 3; + unsigned int aclsupport : 3; + unsigned int cansettime : 1; + unsigned int link_support : 1; + unsigned int symlink_support : 1; + unsigned int case_preserving : 1; + unsigned int case_insensitive : 1; + + /* variable filesystem attributes */ + uint64_t space_avail; + uint64_t space_free; + uint64_t space_total; + time_t cache_expiration; /* applies to space_ attributes */ + SRWLOCK lock; } nfs41_superblock; @@ -375,6 +387,9 @@ int nfs41_superblock_for_fh( IN const nfs41_fh *parent OPTIONAL, OUT nfs41_path_fh *file); +void nfs41_superblock_space_changed( + IN nfs41_superblock *superblock); + void nfs41_superblock_list_init( IN nfs41_superblock_list *superblocks); diff --git a/daemon/nfs41_ops.c b/daemon/nfs41_ops.c index 46c3f06..0039631 100644 --- a/daemon/nfs41_ops.c +++ b/daemon/nfs41_ops.c @@ -426,6 +426,9 @@ int nfs41_open( &open_res.resok4.deleg_stateid); } #endif + + if (create == OPEN4_CREATE) + nfs41_superblock_space_changed(state->file.fh.superblock); out: return status; } @@ -607,6 +610,8 @@ int nfs41_create( file->path->path, &file->name, &file->fh, &file_info, &create_res.cinfo); ReleaseSRWLockShared(&file->path->lock); + + nfs41_superblock_space_changed(file->fh.superblock); out: return status; } @@ -749,6 +754,7 @@ int nfs41_write( eprintf("WRITE succeeded with count=0; returning %s\n", nfs_error_string(status)); } + nfs41_superblock_space_changed(file->fh.superblock); out: return status; } @@ -875,6 +881,7 @@ int nfs41_commit( nfs41_attr_cache_update(session_name_cache(session), file->fh.fileid, &info); } + nfs41_superblock_space_changed(file->fh.superblock); out: return status; } @@ -1170,6 +1177,8 @@ int nfs41_remove( nfs41_name_cache_remove(session_name_cache(session), parent->path->path, target, &remove_res.cinfo); ReleaseSRWLockShared(&parent->path->lock); + + nfs41_superblock_space_changed(parent->fh.superblock); out: return status; } @@ -1322,6 +1331,9 @@ int nfs41_setattr( memcpy(&info->attrmask, &setattr_res.attrsset, sizeof(bitmap4)); nfs41_attr_cache_update(session_name_cache(session), file->fh.fileid, info); + + if (setattr_res.attrsset.arr[0] & FATTR4_WORD0_SIZE) + nfs41_superblock_space_changed(file->fh.superblock); out: return status; } @@ -1430,6 +1442,8 @@ int nfs41_link( dst_dir->path->path, target, &link_out->fh, &info[1], &link_res.cinfo); ReleaseSRWLockShared(&dst_dir->path->lock); + + nfs41_superblock_space_changed(dst_dir->fh.superblock); out: return status; } diff --git a/daemon/nfs41_superblock.c b/daemon/nfs41_superblock.c index 80fdcfe..d09c361 100644 --- a/daemon/nfs41_superblock.c +++ b/daemon/nfs41_superblock.c @@ -82,8 +82,10 @@ static int get_superblock_attrs( nfs41_file_info info; attr_request.arr[0] = FATTR4_WORD0_SUPPORTED_ATTRS | - FATTR4_WORD0_CANSETTIME | FATTR4_WORD0_MAXREAD | - (uint32_t)(FATTR4_WORD0_MAXWRITE) | FATTR4_WORD0_ACLSUPPORT; + FATTR4_WORD0_LINK_SUPPORT | FATTR4_WORD0_SYMLINK_SUPPORT | + FATTR4_WORD0_ACLSUPPORT | FATTR4_WORD0_CANSETTIME | + FATTR4_WORD0_CASE_INSENSITIVE | FATTR4_WORD0_CASE_PRESERVING | + FATTR4_WORD0_MAXREAD | (uint32_t)(FATTR4_WORD0_MAXWRITE); attr_request.arr[1] = FATTR4_WORD1_FS_LAYOUT_TYPE | FATTR4_WORD1_TIME_DELTA; attr_request.count = 2; @@ -112,6 +114,11 @@ static int get_superblock_attrs( superblock->maxwrite = session->fore_chan_attrs.ca_maxrequestsize; superblock->layout_types = info.fs_layout_types; + superblock->aclsupport = info.aclsupport; + superblock->link_support = info.link_support; + superblock->symlink_support = info.symlink_support; + superblock->case_preserving = info.case_preserving; + superblock->case_insensitive = info.case_insensitive; if (bitmap_isset(&info.attrmask, 0, FATTR4_WORD0_CANSETTIME)) superblock->cansettime = info.cansettime; @@ -122,15 +129,18 @@ static int get_superblock_attrs( if (!bitmap_isset(&info.attrmask, 1, FATTR4_WORD1_TIME_DELTA)) superblock->time_delta.seconds = 1; - superblock->aclsupport = info.aclsupport; dprintf(SBLVL, "attributes for fsid(%llu,%llu): " "maxread=%llu, maxwrite=%llu, layout_types: 0x%X, " - "cansettime=%u, time_delta={%llu,%u}, aclsupport=%d\n", + "cansettime=%u, time_delta={%llu,%u}, aclsupport=%u, " + "link_support=%u, symlink_support=%u, case_preserving=%u, " + "case_insensitive=%u\n", superblock->fsid.major, superblock->fsid.minor, superblock->maxread, superblock->maxwrite, superblock->layout_types, superblock->cansettime, superblock->time_delta.seconds, superblock->time_delta.nseconds, - superblock->aclsupport); + superblock->aclsupport, superblock->link_support, + superblock->symlink_support, superblock->case_preserving, + superblock->case_insensitive); out: return status; } @@ -236,3 +246,12 @@ out: file->fh.superblock, status); return status; } + +void nfs41_superblock_space_changed( + IN nfs41_superblock *superblock) +{ + /* invalidate cached volume size attributes */ + AcquireSRWLockExclusive(&superblock->lock); + superblock->cache_expiration = 0; + ReleaseSRWLockExclusive(&superblock->lock); +} diff --git a/daemon/volume.c b/daemon/volume.c index c1c7d92..aee0542 100644 --- a/daemon/volume.c +++ b/daemon/volume.c @@ -24,6 +24,7 @@ #include #include #include +#include #include "nfs41_ops.h" #include "from_kernel.h" @@ -40,6 +41,8 @@ #define TO_UNITS(bytes) (bytes / BYTES_PER_UNIT) +#define VOLUME_CACHE_EXPIRATION 20 + /* NFS41_VOLUME_QUERY */ static int parse_volume(unsigned char *buffer, uint32_t length, nfs41_upcall *upcall) @@ -63,21 +66,46 @@ static int get_volume_size_info( OUT OPTIONAL PLONGLONG avail_out) { nfs41_file_info info = { 0 }; - bitmap4 attr_request = { 2, { 0, FATTR4_WORD1_SPACE_AVAIL | - FATTR4_WORD1_SPACE_FREE | FATTR4_WORD1_SPACE_TOTAL } }; - int status; + nfs41_superblock *superblock = state->file.fh.superblock; + int status = ERROR_NOT_FOUND; - /* query the space_ attributes of the root filesystem */ - status = nfs41_getattr(state->session, &state->file, &attr_request, &info); - if (status) { - eprintf("nfs41_getattr() failed with %s\n", - nfs_error_string(status)); - status = nfs_to_windows_error(status, ERROR_BAD_NET_RESP); - goto out; + AcquireSRWLockShared(&superblock->lock); + /* check superblock for cached attributes */ + if (time(NULL) <= superblock->cache_expiration) { + info.space_total = superblock->space_total; + info.space_avail = superblock->space_avail; + info.space_free = superblock->space_free; + status = NO_ERROR; + + dprintf(2, "%s cached: %llu user, %llu free of %llu total\n", + query, info.space_avail, info.space_free, info.space_total); } + ReleaseSRWLockShared(&superblock->lock); - dprintf(2, "%s: %llu user, %llu free of %llu total\n", query, - info.space_avail, info.space_free, info.space_total); + if (status) { + bitmap4 attr_request = { 2, { 0, FATTR4_WORD1_SPACE_AVAIL | + FATTR4_WORD1_SPACE_FREE | FATTR4_WORD1_SPACE_TOTAL } }; + + /* query the space_ attributes of the filesystem */ + status = nfs41_getattr(state->session, &state->file, + &attr_request, &info); + if (status) { + eprintf("nfs41_getattr() failed with %s\n", + nfs_error_string(status)); + status = nfs_to_windows_error(status, ERROR_BAD_NET_RESP); + goto out; + } + + AcquireSRWLockExclusive(&superblock->lock); + superblock->space_total = info.space_total; + superblock->space_avail = info.space_avail; + superblock->space_free = info.space_free; + superblock->cache_expiration = time(NULL) + VOLUME_CACHE_EXPIRATION; + ReleaseSRWLockExclusive(&superblock->lock); + + dprintf(2, "%s: %llu user, %llu free of %llu total\n", + query, info.space_avail, info.space_free, info.space_total); + } if (total_out) *total_out = TO_UNITS(info.space_total); if (user_out) *user_out = TO_UNITS(info.space_avail); @@ -90,32 +118,20 @@ static int handle_volume_attributes( IN volume_upcall_args *args, IN nfs41_open_state *state) { - /* query the case_ attributes of the root filesystem */ - nfs41_file_info info = { 0 }; - bitmap4 attr_request = { 1, { FATTR4_WORD0_CASE_INSENSITIVE | - FATTR4_WORD0_CASE_PRESERVING | FATTR4_WORD0_SYMLINK_SUPPORT | - FATTR4_WORD0_LINK_SUPPORT } }; PFILE_FS_ATTRIBUTE_INFORMATION attr = &args->info.attribute; + const nfs41_superblock *superblock = state->file.fh.superblock; int status = NO_ERROR; - status = nfs41_getattr(state->session, &state->file, &attr_request, &info); - if (status) { - eprintf("nfs41_getattr() failed with %s\n", - nfs_error_string(status)); - status = nfs_to_windows_error(status, ERROR_BAD_NET_RESP); - goto out; - } - attr->FileSystemAttributes = FILE_SUPPORTS_REMOTE_STORAGE; - if (info.link_support) + if (superblock->link_support) attr->FileSystemAttributes |= FILE_SUPPORTS_HARD_LINKS; - if (info.symlink_support) + if (superblock->symlink_support) attr->FileSystemAttributes |= FILE_SUPPORTS_REPARSE_POINTS; - if (info.case_preserving) + if (superblock->case_preserving) attr->FileSystemAttributes |= FILE_CASE_PRESERVED_NAMES; - if (!info.case_insensitive) + if (!superblock->case_insensitive) attr->FileSystemAttributes |= FILE_CASE_SENSITIVE_SEARCH; - if (state->file.fh.superblock->aclsupport) + if (superblock->aclsupport) attr->FileSystemAttributes |= FILE_PERSISTENT_ACLS; attr->MaximumComponentNameLength = NFS41_MAX_COMPONENT_LEN; @@ -126,9 +142,9 @@ static int handle_volume_attributes( dprintf(2, "FileFsAttributeInformation: case_preserving %u, " "case_insensitive %u, max component %u\n", - info.case_preserving, info.case_insensitive, + superblock->case_preserving, superblock->case_insensitive, attr->MaximumComponentNameLength); -out: + return status; }