--- linux.orig/drivers/net/e1000/e1000_main.c~e1000-e1000_suspend-defined-but-not-used 2006-06-07 16:53:11.000000000 -0700 +++ linux/drivers/net/e1000/e1000_main.c 2006-06-07 16:53:11.000000000 -0700 @@ -4464,6 +4464,12 @@ adapter->config_space = NULL; return; } +#else +static inline int +e1000_pci_save_state(struct e1000_adapter *adapter) +{ + return 0; +} #endif /* CONFIG_PM */ static int @@ -4480,13 +4486,11 @@ if (netif_running(netdev)) e1000_down(adapter); -#ifdef CONFIG_PM /* Implement our own version of pci_save_state(pdev) because pci- * express adapters have 256-byte config spaces. */ retval = e1000_pci_save_state(adapter); if (retval) return retval; -#endif status = E1000_READ_REG(&adapter->hw, STATUS); if (status & E1000_STATUS_LU) --- linux.orig/fs/file.c~D8-actually-add-flags 2006-06-07 16:53:29.000000000 -0700 +++ linux/fs/file.c 2006-06-07 16:53:29.000000000 -0700 @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include --- linux.orig/fs/file_table.c~elevate-writers-opens-part1 2006-06-07 16:53:20.000000000 -0700 +++ linux/fs/file_table.c 2006-06-07 16:53:20.000000000 -0700 @@ -180,8 +180,11 @@ if (unlikely(inode->i_cdev != NULL)) cdev_put(inode->i_cdev); fops_put(file->f_op); - if (file->f_mode & FMODE_WRITE) + if (file->f_mode & FMODE_WRITE) { put_write_access(inode); + if(!special_file(inode->i_mode)) + mnt_drop_write(mnt); + } file_kill(file); file->f_dentry = NULL; file->f_vfsmnt = NULL; --- linux.orig/fs/inode.c~D8-actually-add-flags 2006-06-07 16:53:29.000000000 -0700 +++ linux/fs/inode.c 2006-06-07 16:53:29.000000000 -0700 @@ -1185,7 +1185,7 @@ struct inode *inode = dentry->d_inode; struct timespec now; - if (IS_RDONLY(inode)) + if (IS_RDONLY(inode) || (mnt->mnt_flags & MNT_RDONLY)) return; if ((inode->i_flags & S_NOATIME) || --- linux.orig/fs/namei.c~rmdir 2006-06-07 16:53:12.000000000 -0700 +++ linux/fs/namei.c 2006-06-07 16:53:26.000000000 -0700 @@ -1511,8 +1511,17 @@ return -EACCES; flag &= ~O_TRUNC; - } else if (IS_RDONLY(inode) && (flag & FMODE_WRITE)) - return -EROFS; + } else if (flag & FMODE_WRITE) { + /* + * effectively: !special_file() + * balanced by __fput() + */ + error = mnt_want_write(nd->mnt); + if (error) + return error; + if (IS_RDONLY(inode)) + return -EROFS; + } /* * An append-only file must be opened in append mode for writing. */ @@ -1642,10 +1651,24 @@ if (!path.dentry->d_inode) { if (!IS_POSIXACL(dir->d_inode)) mode &= ~current->fs->umask; - error = vfs_create(dir->d_inode, path.dentry, mode, nd); + /* + * this serves dual roles, it makes sure there is no + * r/o mount, and keeps the write count for what is + * the newly created file + */ + error = mnt_want_write(nd->mnt); + if (!error) + error = vfs_create(dir->d_inode, path.dentry, mode, nd); mutex_unlock(&dir->d_inode->i_mutex); dput(nd->dentry); nd->dentry = path.dentry; + /* + * Unconditionally drop the write access because + * the acc_mode=0 set below will keep may_open() + * from ever failing if there was a r/o mount + * between here and there + */ + mnt_drop_write(nd->mnt); if (error) goto exit; /* Don't check for write permission, don't truncate */ @@ -1829,26 +1852,40 @@ if (!IS_POSIXACL(nd.dentry->d_inode)) mode &= ~current->fs->umask; - if (!IS_ERR(dentry)) { - switch (mode & S_IFMT) { - case 0: case S_IFREG: - error = vfs_create(nd.dentry->d_inode,dentry,mode,&nd); - break; - case S_IFCHR: case S_IFBLK: - error = vfs_mknod(nd.dentry->d_inode,dentry,mode, - new_decode_dev(dev)); - break; - case S_IFIFO: case S_IFSOCK: - error = vfs_mknod(nd.dentry->d_inode,dentry,mode,0); + if (IS_ERR(dentry)) + goto out_unlock; + error = mnt_want_write(nd.mnt); + if (error) + goto out_dput; + switch (mode & S_IFMT) { + case 0: case S_IFREG: + error = vfs_create(nd.dentry->d_inode,dentry,mode,&nd); + break; + case S_IFCHR: case S_IFBLK: + error = mnt_want_write(nd.mnt); + if (error) break; - case S_IFDIR: - error = -EPERM; + error = vfs_mknod(nd.dentry->d_inode,dentry,mode, + new_decode_dev(dev)); + mnt_drop_write(nd.mnt); + break; + case S_IFIFO: case S_IFSOCK: + error = mnt_want_write(nd.mnt); + if (error) break; - default: - error = -EINVAL; - } - dput(dentry); + error = vfs_mknod(nd.dentry->d_inode,dentry,mode,0); + mnt_drop_write(nd.mnt); + break; + case S_IFDIR: + error = -EPERM; + break; + default: + error = -EINVAL; } + mnt_drop_write(nd.mnt); +out_dput: + dput(dentry); +out_unlock: mutex_unlock(&nd.dentry->d_inode->i_mutex); path_release(&nd); out: @@ -1888,30 +1925,37 @@ { int error = 0; char * tmp; + struct dentry *dentry; + struct nameidata nd; tmp = getname(pathname); error = PTR_ERR(tmp); - if (!IS_ERR(tmp)) { - struct dentry *dentry; - struct nameidata nd; + if (IS_ERR(tmp)) + goto out_err; - error = do_path_lookup(dfd, tmp, LOOKUP_PARENT, &nd); - if (error) - goto out; - dentry = lookup_create(&nd, 1); - error = PTR_ERR(dentry); - if (!IS_ERR(dentry)) { - if (!IS_POSIXACL(nd.dentry->d_inode)) - mode &= ~current->fs->umask; - error = vfs_mkdir(nd.dentry->d_inode, dentry, mode); - dput(dentry); - } - mutex_unlock(&nd.dentry->d_inode->i_mutex); - path_release(&nd); -out: - putname(tmp); - } + error = do_path_lookup(dfd, tmp, LOOKUP_PARENT, &nd); + if (error) + goto out; + dentry = lookup_create(&nd, 1); + error = PTR_ERR(dentry); + if (IS_ERR(dentry)) + goto out_unlock; + if (!IS_POSIXACL(nd.dentry->d_inode)) + mode &= ~current->fs->umask; + error = mnt_want_write(nd.mnt); + if (error) + goto out_dput; + error = vfs_mkdir(nd.dentry->d_inode, dentry, mode); + mnt_drop_write(nd.mnt); +out_dput: + dput(dentry); +out_unlock: + mutex_unlock(&nd.dentry->d_inode->i_mutex); + path_release(&nd); +out: + putname(tmp); +out_err: return error; } @@ -2010,10 +2054,16 @@ mutex_lock_nested(&nd.dentry->d_inode->i_mutex, I_MUTEX_PARENT); dentry = lookup_hash(&nd); error = PTR_ERR(dentry); - if (!IS_ERR(dentry)) { - error = vfs_rmdir(nd.dentry->d_inode, dentry); - dput(dentry); - } + if (IS_ERR(dentry)) + goto exit2; + error = mnt_want_write(nd.mnt); + if (error) + goto exit3; + error = vfs_rmdir(nd.dentry->d_inode, dentry); + mnt_drop_write(nd.mnt); +exit3: + dput(dentry); +exit2: mutex_unlock(&nd.dentry->d_inode->i_mutex); exit1: path_release(&nd); @@ -2091,7 +2141,11 @@ inode = dentry->d_inode; if (inode) atomic_inc(&inode->i_count); + error = mnt_want_write(nd.mnt); + if (error) + goto exit2; error = vfs_unlink(nd.dentry->d_inode, dentry); + mnt_drop_write(nd.mnt); exit2: dput(dentry); } @@ -2153,30 +2207,38 @@ int error = 0; char * from; char * to; + struct dentry *dentry; + struct nameidata nd; from = getname(oldname); if(IS_ERR(from)) return PTR_ERR(from); to = getname(newname); error = PTR_ERR(to); - if (!IS_ERR(to)) { - struct dentry *dentry; - struct nameidata nd; + if (IS_ERR(to)) + goto out_putname; - error = do_path_lookup(newdfd, to, LOOKUP_PARENT, &nd); - if (error) - goto out; - dentry = lookup_create(&nd, 0); - error = PTR_ERR(dentry); - if (!IS_ERR(dentry)) { - error = vfs_symlink(nd.dentry->d_inode, dentry, from, S_IALLUGO); - dput(dentry); - } - mutex_unlock(&nd.dentry->d_inode->i_mutex); - path_release(&nd); + error = do_path_lookup(newdfd, to, LOOKUP_PARENT, &nd); + if (error) + goto out; + dentry = lookup_create(&nd, 0); + error = PTR_ERR(dentry); + if (IS_ERR(dentry)) + goto out_unlock; + + error = mnt_want_write(nd.mnt); + if (error) + goto out_dput; + error = vfs_symlink(nd.dentry->d_inode, dentry, from, S_IALLUGO); + mnt_drop_write(nd.mnt); +out_dput: + dput(dentry); +out_unlock: + mutex_unlock(&nd.dentry->d_inode->i_mutex); + path_release(&nd); out: - putname(to); - } + putname(to); +out_putname: putname(from); return error; } @@ -2260,10 +2322,16 @@ goto out_release; new_dentry = lookup_create(&nd, 0); error = PTR_ERR(new_dentry); - if (!IS_ERR(new_dentry)) { - error = vfs_link(old_nd.dentry, nd.dentry->d_inode, new_dentry); - dput(new_dentry); - } + if (IS_ERR(new_dentry)) + goto out_unlock; + error = mnt_want_write(nd.mnt); + if (error) + goto out_dput; + error = vfs_link(old_nd.dentry, nd.dentry->d_inode, new_dentry); + mnt_drop_write(nd.mnt); +out_dput: + dput(new_dentry); +out_unlock: mutex_unlock(&nd.dentry->d_inode->i_mutex); out_release: path_release(&nd); @@ -2439,29 +2507,37 @@ if (error) goto exit; - error = do_path_lookup(newdfd, newname, LOOKUP_PARENT, &newnd); + error = mnt_want_write(oldnd.mnt); if (error) goto exit1; + error = do_path_lookup(newdfd, newname, LOOKUP_PARENT, &newnd); + if (error) + goto exit2; + + error = mnt_want_write(oldnd.mnt); + if (error) + goto exit3; + error = -EXDEV; if (oldnd.mnt != newnd.mnt) - goto exit2; + goto exit4; old_dir = oldnd.dentry; error = -EBUSY; if (oldnd.last_type != LAST_NORM) - goto exit2; + goto exit4; new_dir = newnd.dentry; if (newnd.last_type != LAST_NORM) - goto exit2; + goto exit4; trap = lock_rename(new_dir, old_dir); old_dentry = lookup_hash(&oldnd); error = PTR_ERR(old_dentry); if (IS_ERR(old_dentry)) - goto exit3; + goto exit5; /* source must exist */ error = -ENOENT; if (!old_dentry->d_inode) @@ -2470,33 +2546,37 @@ if (!S_ISDIR(old_dentry->d_inode->i_mode)) { error = -ENOTDIR; if (oldnd.last.name[oldnd.last.len]) - goto exit4; + goto exit6; if (newnd.last.name[newnd.last.len]) - goto exit4; + goto exit6; } /* source should not be ancestor of target */ error = -EINVAL; if (old_dentry == trap) - goto exit4; + goto exit6; new_dentry = lookup_hash(&newnd); error = PTR_ERR(new_dentry); if (IS_ERR(new_dentry)) - goto exit4; + goto exit6; /* target should not be an ancestor of source */ error = -ENOTEMPTY; if (new_dentry == trap) - goto exit5; + goto exit7; error = vfs_rename(old_dir->d_inode, old_dentry, new_dir->d_inode, new_dentry); -exit5: +exit7: dput(new_dentry); -exit4: +exit6: dput(old_dentry); -exit3: +exit5: unlock_rename(new_dir, old_dir); -exit2: +exit4: + mnt_drop_write(newnd.mnt); +exit3: path_release(&newnd); +exit2: + mnt_drop_write(oldnd.mnt); exit1: path_release(&oldnd); exit: --- linux.orig/fs/namespace.c~convert-permission-to-file-and-vfs 2006-06-07 16:53:11.000000000 -0700 +++ linux/fs/namespace.c 2006-06-07 17:04:55.000000000 -0700 @@ -66,6 +66,7 @@ if (mnt) { memset(mnt, 0, sizeof(struct vfsmount)); atomic_set(&mnt->mnt_count, 1); + /* atomic_set(&mnt->writer_count, 0); */ INIT_LIST_HEAD(&mnt->mnt_hash); INIT_LIST_HEAD(&mnt->mnt_child); INIT_LIST_HEAD(&mnt->mnt_mounts); @@ -353,38 +354,41 @@ { struct vfsmount *mnt = v; int err = 0; + int i; static struct proc_fs_info { - int flag; - char *str; + int s_flag; + int mnt_flag; + char *set_str; + char *unset_str; } fs_info[] = { - { MS_SYNCHRONOUS, ",sync" }, - { MS_DIRSYNC, ",dirsync" }, - { MS_MANDLOCK, ",mand" }, - { 0, NULL } - }; - static struct proc_fs_info mnt_info[] = { - { MNT_NOSUID, ",nosuid" }, - { MNT_NODEV, ",nodev" }, - { MNT_NOEXEC, ",noexec" }, - { MNT_NOATIME, ",noatime" }, - { MNT_NODIRATIME, ",nodiratime" }, - { 0, NULL } + { MS_RDONLY, MNT_RDONLY, "ro", "rw" }, + { MS_SYNCHRONOUS, 0, ",sync", NULL }, + { MS_DIRSYNC, 0, ",dirsync", NULL }, + { MS_MANDLOCK, 0, ",mand", NULL }, + { 0, MNT_NOSUID, ",nosuid", NULL }, + { 0, MNT_NODEV, ",nodev", NULL }, + { 0, MNT_NOEXEC, ",noexec", NULL }, + { 0, MNT_NOATIME, ",noatime", NULL }, + { 0, MNT_NODIRATIME, ",nodiratime", NULL } }; - struct proc_fs_info *fs_infop; mangle(m, mnt->mnt_devname ? mnt->mnt_devname : "none"); seq_putc(m, ' '); seq_path(m, mnt, mnt->mnt_root, " \t\n\\"); seq_putc(m, ' '); mangle(m, mnt->mnt_sb->s_type->name); - seq_puts(m, mnt->mnt_sb->s_flags & MS_RDONLY ? " ro" : " rw"); - for (fs_infop = fs_info; fs_infop->flag; fs_infop++) { - if (mnt->mnt_sb->s_flags & fs_infop->flag) - seq_puts(m, fs_infop->str); - } - for (fs_infop = mnt_info; fs_infop->flag; fs_infop++) { - if (mnt->mnt_flags & fs_infop->flag) - seq_puts(m, fs_infop->str); + seq_putc(m, ' '); + for (i = 0; i < ARRAY_SIZE(fs_info); i++) { + struct proc_fs_info *fs_infop = &fs_info[i]; + char *str = NULL; + if ((mnt->mnt_sb->s_flags & fs_infop->s_flag) || + (mnt->mnt_flags & fs_infop->mnt_flag)) + str = fs_infop->set_str; + else + str = fs_infop->unset_str; + + if (str) + seq_puts(m, str); } if (mnt->mnt_sb->s_op->show_options) err = mnt->mnt_sb->s_op->show_options(m, mnt); @@ -678,6 +682,10 @@ if (current->uid != nd->dentry->d_inode->i_uid) return -EPERM; } + /* + * We will eventually check for the mnt->writer_count here, + * but since the code is not used now, skipt it - Dave Hansen + */ if (vfs_permission(nd, MAY_WRITE)) return -EPERM; return 0; @@ -897,7 +905,8 @@ /* * do loopback mount. */ -static int do_loopback(struct nameidata *nd, char *old_name, int recurse) +static int do_loopback(struct nameidata *nd, char *old_name, + int recurse, int mnt_flags) { struct nameidata old_nd; struct vfsmount *mnt = NULL; @@ -935,6 +944,7 @@ spin_unlock(&vfsmount_lock); release_mounts(&umount_list); } + mnt->mnt_flags = mnt_flags; out: up_write(&namespace_sem); @@ -1395,6 +1405,8 @@ ((char *)data_page)[PAGE_SIZE - 1] = 0; /* Separate the per-mountpoint flags */ + if (flags & MS_RDONLY) + mnt_flags |= MNT_RDONLY; if (flags & MS_NOSUID) mnt_flags |= MNT_NOSUID; if (flags & MS_NODEV) @@ -1422,7 +1434,7 @@ retval = do_remount(&nd, flags & ~MS_REMOUNT, mnt_flags, data_page); else if (flags & MS_BIND) - retval = do_loopback(&nd, dev_name, flags & MS_REC); + retval = do_loopback(&nd, dev_name, flags & MS_REC, mnt_flags); else if (flags & (MS_SHARED | MS_PRIVATE | MS_SLAVE | MS_UNBINDABLE)) retval = do_change_type(&nd, flags); else if (flags & MS_MOVE) --- linux.orig/fs/ncpfs/ioctl.c~elevate-writers-file_permission-callers 2006-06-07 16:53:15.000000000 -0700 +++ linux/fs/ncpfs/ioctl.c 2006-06-07 16:53:15.000000000 -0700 @@ -183,7 +183,7 @@ } #endif /* CONFIG_NCPFS_NLS */ -int ncp_ioctl(struct inode *inode, struct file *filp, +static int __ncp_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg) { struct ncp_server *server = NCP_SERVER(inode); @@ -654,3 +654,55 @@ /* #endif */ return -EINVAL; } + +static int ncp_ioctl_need_write(unsigned int cmd) +{ + switch (cmd) { + case NCP_IOC_GET_FS_INFO: + case NCP_IOC_GET_FS_INFO_V2: + case NCP_IOC_NCPREQUEST: + case NCP_IOC_SETDENTRYTTL: + case NCP_IOC_SIGN_INIT: + case NCP_IOC_LOCKUNLOCK: + case NCP_IOC_SET_SIGN_WANTED: + return 0; + case NCP_IOC_GETOBJECTNAME: + case NCP_IOC_SETOBJECTNAME: + case NCP_IOC_GETPRIVATEDATA: + case NCP_IOC_SETPRIVATEDATA: + case NCP_IOC_SETCHARSETS: + case NCP_IOC_GETCHARSETS: + case NCP_IOC_CONN_LOGGED_IN: + case NCP_IOC_GETDENTRYTTL: + case NCP_IOC_GETMOUNTUID2: + case NCP_IOC_SIGN_WANTED: + case NCP_IOC_GETROOT: + case NCP_IOC_SETROOT: + return 0; + default: + /* unkown IOCTL command, assume write */ + WARN_ON(); + } + return 1; +} + +int ncp_ioctl(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg) +{ + int ret; + + if (ncp_ioctl_need_write(cmd)) { + /* + * inside the ioctl(), any failures which + * are because of file_permission() are + * -EACCESS, so it seems consistent to keep + * that here. + */ + if (mnt_want_write(filp->f_vfsmnt)) + return -EACCESS; + } + ret = __ncp_ioctl(inode, filp, cmd, arg); + if (ncp_ioctl_need_write(cmd) + mnt_drop_write(filp->->f_vfsmnt; + return ret; +} --- linux.orig/fs/nfsd/nfs4proc.c~xattr 2006-06-07 16:53:27.000000000 -0700 +++ linux/fs/nfsd/nfs4proc.c 2006-06-07 16:53:27.000000000 -0700 @@ -604,13 +604,18 @@ return status; } } + status = mnt_want_write(current_fh->fh_export->ex_mnt); + if (status) + return status; status = nfs_ok; if (setattr->sa_acl != NULL) status = nfsd4_set_nfs4_acl(rqstp, current_fh, setattr->sa_acl); if (status) - return status; + goto out; status = nfsd_setattr(rqstp, current_fh, &setattr->sa_iattr, 0, (time_t)0); +out: + mnt_drop_write(current_fh->fh_export->ex_mnt); return status; } --- linux.orig/fs/nfsd/nfs4recover.c~elevate-writers-vfs_unlink 2006-06-07 16:53:13.000000000 -0700 +++ linux/fs/nfsd/nfs4recover.c 2006-06-07 16:53:14.000000000 -0700 @@ -155,7 +155,11 @@ dprintk("NFSD: nfsd4_create_clid_dir: DIRECTORY EXISTS\n"); goto out_put; } + status = mnt_want_write(rec_dir.mnt); + if (status) + goto out_put; status = vfs_mkdir(rec_dir.dentry->d_inode, dentry, S_IRWXU); + mnt_drop_write(rec_dir.mnt); out_put: dput(dentry); out_unlock: --- linux.orig/fs/nfsd/vfs.c~elevate-writers-vfs_unlink 2006-06-07 16:53:13.000000000 -0700 +++ linux/fs/nfsd/vfs.c 2006-06-07 16:53:26.000000000 -0700 @@ -1153,6 +1153,9 @@ /* * Get the dir op function pointer. */ + err = mnt_want_write(fhp->fh_export->ex_mnt); + if (err) + goto out_nfserr; err = nfserr_perm; switch (type) { case S_IFREG: @@ -1171,6 +1174,7 @@ printk("nfsd: bad file type %o in nfsd_create\n", type); err = -EINVAL; } + mnt_drop_write(fhp->fh_export->ex_mnt); if (err < 0) goto out_nfserr; @@ -1597,13 +1601,23 @@ err = -EPERM; } else #endif + err = mnt_want_write(ffhp->fh_export->ex_mnt); + if (err) + goto out_dput_new; + + err = mnt_want_write(tfhp->fh_export->ex_mnt); + if (err) + goto out_mnt_drop_write_old; + err = vfs_rename(fdir, odentry, tdir, ndentry); if (!err && EX_ISSYNC(tfhp->fh_export)) { err = nfsd_sync_dir(tdentry); if (!err) err = nfsd_sync_dir(fdentry); } - + mnt_drop_write(tfhp->fh_export->ex_mnt); + out_mnt_drop_write_old: + mnt_drop_write(ffhp->fh_export->ex_mnt); out_dput_new: dput(ndentry); out_dput_old: --- linux.orig/fs/open.c~elevate-writers-vfs_mkdir 2006-06-07 16:53:14.000000000 -0700 +++ linux/fs/open.c 2006-06-07 16:53:23.000000000 -0700 @@ -244,28 +244,32 @@ if (!S_ISREG(inode->i_mode)) goto dput_and_out; - error = vfs_permission(&nd, MAY_WRITE); + error = mnt_want_write(nd.mnt); if (error) goto dput_and_out; + error = vfs_permission(&nd, MAY_WRITE); + if (error) + goto mnt_drop_write_and_out; + error = -EROFS; if (IS_RDONLY(inode)) - goto dput_and_out; + goto mnt_drop_write_and_out; error = -EPERM; if (IS_IMMUTABLE(inode) || IS_APPEND(inode)) - goto dput_and_out; + goto mnt_drop_write_and_out; /* * Make sure that there are no leases. */ error = break_lease(inode, FMODE_WRITE); if (error) - goto dput_and_out; + goto mnt_drop_write_and_out; error = get_write_access(inode); if (error) - goto dput_and_out; + goto mnt_drop_write_and_out; error = locks_verify_truncate(inode, NULL, length); if (!error) { @@ -274,6 +278,8 @@ } put_write_access(inode); +mnt_drop_write_and_out: + mnt_drop_write(nd.mnt); dput_and_out: path_release(&nd); out: @@ -378,16 +384,20 @@ goto out; inode = nd.dentry->d_inode; + error = mnt_want_write(nd.mnt); + if (error) + goto dput_and_out; + error = -EROFS; if (IS_RDONLY(inode)) - goto dput_and_out; + goto mnt_drop_write_and_out; /* Don't worry, the checks are done in inode_change_ok() */ newattrs.ia_valid = ATTR_CTIME | ATTR_MTIME | ATTR_ATIME; if (times) { error = -EPERM; if (IS_APPEND(inode) || IS_IMMUTABLE(inode)) - goto dput_and_out; + goto mnt_drop_write_and_out; error = get_user(newattrs.ia_atime.tv_sec, ×->actime); newattrs.ia_atime.tv_nsec = 0; @@ -395,21 +405,23 @@ error = get_user(newattrs.ia_mtime.tv_sec, ×->modtime); newattrs.ia_mtime.tv_nsec = 0; if (error) - goto dput_and_out; + goto mnt_drop_write_and_out; newattrs.ia_valid |= ATTR_ATIME_SET | ATTR_MTIME_SET; } else { error = -EACCES; if (IS_IMMUTABLE(inode)) - goto dput_and_out; + goto mnt_drop_write_and_out; if (current->fsuid != inode->i_uid && (error = vfs_permission(&nd, MAY_WRITE)) != 0) - goto dput_and_out; + goto mnt_drop_write_and_out; } mutex_lock(&inode->i_mutex); error = notify_change(nd.dentry, &newattrs); mutex_unlock(&inode->i_mutex); +mnt_drop_write_and_out: + mnt_drop_write(nd.mnt); dput_and_out: path_release(&nd); out: @@ -435,16 +447,19 @@ goto out; inode = nd.dentry->d_inode; + error = mnt_want_write(nd.mnt); + if (error) + goto dput_and_out; error = -EROFS; if (IS_RDONLY(inode)) - goto dput_and_out; + goto mnt_drop_write_and_out; /* Don't worry, the checks are done in inode_change_ok() */ newattrs.ia_valid = ATTR_CTIME | ATTR_MTIME | ATTR_ATIME; if (times) { error = -EPERM; if (IS_APPEND(inode) || IS_IMMUTABLE(inode)) - goto dput_and_out; + goto mnt_drop_write_and_out; newattrs.ia_atime.tv_sec = times[0].tv_sec; newattrs.ia_atime.tv_nsec = times[0].tv_usec * 1000; @@ -454,15 +469,17 @@ } else { error = -EACCES; if (IS_IMMUTABLE(inode)) - goto dput_and_out; + goto mnt_drop_write_and_out; if (current->fsuid != inode->i_uid && (error = vfs_permission(&nd, MAY_WRITE)) != 0) - goto dput_and_out; + goto mnt_drop_write_and_out; } mutex_lock(&inode->i_mutex); error = notify_change(nd.dentry, &newattrs); mutex_unlock(&inode->i_mutex); +mnt_drop_write_and_out: + mnt_drop_write(nd.mnt); dput_and_out: path_release(&nd); out: @@ -520,15 +537,29 @@ current->cap_effective = current->cap_permitted; res = __user_walk_fd(dfd, filename, LOOKUP_FOLLOW|LOOKUP_ACCESS, &nd); - if (!res) { - res = vfs_permission(&nd, mode); - /* SuS v2 requires we report a read only fs too */ - if(!res && (mode & S_IWOTH) && IS_RDONLY(nd.dentry->d_inode) - && !special_file(nd.dentry->d_inode->i_mode)) - res = -EROFS; - path_release(&nd); - } + if (res) + goto out; + res = vfs_permission(&nd, mode); + /* SuS v2 requires we report a read only fs too */ + if(res) + goto out_path_release; + if (!(mode & S_IWOTH)) + goto out_path_release; + if (special_file(nd.dentry->d_inode->i_mode)) + goto out_path_release; + + res = mnt_want_write(nd.mnt); + if (res) { + mnt_drop_write(nd.mnt); + goto out_path_release; + } + if (IS_RDONLY(nd.dentry->d_inode)) + res = -EROFS; +out_path_release: + path_release(&nd); + +out: current->fsuid = old_fsuid; current->fsgid = old_fsgid; current->cap_effective = old_cap; --- linux.orig/fs/xattr.c~elevate-writers-vfs_rename-part1 2006-06-07 16:53:26.000000000 -0700 +++ linux/fs/xattr.c 2006-06-07 16:53:27.000000000 -0700 @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include @@ -210,7 +211,11 @@ error = user_path_walk(path, &nd); if (error) return error; + error = mnt_want_write(nd.mnt); + if (error) + return error; error = setxattr(nd.dentry, name, value, size, flags); + mnt_drop_write(nd.mnt); path_release(&nd); return error; } @@ -225,7 +230,11 @@ error = user_path_walk_link(path, &nd); if (error) return error; + error = mnt_want_write(nd.mnt); + if (error) + return error; error = setxattr(nd.dentry, name, value, size, flags); + mnt_drop_write(nd.mnt); path_release(&nd); return error; } @@ -241,9 +250,14 @@ f = fget(fd); if (!f) return error; + error = mnt_want_write(f->f_vfsmnt); + if (error) + goto out_fput; dentry = f->f_dentry; audit_inode(NULL, dentry->d_inode, 0); error = setxattr(dentry, name, value, size, flags); + mnt_drop_write(f->f_vfsmnt); +out_fput: fput(f); return error; } --- linux.orig/include/linux/mount.h~convert-permission-to-file-and-vfs 2006-06-07 16:53:11.000000000 -0700 +++ linux/include/linux/mount.h 2006-06-07 16:53:29.000000000 -0700 @@ -12,6 +12,8 @@ #define _LINUX_MOUNT_H #ifdef __KERNEL__ +#include +#include #include #include #include @@ -22,6 +24,7 @@ #define MNT_NOEXEC 0x04 #define MNT_NOATIME 0x08 #define MNT_NODIRATIME 0x10 +#define MNT_RDONLY 0x20 #define MNT_SHRINKABLE 0x100 @@ -38,6 +41,7 @@ struct list_head mnt_mounts; /* list of children, anchored here */ struct list_head mnt_child; /* and going through their mnt_child */ atomic_t mnt_count; + atomic_t mnt_writers; int mnt_flags; int mnt_expiry_mark; /* true if marked for expiry */ char *mnt_devname; /* Name of device e.g. /dev/dsk/hda1 */ @@ -58,6 +62,19 @@ return mnt; } +static inline int mnt_want_write(struct vfsmount *mnt) +{ + if (mnt->mnt_flags & MNT_RDONLY) + return -EROFS; + atomic_inc(&mnt->mnt_writers); + return 0; +} + +static inline void mnt_drop_write(struct vfsmount *mnt) +{ + atomic_dec(&mnt->mnt_writers); +} + extern void mntput_no_expire(struct vfsmount *mnt); extern void mnt_pin(struct vfsmount *mnt); extern void mnt_unpin(struct vfsmount *mnt); --- linux.orig/ipc/mqueue.c~elevate-writers-vfs_unlink 2006-06-07 16:53:13.000000000 -0700 +++ linux/ipc/mqueue.c 2006-06-07 16:53:20.000000000 -0700 @@ -679,6 +679,9 @@ goto out; filp = do_open(dentry, oflag); } else { + error = mnt_want_write(mqueue_mnt); + if (error) + goto out; filp = do_create(mqueue_mnt->mnt_root, dentry, oflag, mode, u_attr); } @@ -738,8 +741,11 @@ inode = dentry->d_inode; if (inode) atomic_inc(&inode->i_count); - + err = mnt_want_write(mqueue_mnt); + if (err) + goto out_err; err = vfs_unlink(dentry->d_parent->d_inode, dentry); + mnt_drop_write(mqueue_mnt); out_err: dput(dentry); --- linux.orig/mm/filemap.c~D8-actually-add-flags 2006-06-07 16:53:29.000000000 -0700 +++ linux/mm/filemap.c 2006-06-07 16:53:29.000000000 -0700 @@ -23,6 +23,7 @@ #include #include #include +#include #include #include #include @@ -1960,6 +1961,8 @@ return -EINVAL; if (!isblk) { + if (file->f_vfsmnt->mnt_flags & MNT_RDONLY) + return -EROFS; /* FIXME: this is for backwards compatibility with 2.4 */ if (file->f_flags & O_APPEND) *pos = i_size_read(inode); --- linux.orig/net/unix/af_unix.c~elevate-writers-opens-part4 2006-06-07 16:53:24.000000000 -0700 +++ linux/net/unix/af_unix.c 2006-06-07 16:53:25.000000000 -0700 @@ -676,21 +676,27 @@ err = path_lookup(sunname->sun_path, LOOKUP_FOLLOW, &nd); if (err) goto fail; + + err = mnt_want_write(nd.mnt); + if (err) + goto put_path_fail; + err = vfs_permission(&nd, MAY_WRITE); if (err) - goto put_fail; + goto put_write_fail; err = -ECONNREFUSED; if (!S_ISSOCK(nd.dentry->d_inode->i_mode)) - goto put_fail; + goto put_write_fail; u=unix_find_socket_byinode(nd.dentry->d_inode); if (!u) - goto put_fail; + goto put_write_fail; if (u->sk_type == type) touch_atime(nd.mnt, nd.dentry); path_release(&nd); + mnt_drop_write(nd.mnt); err=-EPROTOTYPE; if (u->sk_type != type) { @@ -710,7 +716,9 @@ } return u; -put_fail: +put_write_fail: + mnt_drop_write(nd.mnt); +put_path_fail: path_release(&nd); fail: *error=err; @@ -781,7 +789,11 @@ */ mode = S_IFSOCK | (SOCK_INODE(sock)->i_mode & ~current->fs->umask); + err = mnt_want_write(nd.mnt); + if (err) + goto out_mknod_dput; err = vfs_mknod(nd.dentry->d_inode, dentry, mode, 0); + mnt_drop_write(nd.mnt); if (err) goto out_mknod_dput; mutex_unlock(&nd.dentry->d_inode->i_mutex);