diff options
author | Ralf Baechle <ralf@linux-mips.org> | 1999-02-15 02:15:32 +0000 |
---|---|---|
committer | Ralf Baechle <ralf@linux-mips.org> | 1999-02-15 02:15:32 +0000 |
commit | 86464aed71025541805e7b1515541aee89879e33 (patch) | |
tree | e01a457a4912a8553bc65524aa3125d51f29f810 /fs/nfsd/vfs.c | |
parent | 88f99939ecc6a95a79614574cb7d95ffccfc3466 (diff) |
Merge with Linux 2.2.1.
Diffstat (limited to 'fs/nfsd/vfs.c')
-rw-r--r-- | fs/nfsd/vfs.c | 205 |
1 files changed, 156 insertions, 49 deletions
diff --git a/fs/nfsd/vfs.c b/fs/nfsd/vfs.c index 72d67a13a..109ed75ec 100644 --- a/fs/nfsd/vfs.c +++ b/fs/nfsd/vfs.c @@ -73,9 +73,10 @@ struct raparms { p_rawin; }; -#define FILECACHE_MAX (2 * NFSD_MAXSERVS) -static struct raparms raparms[FILECACHE_MAX]; -static struct raparms * raparm_cache = 0; +int nfsd_nservers = 0; +#define FILECACHE_MAX (2 * nfsd_nservers) +static struct raparms * raparml = NULL; +static struct raparms * raparm_cache = NULL; /* * Lock a parent directory following the VFS locking protocol. @@ -148,16 +149,18 @@ nfsd_lookup(struct svc_rqst *rqstp, struct svc_fh *fhp, const char *name, dprintk("nfsd: nfsd_lookup(fh %p, %s)\n", SVCFH_DENTRY(fhp), name); /* Obtain dentry and export. */ - err = fh_verify(rqstp, fhp, S_IFDIR, MAY_NOP); + err = fh_verify(rqstp, fhp, S_IFDIR, MAY_EXEC); if (err) goto out; dparent = fhp->fh_dentry; exp = fhp->fh_export; +#if 0 err = nfsd_permission(exp, dparent, MAY_EXEC); if (err) goto out; +#endif err = nfserr_noent; if (fs_off_limits(dparent->d_sb)) goto out; @@ -232,13 +235,17 @@ nfsd_setattr(struct svc_rqst *rqstp, struct svc_fh *fhp, struct iattr *iap) dentry = fhp->fh_dentry; inode = dentry->d_inode; + err = inode_change_ok(inode, iap); + if (err) + goto out_nfserr; + /* The size case is special... */ if (iap->ia_valid & ATTR_SIZE) { if (!S_ISREG(inode->i_mode)) printk("nfsd_setattr: size change??\n"); if (iap->ia_size < inode->i_size) { err = nfsd_permission(fhp->fh_export, dentry, MAY_TRUNC); - if (err != 0) + if (err) goto out; } err = get_write_access(inode); @@ -275,9 +282,17 @@ printk("nfsd_setattr: size change??\n"); /* Change the attributes. */ if (iap->ia_valid) { + kernel_cap_t saved_cap = 0; + iap->ia_valid |= ATTR_CTIME; iap->ia_ctime = CURRENT_TIME; + if (current->fsuid != 0) { + saved_cap = current->cap_effective; + cap_clear(current->cap_effective); + } err = notify_change(dentry, iap); + if (current->fsuid != 0) + current->cap_effective = saved_cap; if (err) goto out_nfserr; if (EX_ISSYNC(fhp->fh_export)) @@ -493,6 +508,9 @@ nfsd_write(struct svc_rqst *rqstp, struct svc_fh *fhp, loff_t offset, struct inode *inode; mm_segment_t oldfs; int err = 0; +#ifdef CONFIG_QUOTA + uid_t saved_euid; +#endif if (!cnt) goto out; @@ -522,16 +540,31 @@ nfsd_write(struct svc_rqst *rqstp, struct svc_fh *fhp, loff_t 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 */ @@ -661,7 +694,14 @@ nfsd_create(struct svc_rqst *rqstp, struct svc_fh *fhp, break; case S_IFCHR: case S_IFBLK: + /* The client is _NOT_ required to do security enforcement */ + if(!capable(CAP_SYS_ADMIN)) + { + err = -EPERM; + goto out; + } case S_IFIFO: + case S_IFSOCK: opfunc = dirp->i_op->mknod; break; } @@ -719,6 +759,7 @@ nfsd_truncate(struct svc_rqst *rqstp, struct svc_fh *fhp, unsigned long size) 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) @@ -736,7 +777,13 @@ nfsd_truncate(struct svc_rqst *rqstp, struct svc_fh *fhp, unsigned long size) 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) @@ -941,12 +988,26 @@ out_nfserr: } /* + * We need to do a check-parent every time + * after we have locked the parent - to verify + * that the parent is still our parent and + * that we are still hashed onto it.. + * + * This is requied in case two processes race + * on removing (or moving) the same entry: the + * parent lock will serialize them, but the + * other process will be too late.. + */ +#define check_parent(dir, dentry) \ + ((dir) == (dentry)->d_parent->d_inode && !list_empty(&dentry->d_hash)) + +/* * This follows the model of double_lock() in the VFS. */ static inline void nfsd_double_down(struct semaphore *s1, struct semaphore *s2) { if (s1 != s2) { - if ((unsigned long) s1 < (unsigned long) s2) { + if ((unsigned long) s1 > (unsigned long) s2) { struct semaphore *tmp = s1; s1 = s2; s2 = tmp; @@ -996,18 +1057,15 @@ nfsd_rename(struct svc_rqst *rqstp, struct svc_fh *ffhp, char *fname, int flen, (tlen == 1 || (tlen == 2 && tname[1] == '.')))) goto out; - err = -EXDEV; - if (fdir->i_dev != tdir->i_dev) - goto out_nfserr; - err = -EPERM; - if (!fdir->i_op || !fdir->i_op->rename) - goto out_nfserr; - odentry = lookup_dentry(fname, dget(fdentry), 0); err = PTR_ERR(odentry); if (IS_ERR(odentry)) goto out_nfserr; + err = -ENOENT; + if (!odentry->d_inode) + goto out_dput_old; + ndentry = lookup_dentry(tname, dget(tdentry), 0); err = PTR_ERR(ndentry); if (IS_ERR(ndentry)) @@ -1017,15 +1075,18 @@ nfsd_rename(struct svc_rqst *rqstp, struct svc_fh *ffhp, char *fname, int flen, * Lock the parent directories. */ nfsd_double_down(&tdir->i_sem, &fdir->i_sem); - /* N.B. check for parent changes after locking?? */ - - DQUOT_INIT(fdir); - DQUOT_INIT(tdir); - err = fdir->i_op->rename(fdir, odentry, tdir, ndentry); - if (!err && EX_ISSYNC(tfhp->fh_export)) { - write_inode_now(fdir); - write_inode_now(tdir); - } + err = -ENOENT; + /* GAM3 check for parent changes after locking. */ + if (check_parent(fdir, odentry) && + check_parent(tdir, ndentry)) { + + err = vfs_rename(fdir, odentry, tdir, ndentry); + if (!err && EX_ISSYNC(tfhp->fh_export)) { + write_inode_now(fdir); + write_inode_now(tdir); + } + } else + dprintk("nfsd: Caught race in nfsd_rename"); DQUOT_DROP(fdir); DQUOT_DROP(tdir); @@ -1072,29 +1133,48 @@ nfsd_unlink(struct svc_rqst *rqstp, struct svc_fh *fhp, int type, if (IS_ERR(rdentry)) goto out_nfserr; - /* - * FIXME!! - * - * This should do a double-lock on both rdentry and the parent - */ - err = fh_lock_parent(fhp, rdentry); - if (err) + if (!rdentry->d_inode) { + dput(rdentry); + err = nfserr_noent; goto out; + } + + if (type != S_IFDIR) { + /* It's UNLINK */ + err = fh_lock_parent(fhp, rdentry); + if (err) + goto out; + + err = vfs_unlink(dirp, rdentry); + + DQUOT_DROP(dirp); + fh_unlock(fhp); + + dput(rdentry); - DQUOT_INIT(dirp); - if (type == S_IFDIR) { - err = -ENOTDIR; - if (dirp->i_op && dirp->i_op->rmdir) - err = dirp->i_op->rmdir(dirp, rdentry); } else { - err = -EPERM; - if (dirp->i_op && dirp->i_op->unlink) - err = dirp->i_op->unlink(dirp, rdentry); + /* It's RMDIR */ + /* See comments in fs/namei.c:do_rmdir */ + rdentry->d_count++; + nfsd_double_down(&dirp->i_sem, &rdentry->d_inode->i_sem); + if (!fhp->fh_pre_mtime) + fhp->fh_pre_mtime = dirp->i_mtime; + fhp->fh_locked = 1; + + err = -ENOENT; + if (check_parent(dirp, rdentry)) + err = vfs_rmdir(dirp, rdentry); + + rdentry->d_count--; + DQUOT_DROP(dirp); + if (!fhp->fh_post_version) + fhp->fh_post_version = dirp->i_version; + fhp->fh_locked = 0; + nfsd_double_up(&dirp->i_sem, &rdentry->d_inode->i_sem); + + dput(rdentry); } - DQUOT_DROP(dirp); - fh_unlock(fhp); - dput(rdentry); if (err) goto out_nfserr; if (EX_ISSYNC(fhp->fh_export)) @@ -1230,11 +1310,11 @@ nfsd_permission(struct svc_export *exp, struct dentry *dentry, int acc) { struct inode *inode = dentry->d_inode; int err; + kernel_cap_t saved_cap; if (acc == MAY_NOP) return 0; - - /* +#if 0 dprintk("nfsd: permission 0x%x%s%s%s%s%s mode 0%o%s%s%s\n", acc, (acc & MAY_READ)? " read" : "", @@ -1248,8 +1328,7 @@ nfsd_permission(struct svc_export *exp, struct dentry *dentry, int acc) IS_RDONLY(inode)? " ro" : ""); dprintk(" owner %d/%d user %d/%d\n", inode->i_uid, inode->i_gid, current->fsuid, current->fsgid); - */ - +#endif #ifndef CONFIG_NFSD_SUN if (dentry->d_mounts != dentry) { return nfserr_perm; @@ -1279,15 +1358,33 @@ nfsd_permission(struct svc_export *exp, struct dentry *dentry, int acc) if (inode->i_uid == current->fsuid /* && !(acc & MAY_TRUNC) */) return 0; + if (current->fsuid != 0) { + saved_cap = current->cap_effective; + cap_clear(current->cap_effective); + } + err = permission(inode, acc & (MAY_READ|MAY_WRITE|MAY_EXEC)); /* Allow read access to binaries even when mode 111 */ - if (err == -EPERM && S_ISREG(inode->i_mode) && acc == MAY_READ) + if (err == -EACCES && S_ISREG(inode->i_mode) && acc == MAY_READ) err = permission(inode, MAY_EXEC); + if (current->fsuid != 0) + current->cap_effective = saved_cap; + return err? nfserrno(-err) : 0; } +void +nfsd_racache_shutdown(void) +{ + if (!raparm_cache) + return; + dprintk("nfsd: freeing %d readahead buffers.\n", FILECACHE_MAX); + kfree(raparml); + nfsd_nservers = 0; + raparm_cache = raparml = NULL; +} /* * Initialize readahead param cache */ @@ -1298,9 +1395,19 @@ nfsd_racache_init(void) if (raparm_cache) return; - memset(raparms, 0, sizeof(raparms)); - for (i = 0; i < FILECACHE_MAX - 1; i++) { - raparms[i].p_next = raparms + i + 1; + raparml = kmalloc(sizeof(struct raparms) * FILECACHE_MAX, GFP_KERNEL); + + if (raparml != NULL) { + dprintk("nfsd: allocating %d readahead buffers.\n", + FILECACHE_MAX); + memset(raparml, 0, sizeof(struct raparms) * FILECACHE_MAX); + for (i = 0; i < FILECACHE_MAX - 1; i++) { + raparml[i].p_next = raparml + i + 1; + } + raparm_cache = raparml; + } else { + printk(KERN_WARNING + "nfsd: Could not allocate memory read-ahead cache.\n"); + nfsd_nservers = 0; } - raparm_cache = raparms; } |