diff --git a/fs/nfs/nfs_vfsops.c b/fs/nfs/nfs_vfsops.c index c78825fe88..0f948b5741 100644 --- a/fs/nfs/nfs_vfsops.c +++ b/fs/nfs/nfs_vfsops.c @@ -99,6 +99,8 @@ struct nfs_dirent * Private Function Prototypes ****************************************************************************/ +static int nfs_create(FAR struct nfsmount *nmp, struct nfsnode *np, + FAR const char *relpath, mode_t mode); static int nfs_open(FAR struct file *filep, const char *relpath, int oflags, mode_t mode); static ssize_t nfs_read(FAR struct file *filep, char *buffer, size_t buflen); @@ -172,58 +174,28 @@ const struct mountpt_operations nfs_operations = ****************************************************************************/ /**************************************************************************** - * Name: nfs_open + * Name: nfs_create * * Description: - * If oflags == O_CREAT it creates a file, if not it check to see if the - * type is ok and that deletion is not in progress. + * Create a file. This is part of the file open logic that is executed if + * the user asks to create a file. * * Returned Value: - * 0 on success; a negated errno value on failure. + * 0 on success; a positive errno value on failure. * ****************************************************************************/ -static int nfs_open(FAR struct file *filep, FAR const char *relpath, - int oflags, mode_t mode) +static int nfs_create(FAR struct nfsmount *nmp, struct nfsnode *np, + FAR const char *relpath, mode_t mode) { - struct inode *in; //struct nfs_fattr vap; struct nfsv3_sattr sp; - struct nfsmount *nmp; - struct nfsnode *np; struct CREATE3args create; struct rpc_reply_create resok; int error = 0; - /* Sanity checks */ - - DEBUGASSERT(filep->f_inode != NULL); - - /* Get the mountpoint inode reference from the file structure and the - * mountpoint private data from the inode structure - */ - - in = filep->f_inode; - nmp = (struct nfsmount*)in->i_private; - np = (struct nfsnode*)filep->f_priv; - - DEBUGASSERT(nmp != NULL); - - /* Check if the mount is still healthy */ - - nfs_semtake(nmp); - error = nfs_checkmount(nmp); - if (error != 0) + do { - goto errout_with_semaphore; - } - - if (oflags == O_CREAT) - { - /* Sanity checks */ - - DEBUGASSERT(filep->f_priv == NULL); -again: nfsstats.rpccnt[NFSPROC_CREATE]++; memset(&sp, 0, sizeof(struct nfsv3_sattr)); //memset(&vap, 0, sizeof(struct nfs_fattr)); @@ -241,6 +213,7 @@ again: memset(&create, 0, sizeof(struct CREATE3args)); memset(&resok, 0, sizeof(struct rpc_reply_create)); memcpy(&create.how, &sp, sizeof(struct nfsv3_sattr)); +#warning "BUG HERE: np has not yet been initialized" create.where.dir.length = txdr_unsigned(np->n_fhsize); memcpy(&create.where.dir.handle, &np->n_fhp, sizeof(nfsfh_t)); create.where.length = txdr_unsigned(64); @@ -248,91 +221,205 @@ again: error = nfs_request(nmp, NFSPROC_CREATE, (FAR const void *)&create, (void *)&resok, sizeof(struct rpc_reply_create)); - if (!error) - { - /* Create an instance of the file private data to describe the opened - * file. - */ - - np = (struct nfsnode *)kzalloc(sizeof(struct nfsnode)); - if (!np) - { - fdbg("ERROR: Failed to allocate private data\n"); - error = -ENOMEM; - goto errout_with_semaphore; - } - - /* Initialize the file private data (only need to initialize - * non-zero elements) - */ - - // np->nfsv3_type = fxdr_unsigned(uint32_t, resok.attributes.fa_type); - - /* The full path exists -- but is the final component a file - * or a directory? - */ - - if (np->nfsv3_type == NFDIR) - { - /* It is a directory */ - - error = EISDIR; - fdbg("ERROR: '%s' is a directory\n", relpath); - goto errout_with_semaphore; - } - - np->n_open = true; - memcpy(&np->n_fhp, &resok.create.fshandle.handle, sizeof(nfsfh_t)); - np->n_size = fxdr_hyper(&resok.create.attributes.fa3_size); - memcpy(&resok.create.attributes, &np->n_fattr, sizeof(struct nfs_fattr)); - fxdr_nfsv3time(&resok.create.attributes.fa3_mtime, &np->n_mtime) - np->n_ctime = fxdr_hyper(&resok.create.attributes.fa3_ctime); - - /* Attach the private date to the struct file instance */ - - filep->f_priv = np; - - /* Then insert the new instance into the mountpoint structure. - * It needs to be there (1) to handle error conditions that effect - * all files, and (2) to inform the umount logic that we are busy - * (but a simple reference count could have done that). - */ - - np->n_next = nmp->nm_head; - nmp->nm_head = np->n_next; - error = 0; - } - else - { - if (error == EOPNOTSUPP) - { - goto again; - } - } - - np->n_flag |= NMODIFIED; } - else + while (error == EOPNOTSUPP); + + if (error == 0) { - if (np->nfsv3_type != NFREG) + //np->nfsv3_type = fxdr_unsigned(uint32_t, resok.attributes.fa_type); + + np->n_open = true; + memcpy(&np->n_fhp, &resok.create.fshandle.handle, sizeof(nfsfh_t)); + np->n_size = fxdr_hyper(&resok.create.attributes.fa3_size); + memcpy(&resok.create.attributes, &np->n_fattr, sizeof(struct nfs_fattr)); + fxdr_nfsv3time(&resok.create.attributes.fa3_mtime, &np->n_mtime) + np->n_ctime = fxdr_hyper(&resok.create.attributes.fa3_ctime); + } + + return error; +} + + /**************************************************************************** + * Name: nfs_open + * + * Description: + * If oflags == O_CREAT it creates a file, if not it check to see if the + * type is ok and that deletion is not in progress. + * + * Returned Value: + * 0 on success; a negated errno value on failure. + * + ****************************************************************************/ + +static int nfs_open(FAR struct file *filep, FAR const char *relpath, + int oflags, mode_t mode) +{ + struct stat buf; + struct inode *in; +//struct nfs_fattr vap; + struct nfsmount *nmp; + struct nfsnode *np; + bool readonly; + int error = 0; + + /* Sanity checks */ + + DEBUGASSERT(filep->f_inode != NULL); + + /* Get the mountpoint inode reference from the file structure and the + * mountpoint private data from the inode structure + */ + + in = filep->f_inode; + nmp = (struct nfsmount*)in->i_private; + + DEBUGASSERT(nmp != NULL); + + /* Pre-allocate the file private data to describe the opened file. */ + + np = (struct nfsnode *)kzalloc(sizeof(struct nfsnode)); + if (!np) + { + fdbg("ERROR: Failed to allocate private data\n"); + return -ENOMEM; + } + + /* Check if the mount is still healthy */ + + nfs_semtake(nmp); + error = nfs_checkmount(nmp); + if (error != 0) + { + goto errout_with_semaphore; + } + + /* First check if the file already exists */ + + error = nfs_getfsinfo(nmp, relpath, &buf); + if (error == 0) + { + /* Check if the file is a directory */ + + if (S_ISDIR(buf.st_mode)) { - fdbg("ERROR: open eacces typ=%d\n", np->nfsv3_type); + /* Exit with EISDIR if we attempt to open an directory */ + + fdbg("ERROR: Path is a directory\n"); + error = EISDIR; + goto errout_with_semaphore; + } + + /* Check if the caller has sufficient privileges to open the file */ + + readonly = ((buf.st_mode & (S_IWOTH|S_IWGRP|S_IXUSR)) == 0); + if (((oflags & O_WRONLY) != 0) && readonly) + { + fdbg("ERROR: File is read-only\n"); error = EACCES; goto errout_with_semaphore; } - if (np->n_flag & NMODIFIED) + /* It would be an error if we are asked to create it exclusively */ + + if ((oflags & (O_CREAT|O_EXCL)) == (O_CREAT|O_EXCL)) { - if (np->nfsv3_type == NFDIR) - { - np->n_direofoffset = 0; - } + /* Already exists -- can't create it exclusively */ + + fdbg("ERROR: File exists\n"); + error = EEXIST; + goto errout_with_semaphore; } + + /* If O_TRUNC is specified and the file is opened for writing, + * then truncate the file. This operation requires that the file is + * writable, but we have already checked that. O_TRUNC without write + * access is ignored. + */ + + if ((oflags & (O_TRUNC|O_WRONLY)) == (O_TRUNC|O_WRONLY)) + { + /* Truncate the file to zero length */ + + fvdbg("Truncating file\n"); +#warning "Missing logic" + } + + /* Initialize the file private data from the FSINFO return data */ + /* NOTE: This will require some re-structuring */ +#if 0 + np->n_open = true; + memcpy(&np->n_fhp, &resok.fsinfo.fshandle.handle, sizeof(nfsfh_t)); + np->n_size = fxdr_hyper(&resok.fsinfo.attributes.fa3_size); + memcpy(&resok.fsinfo.attributes, &np->n_fattr, sizeof(struct nfs_fattr)); + fxdr_nfsv3time(&resok.fsinfo.attributes.fa3_mtime, &np->n_mtime) + np->n_ctime = fxdr_hyper(&resok.fsinfo.attributes.fa3_ctime); +#else +#warning "Missing logic" + fdbg("ERROR: Logic to open an existing file is not fully implemented"); + errno = ENOSYS; /* Just fail for now */ + goto errout_with_semaphore; +#endif + /* Fall through to finish the file open operation */ } + /* An error occurred while getting the file status */ + + else + { + /* Check if the stat failed because the file does not exist. */ +#warning "Missing logic" + + /* If the file does not exist then check if we were asked to create + * the file. If the O_CREAT bit is set in the oflags then we should + * create the file if it does not exist. + */ + + if ((oflags & O_CREAT) == 0) + { + /* Return ENOENT if the file does not exist and we were not asked + * to create it. + */ + + fdbg("ERROR: File does not exist\n"); + error = ENOENT; + goto errout_with_semaphore; + } + + /* Create the file */ + + error = nfs_create(nmp, np, relpath, mode); + if (error != 0) + { + fdbg("ERROR: nfs_create failed: %d\n", error); + goto errout_with_semaphore; + } + + /* Fall through to finish the file open operation */ + } + + /* Initialize the file private data (only need to initialize + * non-zero elements) + */ + + /* Attach the private data to the struct file instance */ + + filep->f_priv = np; + + /* Then insert the new instance into the mountpoint structure. + * It needs to be there (1) to handle error conditions that effect + * all files, and (2) to inform the umount logic that we are busy + * (but a simple reference count could have done that). + */ + + np->n_next = nmp->nm_head; + np->n_flag |= NMODIFIED; + nmp->nm_head = np->n_next; + /* For open/close consistency. */ NFS_INVALIDATE_ATTRCACHE(np); + nfs_semgive(nmp); + return OK; errout_with_semaphore: kfree(np); diff --git a/fs/nfs/rpc_clnt.c b/fs/nfs/rpc_clnt.c index 243bbb0f04..a78bc6d78e 100644 --- a/fs/nfs/rpc_clnt.c +++ b/fs/nfs/rpc_clnt.c @@ -241,10 +241,12 @@ rpcclnt_send(struct socket *so, struct sockaddr *nam, int procid, int prog, void *call, struct rpctask *rep) { struct sockaddr *sendnam; + ssize_t nbytes; int error = ESRCH; #ifdef CONFIG_NFS_TCPIP int soflags; #endif + int length; int flags; if (rep != NULL) @@ -290,34 +292,29 @@ rpcclnt_send(struct socket *so, struct sockaddr *nam, int procid, int prog, flags = 0; } + /* Get the length of the call messsage */ + + error = 0; if (prog == PMAPPROG) { - if (procid == PMAPPROC_GETPORT) + if (procid == PMAPPROC_GETPORT || procid == PMAPPROC_UNSET) { - struct rpc_call_pmap *callmsg = (struct rpc_call_pmap *)call; - error = psock_sendto(so, callmsg, sizeof(*callmsg), flags, - sendnam, sizeof(*sendnam)); + length = sizeof(struct rpc_call_pmap); } - else if (procid == PMAPPROC_UNSET) + else { - struct rpc_call_pmap *callmsg = (struct rpc_call_pmap *)call; - error = psock_sendto(so, callmsg, sizeof(*callmsg), flags, - sendnam, sizeof(*sendnam)); + error = EINVAL; } } else if (prog == RPCPROG_MNT) { - if (procid == RPCMNT_UMOUNT) + if (procid == RPCMNT_UMOUNT || procid == RPCMNT_MOUNT) { - struct rpc_call_mount *callmsg = (struct rpc_call_mount *)call; - error = psock_sendto(so, callmsg, sizeof(*callmsg), flags, - sendnam, sizeof(*sendnam)); + length = sizeof(struct rpc_call_mount); } - else if (procid == RPCMNT_MOUNT) + else { - struct rpc_call_mount *callmsg = (struct rpc_call_mount *)call; - error = psock_sendto(so, callmsg, sizeof(*callmsg), flags, - sendnam, sizeof(*sendnam)); + error = EINVAL; } } else if (prog == NFS_PROG) @@ -325,127 +322,98 @@ rpcclnt_send(struct socket *so, struct sockaddr *nam, int procid, int prog, switch (procid) { case NFSPROC_CREATE: - { - struct rpc_call_create *callmsg = (struct rpc_call_create *)call; - error = psock_sendto(so, callmsg, sizeof(*callmsg), flags, - sendnam, sizeof(*sendnam)); - } + length = sizeof(struct rpc_call_create); break; case NFSPROC_READ: - { - struct rpc_call_read *callmsg = (struct rpc_call_read *)call; - error = psock_sendto(so, callmsg, sizeof(*callmsg), flags, - sendnam, sizeof(*sendnam)); - } + length = sizeof(struct rpc_call_read); break; case NFSPROC_WRITE: - { - struct rpc_call_write *callmsg = (struct rpc_call_write *)call; - error = psock_sendto(so, callmsg, sizeof(*callmsg), flags, - sendnam, sizeof(*sendnam)); - } + length = sizeof(struct rpc_call_write); break; case NFSPROC_READDIR: - { - struct rpc_call_readdir *callmsg = (struct rpc_call_readdir *)call; - error = psock_sendto(so, callmsg, sizeof(*callmsg), flags, - sendnam, sizeof(*sendnam)); - } + length = sizeof(struct rpc_call_readdir); break; case NFSPROC_FSSTAT: - { - struct rpc_call_fs *callmsg = (struct rpc_call_fs *)call; - error = psock_sendto(so, callmsg, sizeof(*callmsg), flags, - sendnam, sizeof(*sendnam)); - } + length = sizeof(struct rpc_call_fs); break; case NFSPROC_GETATTR: - { - struct rpc_call_fs *callmsg = (struct rpc_call_fs *)call; - error = psock_sendto(so, callmsg, sizeof(*callmsg), flags, - sendnam, sizeof(*sendnam)); - } + length = sizeof(struct rpc_call_fs); break; case NFSPROC_REMOVE: - { - struct rpc_call_remove *callmsg = (struct rpc_call_remove *)call; - error = psock_sendto(so, callmsg, sizeof(*callmsg), flags, - sendnam, sizeof(*sendnam)); - } + length = sizeof(struct rpc_call_remove); break; case NFSPROC_MKDIR: - { - struct rpc_call_mkdir *callmsg = (struct rpc_call_mkdir *)call; - error = psock_sendto(so, callmsg, sizeof(*callmsg), flags, - sendnam, sizeof(*sendnam)); - } + length = sizeof(struct rpc_call_mkdir); break; case NFSPROC_RMDIR: - { - struct rpc_call_rmdir *callmsg = (struct rpc_call_rmdir *)call; - error = psock_sendto(so, callmsg, sizeof(*callmsg), flags, - sendnam, sizeof(*sendnam)); - } + length = sizeof(struct rpc_call_rmdir); break; case NFSPROC_RENAME: - { - struct rpc_call_rename *callmsg = (struct rpc_call_rename *)call; - error = psock_sendto(so, callmsg, sizeof(*callmsg), flags, - sendnam, sizeof(*sendnam)); - } + length = sizeof(struct rpc_call_rename); break; case NFSPROC_FSINFO: - { - struct rpc_call_fs *callmsg = (struct rpc_call_fs *)call; - error = psock_sendto(so, callmsg, sizeof(*callmsg), flags, - sendnam, sizeof(*sendnam)); - } + length = sizeof(struct rpc_call_fs); break; default: + error = EINVAL; break; } } - - if (error < 0) - { - if (rep != NULL) - { - fdbg("rpc send error %d for service %s\n", error, - rep->r_rpcclnt->rc_prog->prog_name); - - /* Deal with errors for the client side. */ - - if (rep->r_flags & TASK_SOFTTERM) - { - error = EINTR; - } - else - { - rep->r_flags |= TASK_MUSTRESEND; - } - } - else - { - fdbg("rpc service send error %d\n", error); - } - - return error; - } else { - return 0; + error = EINVAL; } + + /* Send the call message */ + + if (error == 0) + { + /* On success, psock_sendto returns the number of bytes sent; + * On failure, it returns -1 with the specific error in errno. + */ + + nbytes = psock_sendto(so, call, length, flags, + sendnam, sizeof(struct sockaddr)); + if (nbytes < 0) + { + /* psock_sendto failed, Sample the error value (subsequent + * calls can change the errno value! + */ + + error = errno; + fdbg("ERROR: psock_sendto failed: %d\n", error); + + if (rep != NULL) + { + fdbg("rpc send error %d for service %s\n", error, + rep->r_rpcclnt->rc_prog->prog_name); + + /* Deal with errors for the client side. */ + + if (rep->r_flags & TASK_SOFTTERM) + { + error = EINTR; + } + else + { + rep->r_flags |= TASK_MUSTRESEND; + } + } + } + } + + return error; } /* Receive a Sun RPC Request/Reply. For SOCK_DGRAM, the work is all