Commit 93adc1e3 authored by NeilBrown's avatar NeilBrown Committed by Chuck Lever

NFSD: set attributes when creating symlinks

The NFS protocol includes attributes when creating symlinks.
Linux does store attributes for symlinks and allows them to be set,
though they are not used for permission checking.

NFSD currently doesn't set standard (struct iattr) attributes when
creating symlinks, but for NFSv4 it does set ACLs and security labels.
This is inconsistent.

To improve consistency, pass the provided attributes into nfsd_symlink()
and call nfsd_create_setattr() to set them.

NOTE: this results in a behaviour change for all NFS versions when the
client sends non-default attributes with a SYMLINK request. With the
Linux client, the only attributes are:
	attr.ia_mode = S_IFLNK | S_IRWXUGO;
	attr.ia_valid = ATTR_MODE;
so the final outcome will be unchanged. Other clients might sent
different attributes, and if they did they probably expect them to be
honoured.

We ignore any error from nfsd_create_setattr().  It isn't really clear
what should be done if a file is successfully created, but the
attributes cannot be set.  NFS doesn't allow partial success to be
reported.  Reporting failure is probably more misleading than reporting
success, so the status is ignored.
Signed-off-by: default avatarNeilBrown <neilb@suse.de>
Signed-off-by: default avatarChuck Lever <chuck.lever@oracle.com>
parent 7fe2a71d
...@@ -397,6 +397,9 @@ nfsd3_proc_symlink(struct svc_rqst *rqstp) ...@@ -397,6 +397,9 @@ nfsd3_proc_symlink(struct svc_rqst *rqstp)
{ {
struct nfsd3_symlinkargs *argp = rqstp->rq_argp; struct nfsd3_symlinkargs *argp = rqstp->rq_argp;
struct nfsd3_diropres *resp = rqstp->rq_resp; struct nfsd3_diropres *resp = rqstp->rq_resp;
struct nfsd_attrs attrs = {
.na_iattr = &argp->attrs,
};
if (argp->tlen == 0) { if (argp->tlen == 0) {
resp->status = nfserr_inval; resp->status = nfserr_inval;
...@@ -423,7 +426,7 @@ nfsd3_proc_symlink(struct svc_rqst *rqstp) ...@@ -423,7 +426,7 @@ nfsd3_proc_symlink(struct svc_rqst *rqstp)
fh_copy(&resp->dirfh, &argp->ffh); fh_copy(&resp->dirfh, &argp->ffh);
fh_init(&resp->fh, NFS3_FHSIZE); fh_init(&resp->fh, NFS3_FHSIZE);
resp->status = nfsd_symlink(rqstp, &resp->dirfh, argp->fname, resp->status = nfsd_symlink(rqstp, &resp->dirfh, argp->fname,
argp->flen, argp->tname, &resp->fh); argp->flen, argp->tname, &attrs, &resp->fh);
kfree(argp->tname); kfree(argp->tname);
out: out:
return rpc_success; return rpc_success;
......
...@@ -813,7 +813,7 @@ nfsd4_create(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, ...@@ -813,7 +813,7 @@ nfsd4_create(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
case NF4LNK: case NF4LNK:
status = nfsd_symlink(rqstp, &cstate->current_fh, status = nfsd_symlink(rqstp, &cstate->current_fh,
create->cr_name, create->cr_namelen, create->cr_name, create->cr_namelen,
create->cr_data, &resfh); create->cr_data, &attrs, &resfh);
break; break;
case NF4BLK: case NF4BLK:
......
...@@ -478,6 +478,9 @@ nfsd_proc_symlink(struct svc_rqst *rqstp) ...@@ -478,6 +478,9 @@ nfsd_proc_symlink(struct svc_rqst *rqstp)
{ {
struct nfsd_symlinkargs *argp = rqstp->rq_argp; struct nfsd_symlinkargs *argp = rqstp->rq_argp;
struct nfsd_stat *resp = rqstp->rq_resp; struct nfsd_stat *resp = rqstp->rq_resp;
struct nfsd_attrs attrs = {
.na_iattr = &argp->attrs,
};
struct svc_fh newfh; struct svc_fh newfh;
if (argp->tlen > NFS_MAXPATHLEN) { if (argp->tlen > NFS_MAXPATHLEN) {
...@@ -499,7 +502,7 @@ nfsd_proc_symlink(struct svc_rqst *rqstp) ...@@ -499,7 +502,7 @@ nfsd_proc_symlink(struct svc_rqst *rqstp)
fh_init(&newfh, NFS_FHSIZE); fh_init(&newfh, NFS_FHSIZE);
resp->status = nfsd_symlink(rqstp, &argp->ffh, argp->fname, argp->flen, resp->status = nfsd_symlink(rqstp, &argp->ffh, argp->fname, argp->flen,
argp->tname, &newfh); argp->tname, &attrs, &newfh);
kfree(argp->tname); kfree(argp->tname);
fh_put(&argp->ffh); fh_put(&argp->ffh);
......
...@@ -1445,14 +1445,24 @@ nfsd_readlink(struct svc_rqst *rqstp, struct svc_fh *fhp, char *buf, int *lenp) ...@@ -1445,14 +1445,24 @@ nfsd_readlink(struct svc_rqst *rqstp, struct svc_fh *fhp, char *buf, int *lenp)
return 0; return 0;
} }
/* /**
* Create a symlink and look up its inode * nfsd_symlink - Create a symlink and look up its inode
* @rqstp: RPC transaction being executed
* @fhp: NFS filehandle of parent directory
* @fname: filename of the new symlink
* @flen: length of @fname
* @path: content of the new symlink (NUL-terminated)
* @attrs: requested attributes of new object
* @resfhp: NFS filehandle of new object
*
* N.B. After this call _both_ fhp and resfhp need an fh_put * N.B. After this call _both_ fhp and resfhp need an fh_put
*
* Returns nfs_ok on success, or an nfsstat in network byte order.
*/ */
__be32 __be32
nfsd_symlink(struct svc_rqst *rqstp, struct svc_fh *fhp, nfsd_symlink(struct svc_rqst *rqstp, struct svc_fh *fhp,
char *fname, int flen, char *fname, int flen,
char *path, char *path, struct nfsd_attrs *attrs,
struct svc_fh *resfhp) struct svc_fh *resfhp)
{ {
struct dentry *dentry, *dnew; struct dentry *dentry, *dnew;
...@@ -1483,13 +1493,14 @@ nfsd_symlink(struct svc_rqst *rqstp, struct svc_fh *fhp, ...@@ -1483,13 +1493,14 @@ nfsd_symlink(struct svc_rqst *rqstp, struct svc_fh *fhp,
host_err = vfs_symlink(&init_user_ns, d_inode(dentry), dnew, path); host_err = vfs_symlink(&init_user_ns, d_inode(dentry), dnew, path);
err = nfserrno(host_err); err = nfserrno(host_err);
cerr = fh_compose(resfhp, fhp->fh_export, dnew, fhp);
if (!err)
nfsd_create_setattr(rqstp, fhp, resfhp, attrs);
fh_unlock(fhp); fh_unlock(fhp);
if (!err) if (!err)
err = nfserrno(commit_metadata(fhp)); err = nfserrno(commit_metadata(fhp));
fh_drop_write(fhp); fh_drop_write(fhp);
cerr = fh_compose(resfhp, fhp->fh_export, dnew, fhp);
dput(dnew); dput(dnew);
if (err==0) err = cerr; if (err==0) err = cerr;
out: out:
......
...@@ -115,6 +115,7 @@ __be32 nfsd_readlink(struct svc_rqst *, struct svc_fh *, ...@@ -115,6 +115,7 @@ __be32 nfsd_readlink(struct svc_rqst *, struct svc_fh *,
char *, int *); char *, int *);
__be32 nfsd_symlink(struct svc_rqst *, struct svc_fh *, __be32 nfsd_symlink(struct svc_rqst *, struct svc_fh *,
char *name, int len, char *path, char *name, int len, char *path,
struct nfsd_attrs *attrs,
struct svc_fh *res); struct svc_fh *res);
__be32 nfsd_link(struct svc_rqst *, struct svc_fh *, __be32 nfsd_link(struct svc_rqst *, struct svc_fh *,
char *, int, struct svc_fh *); char *, int, struct svc_fh *);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment