diff --git a/fs/nfs/nfs_vfsops.c b/fs/nfs/nfs_vfsops.c index 924cde471d..0277e57ab3 100644 --- a/fs/nfs/nfs_vfsops.c +++ b/fs/nfs/nfs_vfsops.c @@ -97,6 +97,8 @@ # error "Length of cookie verify in fs_dirent_s is incorrect" #endif +#define CH_STAT_SIZE (1 << 7) + /**************************************************************************** * Private Data ****************************************************************************/ @@ -115,8 +117,9 @@ static void nfs_semgive(FAR struct nfsmount *nmp); static int nfs_filecreate(FAR struct nfsmount *nmp, FAR struct nfsnode *np, FAR const char *relpath, mode_t mode); -static int nfs_filetruncate(FAR struct nfsmount *nmp, - FAR struct nfsnode *np, off_t length); +static int nfs_filechstat(FAR struct nfsmount *nmp, + FAR struct nfsnode *np, + FAR const struct stat *buf, int flags); static int nfs_fileopen(FAR struct nfsmount *nmp, FAR struct nfsnode *np, FAR const char *relpath, int oflags, mode_t mode); @@ -131,6 +134,8 @@ static ssize_t nfs_write(FAR struct file *filep, FAR const char *buffer, static int nfs_dup(FAR const struct file *oldp, FAR struct file *newp); static int nfs_fsinfo(FAR struct nfsmount *nmp); static int nfs_fstat(FAR const struct file *filep, FAR struct stat *buf); +static int nfs_fchstat(FAR const struct file *filep, + FAR const struct stat *buf, int flags); static int nfs_truncate(FAR struct file *filep, off_t length); static int nfs_opendir(FAR struct inode *mountpt, FAR const char *relpath, FAR struct fs_dirent_s *dir); @@ -157,6 +162,8 @@ static int nfs_rename(FAR struct inode *mountpt, static mode_t nfs_stat_mode(unsigned int mode, unsigned int type); static int nfs_stat(FAR struct inode *mountpt, FAR const char *relpath, FAR struct stat *buf); +static int nfs_chstat(FAR struct inode *mountpt, FAR const char *relpath, + FAR const struct stat *buf, int flags); /**************************************************************************** * Public Data @@ -180,7 +187,7 @@ const struct mountpt_operations nfs_operations = NULL, /* sync */ nfs_dup, /* dup */ nfs_fstat, /* fstat */ - NULL, /* fchstat */ + nfs_fchstat, /* fchstat */ nfs_truncate, /* truncate */ nfs_opendir, /* opendir */ @@ -197,7 +204,7 @@ const struct mountpt_operations nfs_operations = nfs_rmdir, /* rmdir */ nfs_rename, /* rename */ nfs_stat, /* stat */ - NULL /* chstat */ + nfs_chstat /* chstat */ }; /**************************************************************************** @@ -378,24 +385,24 @@ static int nfs_filecreate(FAR struct nfsmount *nmp, FAR struct nfsnode *np, } /**************************************************************************** - * Name: nfs_filetruncate + * Name: nfs_filechstat * * Description: - * Truncate an open file to length. This is part of the file open logic. + * Change the status of an open file. This is part of the file open logic. * * Returned Value: * 0 on success; a negative errno value on failure. * ****************************************************************************/ -static int nfs_filetruncate(FAR struct nfsmount *nmp, - FAR struct nfsnode *np, off_t length) +static int nfs_filechstat(FAR struct nfsmount *nmp, FAR struct nfsnode *np, + FAR const struct stat *buf, int flags) { FAR uint32_t *ptr; int reqlen; int ret; - finfo("Truncating file\n"); + finfo("Changing file status\n"); /* Create the SETATTR RPC call arguments */ @@ -413,16 +420,83 @@ static int nfs_filetruncate(FAR struct nfsmount *nmp, /* Copy the variable-length attributes */ - *ptr++ = nfs_false; /* Don't change mode */ - *ptr++ = nfs_false; /* Don't change uid */ - *ptr++ = nfs_false; /* Don't change gid */ - *ptr++ = nfs_true; /* Use the following size */ - *ptr++ = txdr_unsigned(length >> 32); /* Truncate to the specified length */ - *ptr++ = txdr_unsigned(length); - *ptr++ = HTONL(NFSV3SATTRTIME_TOSERVER); /* Use the server's time */ - *ptr++ = HTONL(NFSV3SATTRTIME_TOSERVER); /* Use the server's time */ - *ptr++ = nfs_false; /* No guard value */ - reqlen += 9 * sizeof(uint32_t); + if (flags & CH_STAT_MODE) + { + *ptr++ = nfs_true; + *ptr++ = txdr_unsigned(buf->st_mode); + reqlen += 2 * sizeof(uint32_t); + } + else + { + *ptr++ = nfs_false; + reqlen += sizeof(uint32_t); + } + + if (flags & CH_STAT_UID) + { + *ptr++ = nfs_true; + *ptr++ = txdr_unsigned(buf->st_uid); + reqlen += 2 * sizeof(uint32_t); + } + else + { + *ptr++ = nfs_false; + reqlen += sizeof(uint32_t); + } + + if (flags & CH_STAT_GID) + { + *ptr++ = nfs_true; + *ptr++ = txdr_unsigned(buf->st_gid); + reqlen += 2 * sizeof(uint32_t); + } + else + { + *ptr++ = nfs_false; + reqlen += sizeof(uint32_t); + } + + if (flags & CH_STAT_SIZE) + { + *ptr++ = nfs_true; + txdr_hyper(buf->st_size, ptr); + ptr += 2; + reqlen += 3 * sizeof(uint32_t); + } + else + { + *ptr++ = nfs_false; + reqlen += sizeof(uint32_t); + } + + if (flags & CH_STAT_ATIME) + { + *ptr++ = nfs_true; + txdr_nfsv3time(&buf->st_atim, ptr); + ptr += 2; + reqlen += 3 * sizeof(uint32_t); + } + else + { + *ptr++ = nfs_false; + reqlen += sizeof(uint32_t); + } + + if (flags & CH_STAT_MTIME) + { + *ptr++ = nfs_true; + txdr_nfsv3time(&buf->st_mtim, ptr); + ptr += 2; + reqlen += 3 * sizeof(uint32_t); + } + else + { + *ptr++ = nfs_false; + reqlen += sizeof(uint32_t); + } + + *ptr++ = nfs_false; /* No guard value */ + reqlen += sizeof(uint32_t); /* Perform the SETATTR RPC */ @@ -549,11 +623,14 @@ static int nfs_fileopen(FAR struct nfsmount *nmp, FAR struct nfsnode *np, if ((oflags & (O_TRUNC | O_WRONLY)) == (O_TRUNC | O_WRONLY)) { + struct stat buf; + /* Truncate the file to zero length. I think we can do this with * the SETATTR call by setting the length to zero. */ - return nfs_filetruncate(nmp, np, 0); + buf.st_size = 0; + return nfs_filechstat(nmp, np, &buf, CH_STAT_SIZE); } return OK; @@ -1235,6 +1312,47 @@ static int nfs_fstat(FAR const struct file *filep, FAR struct stat *buf) return OK; } +/**************************************************************************** + * Name: nfs_fchstat + * + * Description: + * Change information about an open file associated with the file + * structure 'filep'. + * + ****************************************************************************/ + +static int nfs_fchstat(FAR const struct file *filep, + FAR const struct stat *buf, int flags) +{ + FAR struct nfsmount *nmp; + FAR struct nfsnode *np; + int ret; + + finfo("Buf %p\n", buf); + DEBUGASSERT(filep != NULL && buf != NULL); + + /* Recover our private data from the struct file instance */ + + DEBUGASSERT(filep->f_priv != NULL && filep->f_inode != NULL); + np = (FAR struct nfsnode *)filep->f_priv; + + nmp = (FAR struct nfsmount *)filep->f_inode->i_private; + DEBUGASSERT(nmp != NULL); + + ret = nfs_semtake(nmp); + if (ret < 0) + { + return ret; + } + + /* Change the file mode, owner, group and time. */ + + ret = nfs_filechstat(nmp, np, buf, flags); + + nfs_semgive(nmp); + return ret; +} + /**************************************************************************** * Name: nfs_truncate * @@ -1263,9 +1381,12 @@ static int nfs_truncate(FAR struct file *filep, off_t length) ret = nfs_semtake(nmp); if (ret >= 0) { + struct stat buf; + /* Then perform the SETATTR RPC to set the new file size */ - ret = nfs_filetruncate(nmp, np, length); + buf.st_size = length; + ret = nfs_filechstat(nmp, np, &buf, CH_STAT_SIZE); nfs_semgive(nmp); } @@ -2730,3 +2851,60 @@ errout_with_semaphore: nfs_semgive(nmp); return ret; } + +/**************************************************************************** + * Name: nfs_chstat + * + * Description: + * Change information about the file system object at 'relpath' + * + * Returned Value: + * 0 on success; a negated errno value on failure. + * + ****************************************************************************/ + +static int nfs_chstat(FAR struct inode *mountpt, FAR const char *relpath, + FAR const struct stat *buf, int flags) +{ + FAR struct nfsmount *nmp; + struct file_handle fhandle; + struct nfsnode np; + int ret; + + /* Sanity checks */ + + DEBUGASSERT(mountpt && mountpt->i_private); + + /* Get the mountpoint private data from the inode structure */ + + nmp = (FAR struct nfsmount *)mountpt->i_private; + DEBUGASSERT(nmp && buf); + + ret = nfs_semtake(nmp); + if (ret < 0) + { + return ret; + } + + /* Get the file handle of the requested node */ + + ret = nfs_findnode(nmp, relpath, &fhandle, NULL, NULL); + if (ret != OK) + { + ferr("ERROR: nfs_findnode failed: %d\n", ret); + goto errout_with_semaphore; + } + + /* Initialize the nfs node */ + + np.n_fhsize = (uint8_t)fhandle.length; + memcpy(&np.n_fhandle, &fhandle.handle, fhandle.length); + + /* Change the file mode, owner, group and time. */ + + ret = nfs_filechstat(nmp, &np, buf, flags); + +errout_with_semaphore: + nfs_semgive(nmp); + return ret; +}