/* 向文件发送一个命令,如获取文件信息的命令, * 将文件的信息拷贝到arg所指向的内存,如关于文件的flag和版本等信息 */ int ext2_ioctl (struct inode * inode, struct file * filp, unsigned int cmd, unsigned long arg) { ext2_debug ("cmd = %u, arg = %lu\n", cmd, arg); switch (cmd) { case EXT2_IOC_GETFLAGS: put_fs_long (inode->u.ext2_i.i_flags, (long *) arg); return 0; case EXT2_IOC_SETFLAGS: if ((current->euid != inode->i_uid) && !suser()) return -EPERM; if (IS_RDONLY(inode)) return -EROFS; inode->u.ext2_i.i_flags = get_fs_long ((long *) arg); inode->i_ctime = CURRENT_TIME; inode->i_dirt = 1; return 0; case EXT2_IOC_GETVERSION: put_fs_long (inode->u.ext2_i.i_version, (long *) arg); return 0; case EXT2_IOC_SETVERSION: if ((current->euid != inode->i_uid) && !suser()) return -EPERM; if (IS_RDONLY(inode)) return -EROFS; inode->u.ext2_i.i_version = get_fs_long ((long *) arg); inode->i_ctime = CURRENT_TIME; inode->i_dirt = 1; return 0; default: return -EINVAL; } }
static int xiafs_readdir(struct inode * inode, struct file * filp, struct dirent * dirent, int count) { u_int offset, i; struct buffer_head * bh; struct xiafs_direct * de; if (!inode || !inode->i_sb || !S_ISDIR(inode->i_mode)) return -EBADF; if (inode->i_size & (XIAFS_ZSIZE(inode->i_sb) - 1) ) return -EBADF; while (filp->f_pos < inode->i_size) { offset = filp->f_pos & (XIAFS_ZSIZE(inode->i_sb) - 1); bh = xiafs_bread(inode, filp->f_pos >> XIAFS_ZSIZE_BITS(inode->i_sb),0); if (!bh) { filp->f_pos += XIAFS_ZSIZE(inode->i_sb)-offset; continue; } de = (struct xiafs_direct *) (offset + bh->b_data); while (offset < XIAFS_ZSIZE(inode->i_sb) && filp->f_pos < inode->i_size) { if (de->d_ino > inode->i_sb->u.xiafs_sb.s_ninodes || de->d_rec_len < 12 || (char *)de+de->d_rec_len > XIAFS_ZSIZE(inode->i_sb)+bh->b_data || de->d_name_len < 1 || de->d_name_len + 8 > de->d_rec_len || de->d_name_len > _XIAFS_NAME_LEN || de->d_name[de->d_name_len] ) { printk("XIA-FS: bad directory entry (%s %d)\n", WHERE_ERR); brelse(bh); return 0; } offset += de->d_rec_len; filp->f_pos += de->d_rec_len; if (de->d_ino) { for (i = 0; i < de->d_name_len ; i++) put_fs_byte(de->d_name[i],i+dirent->d_name); put_fs_byte(0,i+dirent->d_name); put_fs_long(de->d_ino,&dirent->d_ino); put_fs_word(i,&dirent->d_reclen); brelse(bh); if (!IS_RDONLY (inode)) { inode->i_atime=CURRENT_TIME; inode->i_dirt=1; } return i; } de = (struct xiafs_direct *) (offset + bh->b_data); } brelse(bh); if (offset > XIAFS_ZSIZE(inode->i_sb)) { printk("XIA-FS: bad directory (%s %d)\n", WHERE_ERR); return 0; } } if (!IS_RDONLY (inode)) { inode->i_atime=CURRENT_TIME; inode->i_dirt=1; } return 0; }
int sys_chmod(char *filename, mode_t mode) { struct inode *inode; register struct inode *inodep; int error = namei(filename, &inode, 0, 0); #ifdef USE_NOTIFY_CHANGE struct iattr newattrs; register struct iattr *nap = &newattrs; inodep = inode; if (error) return error; if (IS_RDONLY(inodep)) { iput(inodep); return -EROFS; } if (mode == (mode_t) - 1) mode = inodep->i_mode; nap->ia_mode = (mode & S_IALLUGO) | (inodep->i_mode & ~S_IALLUGO); nap->ia_valid = ATTR_MODE | ATTR_CTIME; inodep->i_dirt = 1; error = notify_change(inodep, nap); iput(inodep); #else if (!error) { inodep = inode; if (IS_RDONLY(inodep)) { iput(inodep); return -EROFS; } if (mode == (mode_t) - 1) mode = inodep->i_mode; mode = (mode & S_IALLUGO) | (inodep->i_mode & ~S_IALLUGO); if ((current->euid != inodep->i_uid) && !suser()) { /* FIXME - Should we iput(inodep); at this point? */ return -EPERM; } if (!suser() && !in_group_p(inodep->i_gid)) { mode &= ~S_ISGID; } inodep->i_mode = mode; inodep->i_dirt = 1; iput(inodep); } #endif return error; }
asmlinkage int sys_chmod(const char * filename, mode_t mode) { struct inode * inode; int error; struct iattr newattrs; error = namei(filename,&inode); if (error) return error; if (IS_RDONLY(inode)) { iput(inode); return -EROFS; } if (IS_IMMUTABLE(inode) || IS_APPEND(inode)) { iput(inode); return -EPERM; } if (mode == (mode_t) -1) mode = inode->i_mode; newattrs.ia_mode = (mode & S_IALLUGO) | (inode->i_mode & ~S_IALLUGO); newattrs.ia_valid = ATTR_MODE | ATTR_CTIME; inode->i_dirt = 1; error = notify_change(inode, &newattrs); iput(inode); return error; }
/* If times==NULL, set access and modification to current time, * must be owner or have write permission. * Else, update from *times, must be owner or super user. */ asmlinkage int sys_utime(char * filename, struct utimbuf * times) { int error; struct inode * inode; struct iattr newattrs; error = namei(filename,&inode); if (error) return error; if (IS_RDONLY(inode)) { iput(inode); return -EROFS; } /* Don't worry, the checks are done in inode_change_ok() */ newattrs.ia_valid = ATTR_CTIME | ATTR_MTIME | ATTR_ATIME; if (times) { error = verify_area(VERIFY_READ, times, sizeof(*times)); if (error) { iput(inode); return error; } newattrs.ia_atime = get_user(×->actime); newattrs.ia_mtime = get_user(×->modtime); newattrs.ia_valid |= ATTR_ATIME_SET | ATTR_MTIME_SET; } else { if (current->fsuid != inode->i_uid && (error = permission(inode,MAY_WRITE)) != 0) { iput(inode); return error; } } error = notify_change(inode, &newattrs); iput(inode); return error; }
int sys_truncate(char *path, loff_t length) { struct inode *inode; register struct inode *inodep; int error; error = namei(path, &inode, NOT_DIR, MAY_WRITE); inodep = inode; if (error) return error; if (IS_RDONLY(inodep)) { iput(inodep); return -EROFS; } #ifdef BLOAT_FS error = get_write_access(inodep); if (error) { iput(inodep); return error; } #endif error = do_truncate(inodep, length); put_write_access(inodep); iput(inodep); return error; }
static int do_chown(register struct inode *inode, uid_t user, gid_t group) { struct iattr newattrs; register struct iattr *nap = &newattrs; if (IS_RDONLY(inode)) return -EROFS; if (user == (uid_t) - 1) user = inode->i_uid; if (group == (gid_t) - 1) group = inode->i_gid; nap->ia_mode = inode->i_mode; nap->ia_uid = user; nap->ia_gid = group; nap->ia_valid = ATTR_UID | ATTR_GID | ATTR_CTIME; /* * If the owner has been changed, remove the setuid bit */ if (user != inode->i_uid && (inode->i_mode & S_ISUID)) { nap->ia_mode &= ~S_ISUID; nap->ia_valid |= ATTR_MODE; } /* * If the group has been changed, remove the setgid bit */ if (group != inode->i_gid && (inode->i_mode & S_ISGID)) { nap->ia_mode &= ~S_ISGID; nap->ia_valid |= ATTR_MODE; } inode->i_dirt = 1; return notify_change(inode, nap); }
static int chown_common(struct _dentry * dentry, uid_t user, gid_t group) { struct _inode * inode; int error; struct iattr newattrs; error = -ENOENT; if (!(inode = d_get_inode(dentry))) { printk(KERN_ERR "chown_common: NULL inode\n"); goto out; } error = -EROFS; if (IS_RDONLY(inode)) goto out; error = -EPERM; if (IS_IMMUTABLE(inode) || IS_APPEND(inode)) goto out; newattrs.ia_valid = ATTR_CTIME; if (user != (uid_t) -1) { newattrs.ia_valid |= ATTR_UID; newattrs.ia_uid = user; } if (group != (gid_t) -1) { newattrs.ia_valid |= ATTR_GID; newattrs.ia_gid = group; } if (!S_ISDIR(inode->i_mode)) newattrs.ia_valid |= ATTR_KILL_SUID|ATTR_KILL_SGID; imutex_lock(parent(inode)); error = notify_change(dentry, &newattrs); imutex_unlock(parent(inode)); out: return error; }
STATIC int xfs_attrmulti_attr_set( struct inode *inode, char *name, const char __user *ubuf, __uint32_t len, __uint32_t flags) { char *kbuf; int error = EFAULT; if (IS_RDONLY(inode)) return -EROFS; if (IS_IMMUTABLE(inode) || IS_APPEND(inode)) return EPERM; if (len > XATTR_SIZE_MAX) return EINVAL; kbuf = memdup_user(ubuf, len); if (IS_ERR(kbuf)) return PTR_ERR(kbuf); error = xfs_attr_set(XFS_I(inode), name, kbuf, len, flags); return error; }
struct chan *devopen(struct chan *c, int omode, struct dirtab *tab, int ntab, Devgen * gen) { int i; struct dir dir; dir.qid.path = 0; for (i = 0;; i++) { switch ((*gen) (c, NULL, tab, ntab, i, &dir)) { case -1: goto Return; case 0: break; case 1: if (c->qid.path == dir.qid.path) { devpermcheck(dir.uid, dir.mode, omode); goto Return; } break; } } Return: c->offset = 0; if ((c->qid.type & QTDIR) && !IS_RDONLY(omode)) error("Tried opening dir with non-read-only mode %o", omode); c->mode = openmode(omode); c->flag |= COPEN; return c; }
asmlinkage long sys_chmod(const char * filename, mode_t mode) { struct nameidata nd; struct inode * inode; int error; struct iattr newattrs; error = user_path_walk(filename, &nd); if (error) goto out; inode = nd.dentry->d_inode; error = -EROFS; if (IS_RDONLY(inode)) goto dput_and_out; error = -EPERM; if (IS_IMMUTABLE(inode) || IS_APPEND(inode)) goto dput_and_out; if (mode == (mode_t) -1) mode = inode->i_mode; newattrs.ia_mode = (mode & S_IALLUGO) | (inode->i_mode & ~S_IALLUGO); newattrs.ia_valid = ATTR_MODE | ATTR_CTIME; error = notify_change(nd.dentry, &newattrs); dput_and_out: path_release(&nd); out: return error; }
int sys_utimes(char *filename, struct timeval *utimes) { int error; struct inode *inode; register struct inode *inodep; struct iattr newattrs; error = namei(filename, &inode, 0, 0); inodep = inode; if (error) return error; if (IS_RDONLY(inodep)) { iput(inodep); return -EROFS; } /* Don't worry, the checks are done in inode_change_ok() */ newattrs.ia_valid = ATTR_CTIME | ATTR_MTIME | ATTR_ATIME; if (utimes) { struct timeval times[2]; if (error = verified_memcpy_fromfs(×, utimes, sizeof(times))) { iput(inodep); return error; } newattrs.ia_atime = times[0].tv_sec; newattrs.ia_mtime = times[1].tv_sec; newattrs.ia_valid |= ATTR_ATIME_SET | ATTR_MTIME_SET; } else if ((error = permission(inodep, MAY_WRITE)) != 0) { iput(inodep); return error; } error = notify_change(inodep, &newattrs); iput(inodep); return error; }
STATIC int xfs_attrmulti_attr_set( struct vnode *vp, char *name, const char __user *ubuf, __uint32_t len, __uint32_t flags) { char *kbuf; int error = EFAULT; if (IS_RDONLY(&vp->v_inode)) return -EROFS; if (IS_IMMUTABLE(&vp->v_inode) || IS_APPEND(&vp->v_inode)) return EPERM; if (len > XATTR_SIZE_MAX) return EINVAL; kbuf = kmalloc(len, GFP_KERNEL); if (!kbuf) return ENOMEM; if (copy_from_user(kbuf, ubuf, len)) goto out_kfree; VOP_ATTR_SET(vp, name, kbuf, len, flags, NULL, error); out_kfree: kfree(kbuf); return error; }
asmlinkage long sys_fchmodat(int dfd, const char __user *filename, mode_t mode) { struct nameidata nd; struct _inode * inode; int error; struct iattr newattrs; error = __user_walk_fd(dfd, filename, LOOKUP_FOLLOW, &nd); if (error) goto out; inode = d_get_inode(nd.dentry); error = -EROFS; if (IS_RDONLY(inode)) goto dput_and_out; error = -EPERM; if (IS_IMMUTABLE(inode) || IS_APPEND(inode)) goto dput_and_out; imutex_lock(parent(inode)); if (mode == (mode_t) -1) mode = inode->i_mode; newattrs.ia_mode = (mode & S_IALLUGO) | (inode->i_mode & ~S_IALLUGO); newattrs.ia_valid = ATTR_MODE | ATTR_CTIME; error = notify_change(nd.dentry, &newattrs); imutex_unlock(parent(inode)); dput_and_out: path_release(&nd); out: return error; }
static inline long do_sys_truncate(const char * path, loff_t length) { struct nameidata nd; struct inode * inode; int error; error = -EINVAL; if (length < 0) /* sorry, but loff_t says... */ goto out; error = user_path_walk(path, &nd); if (error) goto out; inode = nd.dentry->d_inode; /* For directories it's -EISDIR, for other non-regulars - -EINVAL */ error = -EISDIR; if (S_ISDIR(inode->i_mode)) goto dput_and_out; error = -EINVAL; if (!S_ISREG(inode->i_mode)) goto dput_and_out; error = permission(inode,MAY_WRITE); if (error) goto dput_and_out; error = -EROFS; if (IS_RDONLY(inode)) goto dput_and_out; error = -EPERM; if (IS_IMMUTABLE(inode) || IS_APPEND(inode)) goto dput_and_out; /* * Make sure that there are no leases. */ error = get_lease(inode, FMODE_WRITE); if (error) goto dput_and_out; error = get_write_access(inode); if (error) goto dput_and_out; error = locks_verify_truncate(inode, NULL, length); if (!error) { DQUOT_INIT(inode); error = do_truncate(nd.dentry, length); } put_write_access(inode); dput_and_out: path_release(&nd); out: return error; }
asmlinkage long sys_fchmod(unsigned int fd, mode_t mode) { struct inode * inode; struct dentry * dentry; struct file * file; int err = -EBADF; struct iattr newattrs; file = fget(fd); if (!file) goto out; dentry = file->f_dentry; inode = dentry->d_inode; err = -EROFS; if (IS_RDONLY(inode)) goto out_putf; err = -EPERM; if (IS_IMMUTABLE(inode) || IS_APPEND(inode)) goto out_putf; if (mode == (mode_t) -1) mode = inode->i_mode; newattrs.ia_mode = (mode & S_IALLUGO) | (inode->i_mode & ~S_IALLUGO); newattrs.ia_valid = ATTR_MODE | ATTR_CTIME; err = notify_change(dentry, &newattrs); out_putf: fput(file); out: return err; }
static int chown_common(struct dentry * dentry, uid_t user, gid_t group) { struct inode * inode; int error; struct iattr newattrs; error = -ENOENT; if (!(inode = dentry->d_inode)) { printk("chown_common: NULL inode\n"); goto out; } error = -EROFS; if (IS_RDONLY(inode)) goto out; error = -EPERM; if (IS_IMMUTABLE(inode) || IS_APPEND(inode)) goto out; if (user == (uid_t) -1) user = inode->i_uid; if (group == (gid_t) -1) group = inode->i_gid; newattrs.ia_mode = inode->i_mode; newattrs.ia_uid = user; newattrs.ia_gid = group; newattrs.ia_valid = ATTR_UID | ATTR_GID | ATTR_CTIME; /* * If the user or group of a non-directory has been changed by a * non-root user, remove the setuid bit. * 19981026 David C Niemi <*****@*****.**> * * Changed this to apply to all users, including root, to avoid * some races. This is the behavior we had in 2.0. The check for * non-root was definitely wrong for 2.2 anyway, as it should * have been using CAP_FSETID rather than fsuid -- 19990830 SD. */ if ((inode->i_mode & S_ISUID) == S_ISUID && !S_ISDIR(inode->i_mode)) { newattrs.ia_mode &= ~S_ISUID; newattrs.ia_valid |= ATTR_MODE; } /* * Likewise, if the user or group of a non-directory has been changed * by a non-root user, remove the setgid bit UNLESS there is no group * execute bit (this would be a file marked for mandatory locking). * 19981026 David C Niemi <*****@*****.**> * * Removed the fsuid check (see the comment above) -- 19990830 SD. */ if (((inode->i_mode & (S_ISGID | S_IXGRP)) == (S_ISGID | S_IXGRP)) && !S_ISDIR(inode->i_mode)) { newattrs.ia_mode &= ~S_ISGID; newattrs.ia_valid |= ATTR_MODE; } error = DQUOT_TRANSFER(dentry, &newattrs); out: return error; }
/* * Truncate a file to 0 length. */ static void UMSDOS_truncate (struct inode *inode) { Printk (("UMSDOS_truncate\n")); if (!IS_RDONLY (inode)) { fat_truncate (inode); inode->i_ctime = inode->i_mtime = CURRENT_TIME; mark_inode_dirty(inode); } }
asmlinkage int sys_chown(const char * filename, uid_t user, gid_t group) { struct inode * inode; int error; struct iattr newattrs; error = lnamei(filename,&inode); if (error) return error; if (IS_RDONLY(inode)) { iput(inode); return -EROFS; } if (IS_IMMUTABLE(inode) || IS_APPEND(inode)) { iput(inode); return -EPERM; } if (user == (uid_t) -1) user = inode->i_uid; if (group == (gid_t) -1) group = inode->i_gid; newattrs.ia_mode = inode->i_mode; newattrs.ia_uid = user; newattrs.ia_gid = group; newattrs.ia_valid = ATTR_UID | ATTR_GID | ATTR_CTIME; /* * If the owner has been changed, remove the setuid bit */ if (inode->i_mode & S_ISUID) { newattrs.ia_mode &= ~S_ISUID; newattrs.ia_valid |= ATTR_MODE; } /* * If the group has been changed, remove the setgid bit * * Don't remove the setgid bit if no group execute bit. * This is a file marked for mandatory locking. */ if (((inode->i_mode & (S_ISGID | S_IXGRP)) == (S_ISGID | S_IXGRP))) { newattrs.ia_mode &= ~S_ISGID; newattrs.ia_valid |= ATTR_MODE; } inode->i_dirt = 1; if (inode->i_sb->dq_op) { inode->i_sb->dq_op->initialize(inode, -1); if (inode->i_sb->dq_op->transfer(inode, &newattrs, 0)) return -EDQUOT; error = notify_change(inode, &newattrs); if (error) inode->i_sb->dq_op->transfer(inode, &newattrs, 1); } else error = notify_change(inode, &newattrs); iput(inode); return(error); }
/* * access() needs to use the real uid/gid, not the effective uid/gid. * We do this by temporarily clearing all FS-related capabilities and * switching the fsuid/fsgid around to the real ones. */ asmlinkage long sys_faccessat(int dfd, const char __user *filename, int mode) { struct nameidata nd; int old_fsuid, old_fsgid; kernel_cap_t old_cap; int res; const struct _inode *inode; if (mode & ~S_IRWXO) /* where's F_OK, X_OK, W_OK, R_OK? */ return -EINVAL; old_fsuid = current->fsuid; old_fsgid = current->fsgid; old_cap = current->cap_effective; current->fsuid = current->uid; current->fsgid = current->gid; /* * Clear the capabilities if we switch to a non-root user * * FIXME: There is a race here against sys_capset. The * capabilities can change yet we will restore the old * value below. We should hold task_capabilities_lock, * but we cannot because user_path_walk can sleep. */ if (current->uid) cap_clear(current->cap_effective); else current->cap_effective = current->cap_permitted; res = __user_walk_fd(dfd, filename, LOOKUP_FOLLOW|LOOKUP_ACCESS, &nd); if (res) goto out; inode = d_get_inode_ro(nd.dentry); res = vfs_permission(inode, &nd, mode); /* SuS v2 requires we report a read only fs too */ if(res || !(mode & S_IWOTH) || special_file(inode->i_mode)) goto out_path_release; if(IS_RDONLY(inode)) res = -EROFS; out_path_release: path_release(&nd); out: current->fsuid = old_fsuid; current->fsgid = old_fsgid; current->cap_effective = old_cap; return res; }
STATIC int xfs_attrmulti_attr_remove( struct inode *inode, char *name, __uint32_t flags) { if (IS_RDONLY(inode)) return -EROFS; if (IS_IMMUTABLE(inode) || IS_APPEND(inode)) return EPERM; return xfs_attr_remove(XFS_I(inode), name, flags); }
int jfs_ioctl(struct inode * inode, struct file * filp, unsigned int cmd, unsigned long arg) { struct jfs_inode_info *jfs_inode = JFS_IP(inode); unsigned int flags; switch (cmd) { case JFS_IOC_GETFLAGS: flags = jfs_inode->mode2 & JFS_FL_USER_VISIBLE; flags = jfs_map_ext2(flags, 0); return put_user(flags, (int __user *) arg); case JFS_IOC_SETFLAGS: { unsigned int oldflags; if (IS_RDONLY(inode)) return -EROFS; if ((current->fsuid != inode->i_uid) && !capable(CAP_FOWNER)) return -EACCES; if (get_user(flags, (int __user *) arg)) return -EFAULT; flags = jfs_map_ext2(flags, 1); if (!S_ISDIR(inode->i_mode)) flags &= ~JFS_DIRSYNC_FL; oldflags = jfs_inode->mode2; /* * The IMMUTABLE and APPEND_ONLY flags can only be changed by * the relevant capability. */ if ((oldflags & JFS_IMMUTABLE_FL) || ((flags ^ oldflags) & (JFS_APPEND_FL | JFS_IMMUTABLE_FL))) { if (!capable(CAP_LINUX_IMMUTABLE)) return -EPERM; } flags = flags & JFS_FL_USER_MODIFIABLE; flags |= oldflags & ~JFS_FL_USER_MODIFIABLE; jfs_inode->mode2 = flags; jfs_set_inode_flags(inode); inode->i_ctime = CURRENT_TIME_SEC; mark_inode_dirty(inode); return 0; } default: return -ENOTTY; } }
static int report_statvfs(struct vfsmount *mnt, struct inode *ip, struct sco_statvfs *bufp) { struct sco_statvfs buf; struct kstatfs s; int error; #if _KSL < 18 error = vfs_statfs(mnt->mnt_sb, &s); #else error = vfs_statfs(mnt->mnt_sb->s_root, &s); #endif if (error) return error; memset(&buf, 0, sizeof(struct sco_statvfs)); buf.f_bsize = s.f_bsize; buf.f_frsize = s.f_bsize; buf.f_blocks = s.f_blocks; buf.f_bfree = s.f_bfree; buf.f_bavail = s.f_bavail; buf.f_files = s.f_files; buf.f_free = s.f_ffree; buf.f_favail = s.f_ffree; /* SCO addition in the middle! */ buf.f_sid = ip->i_sb->s_dev; /* * Get the name of the filesystem. * * Sadly, some code "in the wild" actually checks the name * against a hard coded list to see if it is a "real" fs or not. * * I believe Informix Dynamic Server for SCO is one such. */ if (strncmp(ip->i_sb->s_type->name, "ext2", 4) == 0) strcpy(buf.f_basetype, "HTFS"); else strcpy(buf.f_basetype, ip->i_sb->s_type->name); /* Check for a few flags statvfs wants but statfs doesn't have. */ if (IS_RDONLY(ip)) buf.f_flag |= 1; if (mnt->mnt_flags & MNT_NOSUID) buf.f_flag |= 2; buf.f_namemax = s.f_namelen; if (copy_to_user(bufp, &buf, sizeof(struct sco_statvfs))) return -EFAULT; return 0; }
asmlinkage int sys_fchown(unsigned int fd, uid_t user, gid_t group) { struct inode * inode; struct file * file; struct iattr newattrs; int error; if (fd >= NR_OPEN || !(file = current->files->fd[fd])) return -EBADF; if (!(inode = file->f_inode)) return -ENOENT; if (IS_RDONLY(inode)) return -EROFS; if (IS_IMMUTABLE(inode) || IS_APPEND(inode)) return -EPERM; if (user == (uid_t) -1) user = inode->i_uid; if (group == (gid_t) -1) group = inode->i_gid; newattrs.ia_mode = inode->i_mode; newattrs.ia_uid = user; newattrs.ia_gid = group; newattrs.ia_valid = ATTR_UID | ATTR_GID | ATTR_CTIME; /* * If the owner has been changed, remove the setuid bit */ if (inode->i_mode & S_ISUID) { newattrs.ia_mode &= ~S_ISUID; newattrs.ia_valid |= ATTR_MODE; } /* * If the group has been changed, remove the setgid bit * * Don't remove the setgid bit if no group execute bit. * This is a file marked for mandatory locking. */ if (((inode->i_mode & (S_ISGID | S_IXGRP)) == (S_ISGID | S_IXGRP))) { newattrs.ia_mode &= ~S_ISGID; newattrs.ia_valid |= ATTR_MODE; } inode->i_dirt = 1; if (inode->i_sb && inode->i_sb->dq_op) { inode->i_sb->dq_op->initialize(inode, -1); if (inode->i_sb->dq_op->transfer(inode, &newattrs, 0)) return -EDQUOT; error = notify_change(inode, &newattrs); if (error) inode->i_sb->dq_op->transfer(inode, &newattrs, 1); } else error = notify_change(inode, &newattrs); return error; }
static long do_write(struct file *file, char *buf, size_t count, unsigned long *pos) { if (!(file->f_mode & O_WRITE)) return -EBADF; if (file->f_inode && IS_RDONLY(file->f_inode)) return -ENOSPC; if (!count) return 0; if (file->f_op && file->f_op->write) return file->f_op->write(file, buf, count, pos); return -EINVAL; }
struct chan *netifopen(struct ether *nif, struct chan *c, int omode) { int id; struct netfile *f; id = 0; if (c->qid.type & QTDIR) { if (!IS_RDONLY(omode)) error(Eperm); } else { switch (NETTYPE(c->qid.path)) { case Ndataqid: case Nctlqid: id = NETID(c->qid.path); openfile(nif, id); break; case Ncloneqid: id = openfile(nif, -1); c->qid.path = NETQID(id, Nctlqid); break; default: if (!IS_RDONLY(omode)) error(Ebadarg); } switch (NETTYPE(c->qid.path)) { case Ndataqid: case Nctlqid: f = nif->f[id]; if (netown(f, current->user, omode & 7) < 0) error(Eperm); break; } } c->mode = openmode(omode); c->flag |= COPEN; c->offset = 0; c->iounit = qiomaxatomic; return c; }
int handle_permission(struct inode *inode, int mask) { int submask; umode_t mode = inode->i_mode; if (mask & MAY_WRITE) { /* * Nobody gets write access to a read-only fs. */ if (IS_RDONLY(inode) && (S_ISREG(mode) || S_ISDIR(mode) || S_ISLNK(mode))) return -EROFS; /* * Nobody gets write access to an immutable file. */ if (IS_IMMUTABLE(inode)) return -EACCES; } /* * MAY_EXEC on regular files requires special handling: We override * filesystem execute permissions if the mode bits aren't set. */ if ((mask & MAY_EXEC) && S_ISREG(mode) && !(mode & S_IXUGO)) return -EACCES; /* Ordinary permission routines do not understand MAY_APPEND. */ submask = mask & ~MAY_APPEND; #ifdef IN_KERNEL_CHANGE_NOT_SUPP /* FIXME! we don't have nameidata for handle lookup. So not sure how * we can check for inode->permissions. We already limit the call * to CAP_DAC_OVERRIDE. So we should be able to skip the ACL check. * But we also skip security_inode_permission below. */ if (inode->i_op && inode->i_op->permission) retval = inode->i_op->permission(inode, submask, nd); else retval = generic_permission(inode, submask, NULL); if (retval) return retval; return security_inode_permission(inode, mask, nd); #else return generic_permission(inode, submask, NULL); #endif }
/* * test if the branch permission is legal or not. */ static int test_br(struct inode *inode, int brperm, char *path) { int err; err = (au_br_writable(brperm) && IS_RDONLY(inode)); if (!err) goto out; err = -EINVAL; pr_err("write permission for readonly mount or inode, %s\n", path); out: return err; }
struct chan *fdtochan(struct fgrp *f, int fd, int mode, int chkmnt, int iref) { struct chan *c; c = 0; spin_lock(&f->lock); if (f->closed) { spin_unlock(&f->lock); error("File group closed"); } if (fd < 0 || f->maxfd < fd || (c = f->fd[fd]) == 0) { spin_unlock(&f->lock); set_errno(EBADF); error("Bad FD %d\n", fd); } if (iref) chan_incref(c); spin_unlock(&f->lock); if (chkmnt && (c->flag & CMSG)) { if (iref) cclose(c); error(Ebadusefd); } if (mode < 0 || c->mode == ORDWR) { return c; } if ((mode & OTRUNC) && IS_RDONLY(c->mode)) { if (iref) cclose(c); error(Ebadusefd); } /* TODO: this is probably wrong. if you get this from a dev, in the dev's * open, you are probably saving mode directly, without passing it through * openmode. */ if ((mode & ~OTRUNC) != c->mode) { warn("Trunc mode issue: mode %o, mode minus trunc %o, chan mode %o\n", mode, mode & ~OTRUNC, c->mode); if (iref) cclose(c); error(Ebadusefd); } return c; }
/* Read a file into user space memory */ static int UMSDOS_file_read( struct inode *inode, struct file *filp, char *buf, int count) { /* We have to set the access time because msdos don't care */ int ret = msdos_file_read(inode,filp,buf,count); if (!IS_RDONLY(inode)){ inode->i_atime = CURRENT_TIME; inode->i_dirt = 1; } return ret; }