diff --git a/include/nwvolume.h b/include/nwvolume.h index 91ce940..86bf255 100644 --- a/include/nwvolume.h +++ b/include/nwvolume.h @@ -130,6 +130,8 @@ extern int nw_set_vol_restrictions(uint8 volnr, int uid, uint32 quota); extern int nw_get_vol_restrictions(uint8 volnr, int uid, uint32 *quota, uint32 *inuse); extern int nw_check_vol_user_space(uint8 volnr, int uid, const char *unixname, off_t old_size, off_t new_size); +extern int nw_adjust_vol_user_space(uint8 volnr, int uid, + off_t old_size, off_t new_size); extern uint32 nw_vol_inode_to_handle(int volume, ino_t inode, DEV_NAMESPACE_MAP *dnm); diff --git a/src/nwfile.c b/src/nwfile.c index 8313f00..5cc9eb4 100644 --- a/src/nwfile.c +++ b/src/nwfile.c @@ -1018,9 +1018,15 @@ int nw_write_file(int fhandle, uint8 *data, int size, uint32 offset) if (qresult < 0) { size = qresult; } else { + int requested = size; size = write(fh->fd, data, size); - if (size > -1) + if (size > -1) { + off_t written_end = (off_t)offset + (off_t)size; + if (requested > 0 && written_end > old_size) + (void)nw_adjust_vol_user_space((uint8)fh->volume, act_uid, + old_size, written_end); fh->fh_flags &= ~FH_CREATED_NEW; + } fh->offd+=(long)size; if (!fh->modified) fh->modified++; @@ -1035,8 +1041,13 @@ int nw_write_file(int fhandle, uint8 *data, int size, uint32 offset) result = nw_check_vol_user_space((uint8)fh->volume, act_uid, fh->fname, qst.st_size, (off_t)offset); - if (!result) + if (!result) { + off_t old_size = qst.st_size; result = unx_ftruncate(fh->fd, offset); + if (!result) + (void)nw_adjust_vol_user_space((uint8)fh->volume, act_uid, + old_size, (off_t)offset); + } XDPRINTF((5,0,"File %s is truncated, result=%d", fh->fname, result)); fh->offd = -1L; if (!result && !fh->modified) diff --git a/src/nwvolume.c b/src/nwvolume.c index 24333ad..bcb946d 100644 --- a/src/nwvolume.c +++ b/src/nwvolume.c @@ -1097,11 +1097,14 @@ static uint32 nw_volume_uid_usage_4k(uint8 volnr, int uid) static int nw_set_vol_restrictions_metadata(uint8 volnr, int uid, uint32 quota) { zNW_user_quota_s userquota; + uint32 inuse; int i; if (read_userquota_from_volume(volnr, &userquota)) nwfs_userquota_init(&userquota); + inuse = nw_volume_uid_usage_4k(volnr, uid); + for (i = 0; i < userquota.nwuq_num_users; i++) { if (nwfs_guid_to_mars_uid(&userquota.nwuq_user[i].nwur_user) == uid) { if (quota == 0) { @@ -1112,6 +1115,7 @@ static int nw_set_vol_restrictions_metadata(uint8 volnr, int uid, uint32 quota) userquota.nwuq_num_users--; } else { userquota.nwuq_user[i].nwur_restriction = (SQUAD)quota; + userquota.nwuq_user[i].nwur_reserved_2 = (QUAD)inuse; } return(write_userquota_to_volume(volnr, &userquota) ? -0xfb : 0); } @@ -1124,11 +1128,32 @@ static int nw_set_vol_restrictions_metadata(uint8 volnr, int uid, uint32 quota) mars_uid_to_nwfs_guid(uid, &userquota.nwuq_user[userquota.nwuq_num_users].nwur_user); userquota.nwuq_user[userquota.nwuq_num_users].nwur_restriction = (SQUAD)quota; - userquota.nwuq_user[userquota.nwuq_num_users].nwur_reserved_2 = 0; + userquota.nwuq_user[userquota.nwuq_num_users].nwur_reserved_2 = (QUAD)inuse; userquota.nwuq_num_users++; return(write_userquota_to_volume(volnr, &userquota) ? -0xfb : 0); } +static uint32 nw_userquota_used_4k(zNW_user_restriction_s *entry, uint32 scanned) +{ + uint32 stored; + + if (!entry) + return(scanned); + if (entry->nwur_reserved_2 > (QUAD)0xffffffffU) + stored = 0xffffffffU; + else + stored = (uint32)entry->nwur_reserved_2; + + /* + * NetWare/NSS does not derive live usage from POSIX uid ownership. It + * stores per-owner usage in the volume user-space-restriction tree and + * adjusts it on writes/deletes/owner changes. Keep the metadata backend in + * that model: use the stored amount for online accounting, but never let it + * go below a full scan of existing host-owned data. + */ + return(stored > scanned ? stored : scanned); +} + static int nw_get_vol_restrictions_metadata(uint8 volnr, int uid, uint32 *quota, uint32 *inuse) { zNW_user_quota_s userquota; @@ -1141,17 +1166,51 @@ static int nw_get_vol_restrictions_metadata(uint8 volnr, int uid, uint32 *quota, for (i = 0; i < userquota.nwuq_num_users; i++) { if (nwfs_guid_to_mars_uid(&userquota.nwuq_user[i].nwur_user) == uid) { + uint32 scanned = nw_volume_uid_usage_4k(volnr, uid); if (userquota.nwuq_user[i].nwur_restriction <= 0) *quota = 0x40000000; else *quota = (uint32)userquota.nwuq_user[i].nwur_restriction; - *inuse = nw_volume_uid_usage_4k(volnr, uid); + *inuse = nw_userquota_used_4k(&userquota.nwuq_user[i], scanned); return(0); } } return(0); } +static int nw_adjust_vol_user_space_metadata(uint8 volnr, int uid, int delta4k) +{ + zNW_user_quota_s userquota; + int i; + + if (delta4k == 0) + return(0); + if (read_userquota_from_volume(volnr, &userquota)) + return(0); + + for (i = 0; i < userquota.nwuq_num_users; i++) { + if (nwfs_guid_to_mars_uid(&userquota.nwuq_user[i].nwur_user) == uid) { + uint32 scanned = nw_volume_uid_usage_4k(volnr, uid); + uint32 used = nw_userquota_used_4k(&userquota.nwuq_user[i], scanned); + + if (delta4k < 0) { + uint32 dec = (uint32)(-delta4k); + used = (used > dec) ? (used - dec) : 0; + } else if (used <= 0xffffffffU - (uint32)delta4k) { + used += (uint32)delta4k; + } else { + used = 0xffffffffU; + } + userquota.nwuq_user[i].nwur_reserved_2 = (QUAD)used; + XDPRINTF((2,0, + "VOL_AdjustUsedUserSpace vol=%d uid=%d delta4k=%d used4k=%u", + volnr, uid, delta4k, used)); + return(write_userquota_to_volume(volnr, &userquota) ? -0xfb : 0); + } + } + return(0); +} + /* NOTE: The error numbers in here are probably wrong */ int nw_set_vol_restrictions(uint8 volnr, int uid, uint32 quota) { @@ -1353,6 +1412,32 @@ int nw_check_vol_user_space(uint8 volnr, int uid, const char *unixname, return(0); } +int nw_adjust_vol_user_space(uint8 volnr, int uid, off_t old_size, off_t new_size) +{ + uint32 old_blocks; + uint32 new_blocks; + int backend = nwfs_quota_backend_current(); + + if (new_size == old_size) + return(0); + + if (backend != NWFS_QUOTA_BACKEND_ID_METADATAONLY && + backend != NWFS_QUOTA_BACKEND_ID_NWQUOTA) { + const char *device; + + if (volnr >= used_nw_volumes || nw_volumes == (NW_VOL *) NULL) + return(0); + device = find_device_file(nw_volumes[volnr].unixname); + if (device != (char *) NULL) + return(0); + } + + old_blocks = quota_blocks_for_size(old_size); + new_blocks = quota_blocks_for_size(new_size); + return(nw_adjust_vol_user_space_metadata(volnr, uid, + (int)new_blocks - (int)old_blocks)); +} + #else int nw_set_vol_restrictions(uint8 volnr, int uid, uint32 quota) { @@ -1371,4 +1456,9 @@ int nw_check_vol_user_space(uint8 volnr, int uid, const char *unixname, { return(0); } + +int nw_adjust_vol_user_space(uint8 volnr, int uid, off_t old_size, off_t new_size) +{ + return(0); +} #endif