/* * Lock a parent directory following the VFS locking protocol. */ int fh_lock_parent(struct svc_fh *parent_fh, struct dentry *dchild) { int nfserr = 0; fh_lock(parent_fh); /* * Make sure the parent->child relationship still holds, * and that the child is still hashed. */ if (dchild->d_parent != parent_fh->fh_dentry) goto out_not_parent; if (list_empty(&dchild->d_hash)) goto out_not_hashed; out: return nfserr; out_not_parent: printk(KERN_WARNING "fh_lock_parent: %s/%s parent changed\n", dchild->d_parent->d_name.name, dchild->d_name.name); goto out_unlock; out_not_hashed: printk(KERN_WARNING "fh_lock_parent: %s/%s unhashed\n", dchild->d_parent->d_name.name, dchild->d_name.name); out_unlock: nfserr = nfserr_noent; fh_unlock(parent_fh); goto out; }
/* * Set the Access and/or Default ACL of a file. */ static __be32 nfsacld_proc_setacl(struct svc_rqst * rqstp, struct nfsd3_setaclargs *argp, struct nfsd_attrstat *resp) { struct inode *inode; svc_fh *fh; __be32 nfserr = 0; int error; dprintk("nfsd: SETACL(2acl) %s\n", SVCFH_fmt(&argp->fh)); fh = fh_copy(&resp->fh, &argp->fh); nfserr = fh_verify(rqstp, &resp->fh, 0, NFSD_MAY_SATTR); if (nfserr) goto out; inode = d_inode(fh->fh_dentry); error = fh_want_write(fh); if (error) goto out_errno; fh_lock(fh); error = set_posix_acl(inode, ACL_TYPE_ACCESS, argp->acl_access); if (error) goto out_drop_lock; error = set_posix_acl(inode, ACL_TYPE_DEFAULT, argp->acl_default); if (error) goto out_drop_lock; fh_unlock(fh); fh_drop_write(fh); nfserr = fh_getattr(fh, &resp->stat); out: /* argp->acl_{access,default} may have been allocated in nfssvc_decode_setaclargs. */ posix_acl_release(argp->acl_access); posix_acl_release(argp->acl_default); return nfserr; out_drop_lock: fh_unlock(fh); fh_drop_write(fh); out_errno: nfserr = nfserrno(error); goto out; }
/* Opens the file designated by file handle FH for reading as a data file. Providing fh_inline_file() for FH designates the "inline file", that is, data included inline in the command file between BEGIN FILE and END FILE. Returns a reader if successful, or a null pointer otherwise. */ struct dfm_reader * dfm_open_reader (struct file_handle *fh, struct lexer *lexer) { struct dfm_reader *r; struct fh_lock *lock; /* TRANSLATORS: this fragment will be interpolated into messages in fh_lock() that identify types of files. */ lock = fh_lock (fh, FH_REF_FILE | FH_REF_INLINE, N_("data file"), FH_ACC_READ, false); if (lock == NULL) return NULL; r = fh_lock_get_aux (lock); if (r != NULL) return r; r = xmalloc (sizeof *r); r->fh = fh_ref (fh); r->lock = lock; r->lexer = lexer; ds_init_empty (&r->line); ds_init_empty (&r->scratch); r->flags = DFM_ADVANCE; r->eof_cnt = 0; r->block_left = 0; if (fh_get_referent (fh) != FH_REF_INLINE) { struct stat s; r->line_number = 0; r->file = fn_open (fh_get_file_name (fh), "rb"); if (r->file == NULL) { msg (ME, _("Could not open `%s' for reading as a data file: %s."), fh_get_file_name (r->fh), strerror (errno)); fh_unlock (r->lock); fh_unref (fh); free (r); return NULL; } r->file_size = fstat (fileno (r->file), &s) == 0 ? s.st_size : -1; } else r->file_size = -1; fh_lock_set_aux (lock, r); return r; }
/* * Truncate a file. * The calling routines must make sure to update the ctime * field and call notify_change. * * XXX Nobody calls this thing? -DaveM * N.B. After this call fhp needs an fh_put */ int nfsd_truncate(struct svc_rqst *rqstp, struct svc_fh *fhp, unsigned long size) { struct dentry *dentry; struct inode *inode; struct iattr newattrs; int err; kernel_cap_t saved_cap; err = fh_verify(rqstp, fhp, S_IFREG, MAY_WRITE | MAY_TRUNC); if (err) goto out; dentry = fhp->fh_dentry; inode = dentry->d_inode; err = get_write_access(inode); if (err) goto out_nfserr; /* Things look sane, lock and do it. */ fh_lock(fhp); DQUOT_INIT(inode); newattrs.ia_size = size; newattrs.ia_valid = ATTR_SIZE | ATTR_CTIME; if (current->fsuid != 0) { saved_cap = current->cap_effective; cap_clear(current->cap_effective); } err = notify_change(dentry, &newattrs); if (current->fsuid != 0) current->cap_effective = saved_cap; if (!err) { vmtruncate(inode, size); if (inode->i_op && inode->i_op->truncate) inode->i_op->truncate(inode); } put_write_access(inode); DQUOT_DROP(inode); fh_unlock(fhp); out_nfserr: if (err) err = nfserrno(-err); out: return err; }
static void test_nfsfh(void){ dev_t dev=0; u32 unfs=0, u32ptr[2]; ino_t ino=0; struct svc_fh A1; int i=20; u32_to_dev_t((__u32)unfs); ino_t_to_u32(ino); u32_to_ino_t((__u32)unfs); mk_fsid_v0(u32ptr, dev, ino); mk_fsid_v1(u32ptr, unfs); SVCFH_fmt(&A1); fh_init(&A1,i); fh_lock(&A1); fh_unlock(&A1); printk("finished nfsfh test\n"); }
/* * Set the Access and/or Default ACL of a file. */ static __be32 nfsd3_proc_setacl(struct svc_rqst * rqstp, struct nfsd3_setaclargs *argp, struct nfsd3_attrstat *resp) { struct inode *inode; svc_fh *fh; __be32 nfserr = 0; int error; fh = fh_copy(&resp->fh, &argp->fh); nfserr = fh_verify(rqstp, &resp->fh, 0, NFSD_MAY_SATTR); if (nfserr) goto out; inode = d_inode(fh->fh_dentry); error = fh_want_write(fh); if (error) goto out_errno; fh_lock(fh); error = set_posix_acl(inode, ACL_TYPE_ACCESS, argp->acl_access); if (error) goto out_drop_lock; error = set_posix_acl(inode, ACL_TYPE_DEFAULT, argp->acl_default); out_drop_lock: fh_unlock(fh); fh_drop_write(fh); out_errno: nfserr = nfserrno(error); out: /* argp->acl_{access,default} may have been allocated in nfs3svc_decode_setaclargs. */ posix_acl_release(argp->acl_access); posix_acl_release(argp->acl_default); RETURN_STATUS(nfserr); }
/* Opens FH, which must have referent type FH_REF_DATASET, and returns a dataset_writer for it, or a null pointer on failure. Cases stored in the dataset_writer will be expected to be drawn from DICTIONARY. */ struct casewriter * dataset_writer_open (struct file_handle *fh, const struct dictionary *dictionary) { struct dataset_writer *writer; struct casewriter *casewriter; struct fh_lock *lock; /* Get exclusive write access to handle. */ /* TRANSLATORS: this fragment will be interpolated into messages in fh_lock() that identify types of files. */ lock = fh_lock (fh, FH_REF_DATASET, N_("dataset"), FH_ACC_WRITE, true); if (lock == NULL) return NULL; /* Create writer. */ writer = xmalloc (sizeof *writer); writer->lock = lock; writer->ds = fh_get_dataset (fh); writer->dict = dict_clone (dictionary); dict_delete_scratch_vars (writer->dict); if (dict_count_values (writer->dict, 0) < dict_get_next_value_idx (writer->dict)) { writer->compactor = case_map_to_compact_dict (writer->dict, 0); dict_compact_values (writer->dict); } else writer->compactor = NULL; writer->subwriter = autopaging_writer_create (dict_get_proto (writer->dict)); casewriter = casewriter_create (dict_get_proto (writer->dict), &dataset_writer_casewriter_class, writer); taint_propagate (casewriter_get_taint (writer->subwriter), casewriter_get_taint (casewriter)); return casewriter; }
/* * Set various file attributes. * N.B. After this call fhp needs an fh_put */ __be32 nfsd_setattr(struct svc_rqst *rqstp, struct svc_fh *fhp, struct iattr *iap, int check_guard, time_t guardtime) { struct dentry *dentry; struct inode *inode; int accmode = NFSD_MAY_SATTR; int ftype = 0; __be32 err; int host_err; int size_change = 0; if (iap->ia_valid & (ATTR_ATIME | ATTR_MTIME | ATTR_SIZE)) accmode |= NFSD_MAY_WRITE|NFSD_MAY_OWNER_OVERRIDE; if (iap->ia_valid & ATTR_SIZE) ftype = S_IFREG; /* Get inode */ err = fh_verify(rqstp, fhp, ftype, accmode); if (err) goto out; dentry = fhp->fh_dentry; inode = dentry->d_inode; /* Ignore any mode updates on symlinks */ if (S_ISLNK(inode->i_mode)) iap->ia_valid &= ~ATTR_MODE; if (!iap->ia_valid) goto out; /* * NFSv2 does not differentiate between "set-[ac]time-to-now" * which only requires access, and "set-[ac]time-to-X" which * requires ownership. * So if it looks like it might be "set both to the same time which * is close to now", and if inode_change_ok fails, then we * convert to "set to now" instead of "set to explicit time" * * We only call inode_change_ok as the last test as technically * it is not an interface that we should be using. It is only * valid if the filesystem does not define it's own i_op->setattr. */ #define BOTH_TIME_SET (ATTR_ATIME_SET | ATTR_MTIME_SET) #define MAX_TOUCH_TIME_ERROR (30*60) if ((iap->ia_valid & BOTH_TIME_SET) == BOTH_TIME_SET && iap->ia_mtime.tv_sec == iap->ia_atime.tv_sec) { /* * Looks probable. * * Now just make sure time is in the right ballpark. * Solaris, at least, doesn't seem to care what the time * request is. We require it be within 30 minutes of now. */ time_t delta = iap->ia_atime.tv_sec - get_seconds(); if (delta < 0) delta = -delta; if (delta < MAX_TOUCH_TIME_ERROR && inode_change_ok(inode, iap) != 0) { /* * Turn off ATTR_[AM]TIME_SET but leave ATTR_[AM]TIME. * This will cause notify_change to set these times * to "now" */ iap->ia_valid &= ~BOTH_TIME_SET; } } /* * The size case is special. * It changes the file as well as the attributes. */ if (iap->ia_valid & ATTR_SIZE) { if (iap->ia_size < inode->i_size) { err = nfsd_permission(rqstp, fhp->fh_export, dentry, NFSD_MAY_TRUNC|NFSD_MAY_OWNER_OVERRIDE); if (err) goto out; } /* * If we are changing the size of the file, then * we need to break all leases. */ host_err = break_lease(inode, FMODE_WRITE | O_NONBLOCK); if (host_err == -EWOULDBLOCK) host_err = -ETIMEDOUT; if (host_err) /* ENOMEM or EWOULDBLOCK */ goto out_nfserr; host_err = get_write_access(inode); if (host_err) goto out_nfserr; size_change = 1; host_err = locks_verify_truncate(inode, NULL, iap->ia_size); if (host_err) { put_write_access(inode); goto out_nfserr; } DQUOT_INIT(inode); } /* sanitize the mode change */ if (iap->ia_valid & ATTR_MODE) { iap->ia_mode &= S_IALLUGO; iap->ia_mode |= (inode->i_mode & ~S_IALLUGO); } /* Revoke setuid/setgid on chown */ if (((iap->ia_valid & ATTR_UID) && iap->ia_uid != inode->i_uid) || ((iap->ia_valid & ATTR_GID) && iap->ia_gid != inode->i_gid)) { iap->ia_valid |= ATTR_KILL_PRIV; if (iap->ia_valid & ATTR_MODE) { /* we're setting mode too, just clear the s*id bits */ iap->ia_mode &= ~S_ISUID; if (iap->ia_mode & S_IXGRP) iap->ia_mode &= ~S_ISGID; } else { /* set ATTR_KILL_* bits and let VFS handle it */ iap->ia_valid |= (ATTR_KILL_SUID | ATTR_KILL_SGID); } } /* Change the attributes. */ iap->ia_valid |= ATTR_CTIME; err = nfserr_notsync; if (!check_guard || guardtime == inode->i_ctime.tv_sec) { fh_lock(fhp); host_err = notify_change(dentry, iap); err = nfserrno(host_err); fh_unlock(fhp); } if (size_change) put_write_access(inode); if (!err) if (EX_ISSYNC(fhp->fh_export)) write_inode_now(inode, 1); out: return err; out_nfserr: err = nfserrno(host_err); goto out; }
__be32 nfsd_lookup_dentry(struct svc_rqst *rqstp, struct svc_fh *fhp, const char *name, unsigned int len, struct svc_export **exp_ret, struct dentry **dentry_ret) { struct svc_export *exp; struct dentry *dparent; struct dentry *dentry; __be32 err; int host_err; dprintk("nfsd: nfsd_lookup(fh %s, %.*s)\n", SVCFH_fmt(fhp), len,name); /* Obtain dentry and export. */ err = fh_verify(rqstp, fhp, S_IFDIR, NFSD_MAY_EXEC); if (err) return err; dparent = fhp->fh_dentry; exp = fhp->fh_export; exp_get(exp); /* Lookup the name, but don't follow links */ if (isdotent(name, len)) { if (len==1) dentry = dget(dparent); else if (dparent != exp->ex_path.dentry) dentry = dget_parent(dparent); else if (!EX_NOHIDE(exp)) dentry = dget(dparent); /* .. == . just like at / */ else { /* checking mountpoint crossing is very different when stepping up */ struct svc_export *exp2 = NULL; struct dentry *dp; struct vfsmount *mnt = mntget(exp->ex_path.mnt); dentry = dget(dparent); while(dentry == mnt->mnt_root && follow_up(&mnt, &dentry)) ; dp = dget_parent(dentry); dput(dentry); dentry = dp; exp2 = rqst_exp_parent(rqstp, mnt, dentry); if (PTR_ERR(exp2) == -ENOENT) { dput(dentry); dentry = dget(dparent); } else if (IS_ERR(exp2)) { host_err = PTR_ERR(exp2); dput(dentry); mntput(mnt); goto out_nfserr; } else { exp_put(exp); exp = exp2; } mntput(mnt); } } else { fh_lock(fhp); dentry = lookup_one_len(name, dparent, len); host_err = PTR_ERR(dentry); if (IS_ERR(dentry)) goto out_nfserr; /* * check if we have crossed a mount point ... */ if (d_mountpoint(dentry)) { if ((host_err = nfsd_cross_mnt(rqstp, &dentry, &exp))) { dput(dentry); goto out_nfserr; } } } *dentry_ret = dentry; *exp_ret = exp; return 0; out_nfserr: exp_put(exp); return nfserrno(host_err); }
/* * Look up one component of a pathname. * N.B. After this call _both_ fhp and resfh need an fh_put * * If the lookup would cross a mountpoint, and the mounted filesystem * is exported to the client with NFSEXP_CROSSMNT, then the lookup is * accepted as it stands and the mounted directory is * returned. Otherwise the covered directory is returned. * NOTE: this mountpoint crossing is not supported properly by all * clients and is explicitly disallowed for NFSv3 * NeilBrown <*****@*****.**> */ int nfsd_lookup(struct svc_rqst *rqstp, struct svc_fh *fhp, const char *name, int len, struct svc_fh *resfh) { struct svc_export *exp; struct dentry *dparent; struct dentry *dentry; int err; dprintk("nfsd: nfsd_lookup(fh %s, %.*s)\n", SVCFH_fmt(fhp), len,name); /* Obtain dentry and export. */ err = fh_verify(rqstp, fhp, S_IFDIR, MAY_EXEC); if (err) goto out; dparent = fhp->fh_dentry; exp = fhp->fh_export; err = nfserr_acces; /* Lookup the name, but don't follow links */ if (isdotent(name, len)) { if (len==1) dentry = dget(dparent); else { /* must be ".." */ /* checking mountpoint crossing is very different when stepping up */ if (dparent == exp->ex_dentry) { if (!EX_CROSSMNT(exp)) dentry = dget(dparent); /* .. == . just like at / */ else { struct svc_export *exp2 = NULL; struct dentry *dp; struct vfsmount *mnt = mntget(exp->ex_mnt); dentry = dget(dparent); while(follow_up(&mnt, &dentry)) ; dp = dget(dentry->d_parent); dput(dentry); dentry = dp; for ( ; exp2 == NULL && dp->d_parent != dp; dp=dp->d_parent) exp2 = exp_get(exp->ex_client, dp->d_inode->i_dev, dp->d_inode->i_ino); if (exp2==NULL) { dput(dentry); dentry = dget(dparent); } else { exp = exp2; } mntput(mnt); } } else dentry = dget(dparent->d_parent); } } else { fh_lock(fhp); dentry = lookup_one_len(name, dparent, len); err = PTR_ERR(dentry); if (IS_ERR(dentry)) goto out_nfserr; /* * check if we have crossed a mount point ... */ if (d_mountpoint(dentry)) { struct svc_export *exp2 = NULL; struct vfsmount *mnt = mntget(exp->ex_mnt); struct dentry *mounts = dget(dentry); while (follow_down(&mnt,&mounts)&&d_mountpoint(mounts)) ; exp2 = exp_get(rqstp->rq_client, mounts->d_inode->i_dev, mounts->d_inode->i_ino); if (exp2 && EX_CROSSMNT(exp2)) { /* successfully crossed mount point */ exp = exp2; dput(dentry); dentry = mounts; } else dput(mounts); mntput(mnt); } } /* * Note: we compose the file handle now, but as the * dentry may be negative, it may need to be updated. */ err = fh_compose(resfh, exp, dentry, fhp); if (!err && !dentry->d_inode) err = nfserr_noent; out: return err; out_nfserr: err = nfserrno(err); goto out; }
/* * Set various file attributes. * N.B. After this call fhp needs an fh_put */ int nfsd_setattr(struct svc_rqst *rqstp, struct svc_fh *fhp, struct iattr *iap, int check_guard, time_t guardtime) { struct dentry *dentry; struct inode *inode; int accmode = MAY_SATTR; int ftype = 0; int imode; int err; int size_change = 0; if (iap->ia_valid & (ATTR_ATIME | ATTR_MTIME | ATTR_SIZE)) accmode |= MAY_WRITE|MAY_OWNER_OVERRIDE; if (iap->ia_valid & ATTR_SIZE) ftype = S_IFREG; /* Get inode */ err = fh_verify(rqstp, fhp, ftype, accmode); if (err || !iap->ia_valid) goto out; dentry = fhp->fh_dentry; inode = dentry->d_inode; /* NFSv2 does not differentiate between "set-[ac]time-to-now" * which only requires access, and "set-[ac]time-to-X" which * requires ownership. * So if it looks like it might be "set both to the same time which * is close to now", and if inode_change_ok fails, then we * convert to "set to now" instead of "set to explicit time" * * We only call inode_change_ok as the last test as technically * it is not an interface that we should be using. It is only * valid if the filesystem does not define it's own i_op->setattr. */ #define BOTH_TIME_SET (ATTR_ATIME_SET | ATTR_MTIME_SET) #define MAX_TOUCH_TIME_ERROR (30*60) if ((iap->ia_valid & BOTH_TIME_SET) == BOTH_TIME_SET && iap->ia_mtime == iap->ia_atime ) { /* Looks probable. Now just make sure time is in the right ballpark. * Solaris, at least, doesn't seem to care what the time request is. * We require it be within 30 minutes of now. */ time_t delta = iap->ia_atime - CURRENT_TIME; if (delta<0) delta = -delta; if (delta < MAX_TOUCH_TIME_ERROR && inode_change_ok(inode, iap) != 0) { /* turn off ATTR_[AM]TIME_SET but leave ATTR_[AM]TIME * this will cause notify_change to set these times to "now" */ iap->ia_valid &= ~BOTH_TIME_SET; } } /* The size case is special. It changes the file as well as the attributes. */ if (iap->ia_valid & ATTR_SIZE) { if (iap->ia_size < inode->i_size) { err = nfsd_permission(fhp->fh_export, dentry, MAY_TRUNC|MAY_OWNER_OVERRIDE); if (err) goto out; } /* * If we are changing the size of the file, then * we need to break all leases. */ err = get_lease(inode, FMODE_WRITE); if (err) goto out_nfserr; err = get_write_access(inode); if (err) goto out_nfserr; err = locks_verify_truncate(inode, NULL, iap->ia_size); if (err) { put_write_access(inode); goto out_nfserr; } DQUOT_INIT(inode); } imode = inode->i_mode; if (iap->ia_valid & ATTR_MODE) { iap->ia_mode &= S_IALLUGO; imode = iap->ia_mode |= (imode & ~S_IALLUGO); } /* Revoke setuid/setgid bit on chown/chgrp */ if ((iap->ia_valid & ATTR_UID) && (imode & S_ISUID) && iap->ia_uid != inode->i_uid) { iap->ia_valid |= ATTR_MODE; iap->ia_mode = imode &= ~S_ISUID; } if ((iap->ia_valid & ATTR_GID) && (imode & S_ISGID) && iap->ia_gid != inode->i_gid) { iap->ia_valid |= ATTR_MODE; iap->ia_mode = imode &= ~S_ISGID; } /* Change the attributes. */ iap->ia_valid |= ATTR_CTIME; if (iap->ia_valid & ATTR_SIZE) { fh_lock(fhp); size_change = 1; } err = nfserr_notsync; if (!check_guard || guardtime == inode->i_ctime) { err = notify_change(dentry, iap); err = nfserrno(err); } if (size_change) { fh_unlock(fhp); put_write_access(inode); } if (!err) if (EX_ISSYNC(fhp->fh_export)) write_inode_now(inode, 1); out: return err; out_nfserr: err = nfserrno(err); goto out; }
/* * Look up one component of a pathname. * N.B. After this call _both_ fhp and resfh need an fh_put * * If the lookup would cross a mountpoint, and the mounted filesystem * is exported to the client with NFSEXP_NOHIDE, then the lookup is * accepted as it stands and the mounted directory is * returned. Otherwise the covered directory is returned. * NOTE: this mountpoint crossing is not supported properly by all * clients and is explicitly disallowed for NFSv3 * NeilBrown <*****@*****.**> */ int nfsd_lookup(struct svc_rqst *rqstp, struct svc_fh *fhp, const char *name, int len, struct svc_fh *resfh) { struct svc_export *exp; struct dentry *dparent; struct dentry *dentry; int err; dprintk("nfsd: nfsd_lookup(fh %s, %.*s)\n", SVCFH_fmt(fhp), len,name); /* Obtain dentry and export. */ err = fh_verify(rqstp, fhp, S_IFDIR, MAY_EXEC); if (err) return err; dparent = fhp->fh_dentry; exp = fhp->fh_export; exp_get(exp); err = nfserr_acces; /* Lookup the name, but don't follow links */ if (isdotent(name, len)) { if (len==1) dentry = dget(dparent); else if (dparent != exp->ex_dentry) { dentry = dget_parent(dparent); } else if (!EX_NOHIDE(exp)) dentry = dget(dparent); /* .. == . just like at / */ else { /* checking mountpoint crossing is very different when stepping up */ struct svc_export *exp2 = NULL; struct dentry *dp; struct vfsmount *mnt = mntget(exp->ex_mnt); dentry = dget(dparent); while(dentry == mnt->mnt_root && follow_up(&mnt, &dentry)) ; dp = dget_parent(dentry); dput(dentry); dentry = dp; exp2 = exp_parent(exp->ex_client, mnt, dentry, &rqstp->rq_chandle); if (IS_ERR(exp2)) { err = PTR_ERR(exp2); dput(dentry); mntput(mnt); goto out_nfserr; } if (!exp2) { dput(dentry); dentry = dget(dparent); } else { exp_put(exp); exp = exp2; } mntput(mnt); } } else { fh_lock(fhp); dentry = lookup_one_len(name, dparent, len); err = PTR_ERR(dentry); if (IS_ERR(dentry)) goto out_nfserr; /* * check if we have crossed a mount point ... */ if (d_mountpoint(dentry)) { if ((err = nfsd_cross_mnt(rqstp, &dentry, &exp))) { dput(dentry); goto out_nfserr; } } } /* * Note: we compose the file handle now, but as the * dentry may be negative, it may need to be updated. */ err = fh_compose(resfh, exp, dentry, fhp); if (!err && !dentry->d_inode) err = nfserr_noent; dput(dentry); out: exp_put(exp); return err; out_nfserr: err = nfserrno(err); goto out; }
/* * Write data to a file. * The stable flag requests synchronous writes. * N.B. After this call fhp needs an fh_put */ int nfsd_write(struct svc_rqst *rqstp, struct svc_fh *fhp, loff_t offset, char *buf, unsigned long cnt, int stable) { struct svc_export *exp; struct file file; struct dentry *dentry; struct inode *inode; mm_segment_t oldfs; int err = 0; #ifdef CONFIG_QUOTA uid_t saved_euid; #endif if (!cnt) goto out; err = nfsd_open(rqstp, fhp, S_IFREG, OPEN_WRITE, &file); if (err) goto out; err = nfserr_perm; if (!file.f_op->write) goto out_close; dentry = file.f_dentry; inode = dentry->d_inode; exp = fhp->fh_export; /* * Request sync writes if * - the sync export option has been set, or * - the client requested O_SYNC behavior (NFSv3 feature). * When gathered writes have been configured for this volume, * flushing the data to disk is handled separately below. */ if ((stable || (stable = EX_ISSYNC(exp))) && !EX_WGATHER(exp)) file.f_flags |= O_SYNC; fh_lock(fhp); /* lock inode */ file.f_pos = offset; /* set write offset */ /* Write the data. */ oldfs = get_fs(); set_fs(KERNEL_DS); #ifdef CONFIG_QUOTA /* This is for disk quota. */ saved_euid = current->euid; current->euid = current->fsuid; err = file.f_op->write(&file, buf, cnt, &file.f_pos); current->euid = saved_euid; #else err = file.f_op->write(&file, buf, cnt, &file.f_pos); #endif set_fs(oldfs); /* clear setuid/setgid flag after write */ if (err >= 0 && (inode->i_mode & (S_ISUID | S_ISGID))) { struct iattr ia; kernel_cap_t saved_cap; ia.ia_valid = ATTR_MODE; ia.ia_mode = inode->i_mode & ~(S_ISUID | S_ISGID); if (current->fsuid != 0) { saved_cap = current->cap_effective; cap_clear(current->cap_effective); } notify_change(dentry, &ia); if (current->fsuid != 0) current->cap_effective = saved_cap; } fh_unlock(fhp); /* unlock inode */ if (err >= 0 && stable) { static unsigned long last_ino = 0; static kdev_t last_dev = NODEV; /* * Gathered writes: If another process is currently * writing to the file, there's a high chance * this is another nfsd (triggered by a bulk write * from a client's biod). Rather than syncing the * file with each write request, we sleep for 10 msec. * * I don't know if this roughly approximates * C. Juszak's idea of gathered writes, but it's a * nice and simple solution (IMHO), and it seems to * work:-) */ if (EX_WGATHER(exp) && (inode->i_writecount > 1 || (last_ino == inode->i_ino && last_dev == inode->i_dev))) { #if 0 interruptible_sleep_on_timeout(&inode->i_wait, 10 * HZ / 1000); #else dprintk("nfsd: write defer %d\n", current->pid); schedule_timeout((HZ+99)/100); dprintk("nfsd: write resume %d\n", current->pid); #endif } if (inode->i_state & I_DIRTY) { dprintk("nfsd: write sync %d\n", current->pid); nfsd_sync(inode, &file); write_inode_now(inode); } wake_up(&inode->i_wait); last_ino = inode->i_ino; last_dev = inode->i_dev; } dprintk("nfsd: write complete\n"); if (err >= 0) err = 0; else err = nfserrno(-err); out_close: nfsd_close(&file); out: return err; }
/* Opens the CSV file designated by file handle FH for writing cases from dictionary DICT according to the given OPTS. No reference to D is retained, so it may be modified or destroyed at will after this function returns. */ struct casewriter * csv_writer_open (struct file_handle *fh, const struct dictionary *dict, const struct csv_writer_options *opts) { struct csv_writer *w; int i; /* Create and initialize writer. */ w = xmalloc (sizeof *w); w->fh = fh_ref (fh); w->lock = NULL; w->file = NULL; w->rf = NULL; w->opts = *opts; w->encoding = xstrdup (dict_get_encoding (dict)); w->n_csv_vars = dict_get_var_cnt (dict); w->csv_vars = xnmalloc (w->n_csv_vars, sizeof *w->csv_vars); for (i = 0; i < w->n_csv_vars; i++) { const struct variable *var = dict_get_var (dict, i); struct csv_var *cv = &w->csv_vars[i]; cv->width = var_get_width (var); cv->case_index = var_get_case_index (var); cv->format = *var_get_print_format (var); if (opts->recode_user_missing) mv_copy (&cv->missing, var_get_missing_values (var)); else mv_init (&cv->missing, cv->width); if (opts->use_value_labels) cv->val_labs = val_labs_clone (var_get_value_labels (var)); else cv->val_labs = NULL; } /* Open file handle as an exclusive writer. */ /* TRANSLATORS: this fragment will be interpolated into messages in fh_lock() that identify types of files. */ w->lock = fh_lock (fh, FH_REF_FILE, N_("CSV file"), FH_ACC_WRITE, true); if (w->lock == NULL) goto error; /* Create the file on disk. */ w->rf = replace_file_start (fh_get_file_name (fh), "w", 0666, &w->file, NULL); if (w->rf == NULL) { msg (ME, _("Error opening `%s' for writing as a system file: %s."), fh_get_file_name (fh), strerror (errno)); goto error; } if (opts->include_var_names) write_var_names (w, dict); if (write_error (w)) goto error; return casewriter_create (dict_get_proto (dict), &csv_file_casewriter_class, w); error: close_writer (w); return NULL; }