示例#1
0
文件: dir.c 项目: 1x23/unifi-gpl
/*
 * Check permission.  The two basic access models of FUSE are:
 *
 * 1) Local access checking ('default_permissions' mount option) based
 * on file mode.  This is the plain old disk filesystem permission
 * modell.
 *
 * 2) "Remote" access checking, where server is responsible for
 * checking permission in each inode operation.  An exception to this
 * is if ->permission() was invoked from sys_access() in which case an
 * access request is sent.  Execute permission is still checked
 * locally based on file mode.
 */
static int fuse_permission(struct inode *inode, int mask, struct nameidata *nd)
{
	struct fuse_conn *fc = get_fuse_conn(inode);

	if (!fuse_allow_task(fc, current))
		return -EACCES;
	else if (fc->flags & FUSE_DEFAULT_PERMISSIONS) {
#ifdef KERNEL_2_6_10_PLUS
		int err = generic_permission(inode, mask, NULL);
#else
		int err = vfs_permission(inode, mask);
#endif

		/* If permission is denied, try to refresh file
		   attributes.  This is also needed, because the root
		   node will at first have no permissions */
		if (err == -EACCES) {
		 	err = fuse_do_getattr(inode);
			if (!err)
#ifdef KERNEL_2_6_10_PLUS
				err = generic_permission(inode, mask, NULL);
#else
				err = vfs_permission(inode, mask);
#endif
		}

		/* Note: the opposite of the above test does not
		   exist.  So if permissions are revoked this won't be
		   noticed immediately, only after the attribute
		   timeout has expired */

		return err;
	} else {
		int mode = inode->i_mode;
#ifndef KERNEL_2_6_11_PLUS
		if ((mask & MAY_WRITE) && IS_RDONLY(inode) &&
                    (S_ISREG(mode) || S_ISDIR(mode) || S_ISLNK(mode)))
                        return -EROFS;
#endif
		if ((mask & MAY_EXEC) && !S_ISDIR(mode) && !(mode & S_IXUGO))
			return -EACCES;

#ifndef LOOKUP_CHDIR
#define LOOKUP_CHDIR 0
#endif
		if (nd && (nd->flags & (LOOKUP_ACCESS | LOOKUP_CHDIR)))
			return fuse_access(inode, mask);
		return 0;
	}
}
示例#2
0
/* Basically copied from the kernel vfs permission(), but we've changed
 * the following: (1) the IS_RDONLY check is skipped, and (2) if you set
 * the mount option `nfsperms=insceure', we assume that -EACCES means that
 * the export is read-only and we should check standard Unix permissions.
 * This means that NFS ACL checks (or other advanced permission features)
 * are bypassed.
 */
static int inode_permission(struct inode *inode, int mask, struct nameidata *nd,
			    int bindex)
{
	int retval, submask;

	if (mask & MAY_WRITE) {
		/* The first branch is allowed to be really readonly. */
		if (bindex == 0) {
			umode_t mode = inode->i_mode;
			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;
	}

	/* Ordinary permission routines do not understand MAY_APPEND. */
	submask = mask & ~MAY_APPEND;
	if (inode->i_op && inode->i_op->permission) {
		retval = inode->i_op->permission(inode, submask, nd);
		if ((retval == -EACCES) && (submask & MAY_WRITE) &&
		    (!strcmp("nfs", (inode)->i_sb->s_type->name)) &&
		    (nd) && (nd->mnt) && (nd->mnt->mnt_sb) &&
		    (branchperms(nd->mnt->mnt_sb, bindex) & MAY_NFSRO)) {
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,10)
			retval = vfs_permission(inode, submask);
#else
			retval = generic_permission(inode, submask, NULL);
#endif
		}
	} else {
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,10)
		retval = vfs_permission(inode, submask);
#else
		retval = generic_permission(inode, submask, NULL);
#endif
	}
	
	if (retval && retval != -EROFS) /* ignore EROFS */
		return retval;

	retval = security_inode_permission(inode, mask, nd);
	return ((retval == -EROFS) ? 0 : retval); /* ignore EROFS */
}
示例#3
0
int
nfs_permission(struct inode *inode, int mask)
{
	int			error = vfs_permission(inode, mask);

	if (!NFS_PROTO(inode)->access)
		goto out;

	if (error == -EROFS)
		goto out;

	/*
	 * Trust UNIX mode bits except:
	 *
	 * 1) When override capabilities may have been invoked
	 * 2) When root squashing may be involved
	 * 3) When ACLs may overturn a negative answer */
	if (!capable(CAP_DAC_OVERRIDE) && !capable(CAP_DAC_READ_SEARCH)
	    && (current->fsuid != 0) && (current->fsgid != 0)
	    && error != -EACCES)
		goto out;

	error = NFS_PROTO(inode)->access(inode, mask, 0);

	if (error == -EACCES && NFS_CLIENT(inode)->cl_droppriv &&
	    current->uid != 0 && current->gid != 0 &&
	    (current->fsuid != current->uid || current->fsgid != current->gid))
		error = NFS_PROTO(inode)->access(inode, mask, 1);

 out:
	return error;
}
示例#4
0
文件: open.c 项目: 274914765/C
asmlinkage long sys_chroot(const char __user * filename)
{
    struct nameidata nd;
    int error;

    error = __user_walk(filename, LOOKUP_FOLLOW | LOOKUP_DIRECTORY | LOOKUP_NOALT, &nd);
    if (error)
        goto out;

    error = vfs_permission(&nd, MAY_EXEC);
    if (error)
        goto dput_and_out;

    error = -EPERM;
    if (!capable(CAP_SYS_CHROOT))
        goto dput_and_out;

    set_fs_root(current->fs, &nd.path);
    set_fs_altroot();
    error = 0;
dput_and_out:
    path_put(&nd.path);
out:
    return error;
}
示例#5
0
asmlinkage long sys_chroot(const char __user * filename)
{
	struct nameidata nd;
	struct fs_struct *fs;
	int error;

	error = __user_walk(filename, LOOKUP_FOLLOW | LOOKUP_DIRECTORY | LOOKUP_NOALT, &nd);
	if (error)
		goto out;

	error = vfs_permission(d_get_inode_ro(nd.dentry), &nd, MAY_EXEC);
	if (error)
		goto dput_and_out;

	error = -EPERM;
	if (!capable(CAP_SYS_CHROOT))
		goto dput_and_out;

	fs = tx_cache_get_fs(current);

	set_fs_root(fs, nd.mnt, parent(nd.dentry));
	set_fs_altroot();
	error = 0;
dput_and_out:
	path_release(&nd);
out:
	return error;
}
示例#6
0
asmlinkage long sys_fchdir(unsigned int fd)
{
	struct nameidata nd;
	struct file *file;
	int error;

	error = -EBADF;
	file = fget(fd);
	if (!file)
		goto out;

	nd.dentry = file->f_path.dentry;
	nd.mnt = file->f_path.mnt;
	nd.flags = 0;

	error = -ENOTDIR;
	if (!S_ISDIR(nd.dentry->d_inode->i_mode))
		goto out_putf;

	error = vfs_permission(&nd, MAY_EXEC);
	if (!error)
		set_fs_pwd(current->fs, nd.mnt, nd.dentry);
out_putf:
	fput(file);
out:
	return error;
}
示例#7
0
static long do_sys_truncate(const char __user * 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 = d_get_inode(nd.dentry);

	/* 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 = vfs_permission(inode, &nd, 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 = break_lease(parent(inode), FMODE_WRITE);
	if (error)
		goto dput_and_out;

	error = get_write_access(parent(inode));
	if (error)
		goto dput_and_out;

	error = locks_verify_truncate(parent(inode), NULL, length);
	if (!error) {
		DQUOT_INIT(inode);
		error = do_truncate(nd.dentry, length, 0, NULL);
	}
	put_write_access(parent(inode));

dput_and_out:
	path_release(&nd);
out:
	return error;
}
示例#8
0
/*
 * Note that a shared library must be both readable and executable due to
 * security reasons.
 *
 * Also note that we take the address to load from from the file itself.
 */
asmlinkage long sys_uselib(const char __user * library)
{
	struct file * file;
	struct nameidata nd;
	const struct _inode *inode;
	int error;

	error = __user_path_lookup_open(library, LOOKUP_FOLLOW, &nd, FMODE_READ|FMODE_EXEC);
	if (error)
		goto out;

	error = -EACCES;
	if (nd.mnt->mnt_flags & MNT_NOEXEC)
		goto exit;
	error = -EINVAL;
	inode = d_get_inode_ro(nd.dentry);
	if (!S_ISREG(inode->i_mode))
		goto exit;

	error = vfs_permission(inode, &nd, MAY_READ | MAY_EXEC);
	if (error)
		goto exit;

	file = nameidata_to_filp(&nd, O_RDONLY);
	error = PTR_ERR(file);
	if (IS_ERR(file))
		goto out;

	error = -ENOEXEC;
	if(file->f_op) {
		struct linux_binfmt * fmt;

		read_lock(&binfmt_lock);
		for (fmt = formats ; fmt ; fmt = fmt->next) {
			if (!fmt->load_shlib)
				continue;
			if (!try_module_get(fmt->module))
				continue;
			read_unlock(&binfmt_lock);
			error = fmt->load_shlib(file);
			read_lock(&binfmt_lock);
			put_binfmt(fmt);
			if (error != -ENOEXEC)
				break;
		}
		read_unlock(&binfmt_lock);
	}
	fput(file);
out:
  	return error;
exit:
	release_open_intent(&nd);
	path_release(&nd);
	goto out;
}
示例#9
0
/*
 * 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;
}
示例#10
0
/* 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 long sys_utime(char __user * filename, struct utimbuf __user * times)
{
	int error;
	struct nameidata nd;
	struct inode * inode;
	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;

	/* 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;

		error = get_user(newattrs.ia_atime.tv_sec, &times->actime);
		newattrs.ia_atime.tv_nsec = 0;
		if (!error)
			error = get_user(newattrs.ia_mtime.tv_sec, &times->modtime);
		newattrs.ia_mtime.tv_nsec = 0;
		if (error)
			goto dput_and_out;

		newattrs.ia_valid |= ATTR_ATIME_SET | ATTR_MTIME_SET;
	} else {
                error = -EACCES;
                if (IS_IMMUTABLE(inode))
                        goto dput_and_out;

		if (current->fsuid != inode->i_uid &&
		    (error = vfs_permission(&nd, MAY_WRITE)) != 0)
			goto dput_and_out;
	}
	mutex_lock(&inode->i_mutex);
	error = notify_change(nd.dentry, &newattrs);
	mutex_unlock(&inode->i_mutex);
dput_and_out:
	path_release(&nd);
out:
	return error;
}
示例#11
0
/* 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.
 */
long do_utimes(int dfd, char __user *filename, struct timeval *times)
{
	int error;
	struct nameidata nd;
	struct inode * inode;
	struct iattr newattrs;

	error = __user_walk_fd(dfd, filename, LOOKUP_FOLLOW, &nd);

	if (error)
		goto out;
	inode = nd.dentry->d_inode;

	error = -EROFS;
	if (IS_RDONLY(inode))
		goto dput_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;

		newattrs.ia_atime.tv_sec = times[0].tv_sec;
		newattrs.ia_atime.tv_nsec = times[0].tv_usec * 1000;
		newattrs.ia_mtime.tv_sec = times[1].tv_sec;
		newattrs.ia_mtime.tv_nsec = times[1].tv_usec * 1000;
		newattrs.ia_valid |= ATTR_ATIME_SET | ATTR_MTIME_SET;
	} else {
		error = -EACCES;
                if (IS_IMMUTABLE(inode))
                        goto dput_and_out;

		if (current->fsuid != inode->i_uid &&
		    (error = vfs_permission(&nd, MAY_WRITE)) != 0)
			goto dput_and_out;
	}
	mutex_lock(&inode->i_mutex);
	error = notify_change(nd.dentry, &newattrs);
	mutex_unlock(&inode->i_mutex);
dput_and_out:
	path_release(&nd);
out:
	return error;
}
示例#12
0
extern int
vnode_shadow_iop_permission(
    INODE_T *inode,
    int mask
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,27)
    , struct nameidata *nd
#endif
#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,32)
    , unsigned int flags
#endif
)
{
    INODE_T *real_inode;
    DENT_T *dp;
    int err;

    /* we don't have multiple dentries per shadow inode, so this is OK */
    dp = vnlayer_inode2dentry_internal(inode, NULL, NULL, NULL);
    ASSERT(dp != NULL);
    real_inode = REALDENTRY(dp)->d_inode;

    if (real_inode == NULL) {        /* just in case; not expected! */
        VNODE_DPUT(dp);
        return -EACCES;
    }
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,27)
    err = permission(real_inode, mask, nd);
#else
    err = inode_permission(real_inode, mask);
#endif

    if (err == 0) {
        /* don't call permission() on inode, it will call back to us! */
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,10)
        err = vfs_permission(inode, mask);
#elif LINUX_VERSION_CODE < KERNEL_VERSION(2,6,38)
        err = generic_permission(inode, mask, NULL);
#else
        err = generic_permission(inode, mask, flags, NULL);
#endif
    }
    VNODE_DPUT(dp);
    return(err);
}
示例#13
0
asmlinkage long sys_chdir(const char __user * filename)
{
	struct nameidata nd;
	int error;

	error = __user_walk(filename, LOOKUP_FOLLOW|LOOKUP_DIRECTORY, &nd);
	if (error)
		goto out;

	error = vfs_permission(&nd, MAY_EXEC);
	if (error)
		goto dput_and_out;

	set_fs_pwd(current->fs, nd.mnt, nd.dentry);

dput_and_out:
	path_release(&nd);
out:
	return error;
}
示例#14
0
asmlinkage long sys_chdir(const char __user * filename)
{
	struct nameidata nd;
	int error;
	struct fs_struct *fs;

	error = __user_walk(filename,
			    LOOKUP_FOLLOW|LOOKUP_DIRECTORY|LOOKUP_CHDIR, &nd);
	if (error)
		goto out;

	error = vfs_permission(d_get_inode_ro(nd.dentry), &nd, MAY_EXEC);
	if (error)
		goto dput_and_out;
	
	fs = tx_cache_get_fs(current);
	
	set_fs_pwd(fs, nd.mnt, parent(nd.dentry));

dput_and_out:
	path_release(&nd);
out:
	return error;
}
int
nfs_permission(struct inode *inode, int mask)
{
	struct nfs_server *server = NFS_SERVER(inode);
	struct nfs_access_cache *cache = &NFS_I(inode)->cache_access;
	struct rpc_cred *cred;
	int mode = inode->i_mode;
	int error;

	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;
	}

	if ((server->flags & NFS_MOUNT_NOACL) || !NFS_PROTO(inode)->access)
		goto out_notsup;
	cred = rpcauth_lookupcred(NFS_CLIENT(inode)->cl_auth, 0);
	if (cache->cred == cred
	    && time_before(jiffies, cache->jiffies + NFS_ATTRTIMEO(inode))) {
		if (!cache->err) {
			/* Is the mask a subset of an accepted mask? */
			if ((cache->mask & mask) == mask)
				goto out_cached;
	       	} else {
			/* ...or is it a superset of a rejected mask? */
			if ((cache->mask & mask) == cache->mask)
				goto out_cached;
		}
	}
	error = NFS_PROTO(inode)->access(inode, cred, mask);
	if (!error || error == -EACCES) {
		cache->jiffies = jiffies;
		if (cache->cred)
			put_rpccred(cache->cred);
		cache->cred = cred;
		cache->mask = mask;
		cache->err = error;
		return error;
	}
	put_rpccred(cred);
out_notsup:
	nfs_revalidate_inode(NFS_SERVER(inode), inode);
	return vfs_permission(inode, mask);
out_cached:
	put_rpccred(cred);
	return cache->err;
}
示例#16
0
文件: open.c 项目: 274914765/C
static long do_sys_truncate(const char __user * 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.path.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 = mnt_want_write(nd.path.mnt);
    if (error)
        goto dput_and_out;

    error = vfs_permission(&nd, MAY_WRITE);
    if (error)
        goto mnt_drop_write_and_out;

    error = -EPERM;
    if (IS_IMMUTABLE(inode) || IS_APPEND(inode))
        goto mnt_drop_write_and_out;

    error = get_write_access(inode);
    if (error)
        goto mnt_drop_write_and_out;

    /*
     * Make sure that there are no leases.  get_write_access() protects
     * against the truncate racing with a lease-granting setlease().
     */
    error = break_lease(inode, FMODE_WRITE);
    if (error)
        goto put_write_and_out;

    error = locks_verify_truncate(inode, NULL, length);
    if (!error) {
        DQUOT_INIT(inode);
        error = do_truncate(nd.path.dentry, length, 0, NULL);
    }

put_write_and_out:
    put_write_access(inode);
mnt_drop_write_and_out:
    mnt_drop_write(nd.path.mnt);
dput_and_out:
    path_put(&nd.path);
out:
    return error;
}
示例#17
0
文件: open.c 项目: 274914765/C
/*
 * 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 uninitialized_var(old_cap);  /* !SECURE_NO_SETUID_FIXUP */
    int res;

    if (mode & ~S_IRWXO)    /* where's F_OK, X_OK, W_OK, R_OK? */
        return -EINVAL;

    old_fsuid = current->fsuid;
    old_fsgid = current->fsgid;

    current->fsuid = current->uid;
    current->fsgid = current->gid;

    if (!issecure(SECURE_NO_SETUID_FIXUP)) {
        /*
         * Clear the capabilities if we switch to a non-root user
         */
#ifndef CONFIG_SECURITY_FILE_CAPABILITIES
        /*
         * 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.
         */
#endif /* ndef CONFIG_SECURITY_FILE_CAPABILITIES */
        if (current->uid)
            old_cap = cap_set_effective(__cap_empty_set);
        else
            old_cap = cap_set_effective(current->cap_permitted);
    }

    res = __user_walk_fd(dfd, filename, LOOKUP_FOLLOW|LOOKUP_ACCESS, &nd);
    if (res)
        goto out;

    res = vfs_permission(&nd, mode);
    /* SuS v2 requires we report a read only fs too */
    if(res || !(mode & S_IWOTH) ||
       special_file(nd.path.dentry->d_inode->i_mode))
        goto out_path_release;
    /*
     * This is a rare case where using __mnt_is_readonly()
     * is OK without a mnt_want/drop_write() pair.  Since
     * no actual write to the fs is performed here, we do
     * not need to telegraph to that to anyone.
     *
     * By doing this, we accept that this access is
     * inherently racy and know that the fs may change
     * state before we even see this result.
     */
    if (__mnt_is_readonly(nd.path.mnt))
        res = -EROFS;

out_path_release:
    path_put(&nd.path);
out:
    current->fsuid = old_fsuid;
    current->fsgid = old_fsgid;

    if (!issecure(SECURE_NO_SETUID_FIXUP))
        cap_set_effective(old_cap);

    return res;
}
示例#18
0
int
nfs_permission(struct inode *inode, int mask, struct nameidata *nd)
{
	struct nfs_access_cache *cache = &NFS_I(inode)->cache_access;
	struct rpc_cred *cred;
	int mode = inode->i_mode;
	int res;

	if (mask == 0)
		return 0;
	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;
	}
	/* Are we checking permissions on anything other than lookup/execute? */
	if ((mask & MAY_EXEC) == 0) {
		/* We only need to check permissions on file open() and access() */
		if (!nd || !(nd->flags & (LOOKUP_OPEN|LOOKUP_ACCESS)))
			return 0;
		/* NFSv4 has atomic_open... */
		if (NFS_PROTO(inode)->version > 3 && (nd->flags & LOOKUP_OPEN))
			return 0;
	}

	lock_kernel();

	if (!NFS_PROTO(inode)->access)
		goto out_notsup;

	cred = rpcauth_lookupcred(NFS_CLIENT(inode)->cl_auth, 0);
	if (cache->cred == cred
	    && time_before(jiffies, cache->jiffies + NFS_ATTRTIMEO(inode))
	    && !(NFS_FLAGS(inode) & NFS_INO_INVALID_ATTR)) {
		if (!(res = cache->err)) {
			/* Is the mask a subset of an accepted mask? */
			if ((cache->mask & mask) == mask)
				goto out;
		} else {
			/* ...or is it a superset of a rejected mask? */
			if ((cache->mask & mask) == cache->mask)
				goto out;
		}
	}

	res = NFS_PROTO(inode)->access(inode, cred, mask);
	if (!res || res == -EACCES)
		goto add_cache;
out:
	put_rpccred(cred);
	unlock_kernel();
	return res;
out_notsup:
	nfs_revalidate_inode(NFS_SERVER(inode), inode);
	res = vfs_permission(inode, mask);
	unlock_kernel();
	return res;
add_cache:
	cache->jiffies = jiffies;
	if (cache->cred)
		put_rpccred(cache->cred);
	cache->cred = cred;
	cache->mask = mask;
	cache->err = res;
	unlock_kernel();
	return res;
}
示例#19
0
/* 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.
 */
long do_utimes(int dfd, char __user *filename, struct timespec *times, int flags)
{
	int error;
	struct nameidata nd;
	struct dentry *dentry;
	struct inode *inode;
	struct iattr newattrs;
	struct file *f = NULL;

	error = -EINVAL;
	if (times && (!nsec_valid(times[0].tv_nsec) ||
		      !nsec_valid(times[1].tv_nsec))) {
		goto out;
	}

	if (flags & ~AT_SYMLINK_NOFOLLOW)
		goto out;

	if (filename == NULL && dfd != AT_FDCWD) {
		error = -EINVAL;
		if (flags & AT_SYMLINK_NOFOLLOW)
			goto out;

		error = -EBADF;
		f = fget(dfd);
		if (!f)
			goto out;
		dentry = f->f_path.dentry;
	} else {
		error = __user_walk_fd(dfd, filename, (flags & AT_SYMLINK_NOFOLLOW) ? 0 : LOOKUP_FOLLOW, &nd);
		if (error)
			goto out;

		dentry = nd.dentry;
	}

	inode = dentry->d_inode;

	error = -EROFS;
	if (IS_RDONLY(inode))
		goto dput_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;

		if (times[0].tv_nsec == UTIME_OMIT)
			newattrs.ia_valid &= ~ATTR_ATIME;
		else if (times[0].tv_nsec != UTIME_NOW) {
			newattrs.ia_atime.tv_sec = times[0].tv_sec;
			newattrs.ia_atime.tv_nsec = times[0].tv_nsec;
			newattrs.ia_valid |= ATTR_ATIME_SET;
		}

		if (times[1].tv_nsec == UTIME_OMIT)
			newattrs.ia_valid &= ~ATTR_MTIME;
		else if (times[1].tv_nsec != UTIME_NOW) {
			newattrs.ia_mtime.tv_sec = times[1].tv_sec;
			newattrs.ia_mtime.tv_nsec = times[1].tv_nsec;
			newattrs.ia_valid |= ATTR_MTIME_SET;
		}
	} else {
		error = -EACCES;
                if (IS_IMMUTABLE(inode))
                        goto dput_and_out;

		if (!is_owner_or_cap(inode)) {
			if (f) {
				if (!(f->f_mode & FMODE_WRITE))
					goto dput_and_out;
			} else {
				error = vfs_permission(&nd, MAY_WRITE);
				if (error)
					goto dput_and_out;
			}
		}
	}
	mutex_lock(&inode->i_mutex);
	error = notify_change(dentry, &newattrs);
	mutex_unlock(&inode->i_mutex);
dput_and_out:
	if (f)
		fput(f);
	else
		path_release(&nd);
out:
	return error;
}