From 3d024372293a279cf3a6cbdfe75a384ec7c8b200 Mon Sep 17 00:00:00 2001 From: Jeffrey Altman Date: Thu, 5 Apr 2012 23:14:18 -0400 Subject: [PATCH] Windows: Check Avail Space on extending SetEndOfFile When cm_SetLength() is called with an extending file length, check the available free space in the volume to see if the new length will fit. If not, return CM_ERROR_SPACE. This permits applications to discover that there is insufficient space prior to writing all of the data into the windows page cache at which point it will be too late. There is still the possibility of a race that can result in data loss if two applications are writing into the same volume at the same time and there is insufficient room. Change-Id: Ieef2c48f5b6edc8d101b6527af3a3f87fe55f6ca Reviewed-on: http://gerrit.openafs.org/7057 Tested-by: Jeffrey Altman Reviewed-by: Jeffrey Altman --- src/WINNT/afsd/cm_vnodeops.c | 78 +++++++++++++++++++++++++++++++++++- src/WINNT/afsd/cm_vnodeops.h | 2 + 2 files changed, 78 insertions(+), 2 deletions(-) diff --git a/src/WINNT/afsd/cm_vnodeops.c b/src/WINNT/afsd/cm_vnodeops.c index 51df2fea8..0da77a692 100644 --- a/src/WINNT/afsd/cm_vnodeops.c +++ b/src/WINNT/afsd/cm_vnodeops.c @@ -2649,6 +2649,73 @@ void cm_StatusFromAttr(AFSStoreStatus *statusp, cm_scache_t *scp, cm_attr_t *att statusp->Mask = mask; } +int +cm_IsSpaceAvailable(cm_fid_t * fidp, osi_hyper_t *sizep, cm_user_t *userp, cm_req_t *reqp) +{ + int spaceAvail = 1; + afs_uint32 code; + cm_conn_t *connp; + struct rx_connection * rxconnp; + AFSFetchVolumeStatus volStat; + cm_volume_t *volp = NULL; + afs_uint32 volType; + char *Name; + char *OfflineMsg; + char *MOTD; + char volName[32]="(unknown)"; + char offLineMsg[256]="server temporarily inaccessible"; + char motd[256]="server temporarily inaccessible"; + osi_hyper_t freespace; + + if (fidp->cell==AFS_FAKE_ROOT_CELL_ID && + fidp->volume==AFS_FAKE_ROOT_VOL_ID) + { + goto _done; + } + + volp = cm_GetVolumeByFID(fidp); + if (!volp) { + spaceAvail = 0; + goto _done; + } + volType = cm_VolumeType(volp, fidp->volume); + if (volType == ROVOL || volType == BACKVOL) { + spaceAvail = 0; + goto _done; + } + + Name = volName; + OfflineMsg = offLineMsg; + MOTD = motd; + + do { + code = cm_ConnFromFID(fidp, userp, reqp, &connp); + if (code) continue; + + rxconnp = cm_GetRxConn(connp); + code = RXAFS_GetVolumeStatus(rxconnp, fidp->volume, + &volStat, &Name, &OfflineMsg, &MOTD); + rx_PutConnection(rxconnp); + + } while (cm_Analyze(connp, userp, reqp, fidp, 0, NULL, NULL, NULL, code)); + code = cm_MapRPCError(code, reqp); + if (code == 0) { + if (volStat.MaxQuota) { + freespace.QuadPart = 1024 * (afs_int64)min(volStat.MaxQuota - volStat.BlocksInUse, volStat.PartBlocksAvail); + } else { + freespace.QuadPart = 1024 * (afs_int64)volStat.PartBlocksAvail; + } + spaceAvail = LargeIntegerGreaterThanOrEqualTo(freespace, *sizep); + } + /* the rpc failed, assume there is space and we can fail it later. */ + + _done: + if (volp) + cm_PutVolume(volp); + + return spaceAvail; +} + /* set the file size, and make sure that all relevant buffers have been * truncated. Ensure that any partially truncated buffers have been zeroed * to the end of the buffer. @@ -2730,13 +2797,20 @@ long cm_SetLength(cm_scache_t *scp, osi_hyper_t *sizep, cm_user_t *userp, } else if (LargeIntegerGreaterThan(*sizep, scp->length)) { /* really extending the file */ - scp->length = *sizep; - scp->mask |= CM_SCACHEMASK_LENGTH; + /* Check to see if we have sufficient quota */ + if (cm_IsSpaceAvailable(&scp->fid, sizep, userp, reqp)) { + scp->length = *sizep; + scp->mask |= CM_SCACHEMASK_LENGTH; + } else { + code = CM_ERROR_SPACE; + goto syncopdone; + } } /* done successfully */ code = 0; + syncopdone: cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS | CM_SCACHESYNC_SETSTATUS | CM_SCACHESYNC_SETSIZE); diff --git a/src/WINNT/afsd/cm_vnodeops.h b/src/WINNT/afsd/cm_vnodeops.h index fd2275ad5..47e4fd446 100644 --- a/src/WINNT/afsd/cm_vnodeops.h +++ b/src/WINNT/afsd/cm_vnodeops.h @@ -242,6 +242,8 @@ extern cm_key_t cm_GenerateKey(afs_uint16 session_id, afs_offs_t process_id, afs extern int cm_KeyEquals(cm_key_t * k1, cm_key_t * k2, int flags); +extern int cm_IsSpaceAvailable(cm_fid_t * fidp, osi_hyper_t *sizep, cm_user_t *userp, cm_req_t *reqp); + #define MAX_SYMLINK_COUNT 64 /* make this big enough so that one buffer of dir pages won't overflow. We'll -- 2.39.5