Пример #1
0
static int
zpl_getattr(struct vfsmount *mnt, struct dentry *dentry, struct kstat *stat)
{
	boolean_t issnap = ITOZSB(dentry->d_inode)->z_issnap;
	int error;

	/*
	 * Ensure MNT_SHRINKABLE is set on snapshots to ensure they are
	 * unmounted automatically with the parent file system.  This
	 * is done on the first getattr because it's not easy to get the
	 * vfsmount structure at mount time.  This call path is explicitly
	 * marked unlikely to avoid any performance impact.  FWIW, ext4
	 * resorts to a similar trick for sysadmin convenience.
	 */
	if (unlikely(issnap && !(mnt->mnt_flags & MNT_SHRINKABLE)))
		mnt->mnt_flags |= MNT_SHRINKABLE;

	error = -zfs_getattr_fast(dentry->d_inode, stat);
	ASSERT3S(error, <=, 0);

	return (error);
}
Пример #2
0
/* ARGSUSED */
int
zfsctl_snapdir_remove(struct inode *dip, char *name, cred_t *cr, int flags)
{
	zfs_sb_t *zsb = ITOZSB(dip);
	char *snapname, *real;
	int error;

	ZFS_ENTER(zsb);

	snapname = kmem_alloc(MAXNAMELEN, KM_SLEEP);
	real = kmem_alloc(MAXNAMELEN, KM_SLEEP);

	if (zsb->z_case == ZFS_CASE_INSENSITIVE) {
		error = dmu_snapshot_realname(zsb->z_os, name, real,
		    MAXNAMELEN, NULL);
		if (error == 0) {
			name = real;
		} else if (error != ENOTSUP) {
			goto out;
		}
	}

	error = zfsctl_snapshot_zname(dip, name, MAXNAMELEN, snapname);
	if (!error)
		error = zfs_secpolicy_destroy_perms(snapname, cr);
	if (error)
		goto out;

	error = zfsctl_unmount_snapshot(zsb, name, MNT_FORCE);
	if ((error == 0) || (error == ENOENT))
		error = dmu_objset_destroy(snapname, B_FALSE);
out:
	kmem_free(snapname, MAXNAMELEN);
	kmem_free(real, MAXNAMELEN);

	ZFS_EXIT(zsb);

	return (error);
}
Пример #3
0
static int
__zpl_xattr_acl_set_default(struct inode *ip, const char *name,
    const void *value, size_t size, int flags)
{
	struct posix_acl *acl;
	int type = ACL_TYPE_DEFAULT;
	int error = 0;
	/* xattr_resolve_name will do this for us if this is defined */
#ifndef HAVE_XATTR_HANDLER_NAME
	if (strcmp(name, "") != 0)
		return (-EINVAL);
#endif
	if (ITOZSB(ip)->z_acl_type != ZFS_ACLTYPE_POSIXACL)
		return (-EOPNOTSUPP);

	if (!zpl_inode_owner_or_capable(ip))
		return (-EPERM);

	if (value) {
		acl = zpl_acl_from_xattr(value, size);
		if (IS_ERR(acl))
			return (PTR_ERR(acl));
		else if (acl) {
			error = zpl_posix_acl_valid(ip, acl);
			if (error) {
				zpl_posix_acl_release(acl);
				return (error);
			}
		}
	} else {
		acl = NULL;
	}

	error = zpl_set_acl(ip, acl, type);
	zpl_posix_acl_release(acl);

	return (error);
}
Пример #4
0
/*
 * When a .zfs/snapshot/<snapshot> inode is evicted they must be removed
 * from the snapshot list.  This will normally happen as part of the auto
 * unmount, however in the case of a manual snapshot unmount this will be
 * the only notification we receive.
 */
void
zfsctl_snapdir_inactive(struct inode *ip)
{
	zfs_sb_t *zsb = ITOZSB(ip);
	zfs_snapentry_t *sep, *next;

	mutex_enter(&zsb->z_ctldir_lock);

	sep = avl_first(&zsb->z_ctldir_snaps);
	while (sep != NULL) {
		next = AVL_NEXT(&zsb->z_ctldir_snaps, sep);

		if (sep->se_inode == ip) {
			avl_remove(&zsb->z_ctldir_snaps, sep);
			taskq_cancel_id(zfs_expire_taskq, sep->se_taskqid);
			zfsctl_sep_free(sep);
			break;
		}
		sep = next;
	}

	mutex_exit(&zsb->z_ctldir_lock);
}
Пример #5
0
int
zpl_chmod_acl(struct inode *ip)
{
	struct posix_acl *acl;
	int error;

	if (ITOZSB(ip)->z_acl_type != ZFS_ACLTYPE_POSIXACL)
		return (0);

	if (S_ISLNK(ip->i_mode))
		return (-EOPNOTSUPP);

	acl = zpl_get_acl(ip, ACL_TYPE_ACCESS);
	if (IS_ERR(acl) || !acl)
		return (PTR_ERR(acl));

	error = posix_acl_chmod(&acl,GFP_KERNEL, ip->i_mode);
	if (!error)
		error = zpl_set_acl(ip,ACL_TYPE_ACCESS, acl);

	zpl_posix_acl_release(acl);

	return (error);
}
Пример #6
0
static int
zpl_xattr_filldir(xattr_filldir_t *xf, const char *name, int name_len)
{
	if (!strncmp(name, XATTR_USER_PREFIX, XATTR_USER_PREFIX_LEN))
		if (!(ITOZSB(xf->inode)->z_flags & ZSB_XATTR))
			return (0);

	if (!strncmp(name, XATTR_TRUSTED_PREFIX, XATTR_TRUSTED_PREFIX_LEN))
		if (!capable(CAP_SYS_ADMIN))
			return (0);

	/* When xf->buf is NULL only calculate the required size. */
	if (xf->buf) {
		if (xf->offset + name_len + 1 > xf->size)
			return (-ERANGE);

		memcpy(xf->buf + xf->offset, name, name_len);
		xf->buf[xf->offset + name_len] = '\0';
	}

	xf->offset += (name_len + 1);

	return (0);
}
Пример #7
0
int
zfsctl_mount_snapshot(struct path *path, int flags)
{
	struct dentry *dentry = path->dentry;
	struct inode *ip = dentry->d_inode;
	zfs_sb_t *zsb = ITOZSB(ip);
	char *full_name, *full_path;
	zfs_snapentry_t *sep;
	zfs_snapentry_t search;
	char *argv[] = { "/bin/sh", "-c", NULL, NULL };
	char *envp[] = { NULL };
	int error;

	ZFS_ENTER(zsb);

	full_name = kmem_zalloc(MAXNAMELEN, KM_SLEEP);
	full_path = kmem_zalloc(PATH_MAX, KM_SLEEP);

	error = zfsctl_snapshot_zname(ip, dname(dentry), MAXNAMELEN, full_name);
	if (error)
		goto error;

	error = zfsctl_snapshot_zpath(path, PATH_MAX, full_path);
	if (error)
		goto error;

	/*
	 * Attempt to mount the snapshot from user space.  Normally this
	 * would be done using the vfs_kern_mount() function, however that
	 * function is marked GPL-only and cannot be used.  On error we
	 * careful to log the real error to the console and return EISDIR
	 * to safely abort the automount.  This should be very rare.
	 */
	argv[2] = kmem_asprintf(SET_MOUNT_CMD, full_name, full_path);
	error = call_usermodehelper(argv[0], argv, envp, UMH_WAIT_PROC);
	strfree(argv[2]);
	if (error) {
		printk("ZFS: Unable to automount %s at %s: %d\n",
		    full_name, full_path, error);
		error = EISDIR;
		goto error;
	}

	mutex_enter(&zsb->z_ctldir_lock);

	/*
	 * Ensure a previous entry does not exist, if it does safely remove
	 * it any cancel the outstanding expiration.  This can occur when a
	 * snapshot is manually unmounted and then an automount is triggered.
	 */
	search.se_name = full_name;
	sep = avl_find(&zsb->z_ctldir_snaps, &search, NULL);
	if (sep) {
		avl_remove(&zsb->z_ctldir_snaps, sep);
		taskq_cancel_id(zfs_expire_taskq, sep->se_taskqid);
		zfsctl_sep_free(sep);
	}

	sep = zfsctl_sep_alloc();
	sep->se_name = full_name;
	sep->se_path = full_path;
	sep->se_inode = ip;
	avl_add(&zsb->z_ctldir_snaps, sep);

	sep->se_taskqid = taskq_dispatch_delay(zfs_expire_taskq,
	    zfsctl_expire_snapshot, sep, TQ_SLEEP,
	    ddi_get_lbolt() + zfs_expire_snapshot * HZ);

	mutex_exit(&zsb->z_ctldir_lock);
error:
	if (error) {
		kmem_free(full_name, MAXNAMELEN);
		kmem_free(full_path, PATH_MAX);
	}

	ZFS_EXIT(zsb);

	return (error);
}
Пример #8
0
/*ARGSUSED*/
int
zfsctl_snapdir_rename(struct inode *sdip, char *sname,
    struct inode *tdip, char *tname, cred_t *cr, int flags)
{
	zfs_sb_t *zsb = ITOZSB(sdip);
	zfs_snapentry_t search, *sep;
	avl_index_t where;
	char *to, *from, *real;
	int error;

	ZFS_ENTER(zsb);

	to = kmem_alloc(MAXNAMELEN, KM_SLEEP);
	from = kmem_alloc(MAXNAMELEN, KM_SLEEP);
	real = kmem_alloc(MAXNAMELEN, KM_SLEEP);

	if (zsb->z_case == ZFS_CASE_INSENSITIVE) {
		error = dmu_snapshot_realname(zsb->z_os, sname, real,
		    MAXNAMELEN, NULL);
		if (error == 0) {
			sname = real;
		} else if (error != ENOTSUP) {
			goto out;
		}
	}

	error = zfsctl_snapshot_zname(sdip, sname, MAXNAMELEN, from);
	if (!error)
		error = zfsctl_snapshot_zname(tdip, tname, MAXNAMELEN, to);
	if (!error)
		error = zfs_secpolicy_rename_perms(from, to, cr);
	if (error)
		goto out;

	/*
	 * Cannot move snapshots out of the snapdir.
	 */
	if (sdip != tdip) {
		error = EINVAL;
		goto out;
	}

	/*
	 * No-op when names are identical.
	 */
	if (strcmp(sname, tname) == 0) {
		error = 0;
		goto out;
	}

	mutex_enter(&zsb->z_ctldir_lock);

	error = dmu_objset_rename(from, to, B_FALSE);
	if (error)
		goto out_unlock;

	search.se_name = (char *)sname;
	sep = avl_find(&zsb->z_ctldir_snaps, &search, &where);
	if (sep)
		zfsctl_rename_snap(zsb, sep, tname);

out_unlock:
	mutex_exit(&zsb->z_ctldir_lock);
out:
	kmem_free(from, MAXNAMELEN);
	kmem_free(to, MAXNAMELEN);
	kmem_free(real, MAXNAMELEN);

	ZFS_EXIT(zsb);

	return (error);
}
Пример #9
0
int
zpl_set_acl(struct inode *ip, int type, struct posix_acl *acl)
{
	struct super_block *sb = ITOZSB(ip)->z_sb;
	char *name, *value = NULL;
	int error = 0;
	size_t size = 0;

	if (S_ISLNK(ip->i_mode))
		return (-EOPNOTSUPP);

	switch(type) {
	case ACL_TYPE_ACCESS:
		name = POSIX_ACL_XATTR_ACCESS;
		if (acl) {
			zpl_equivmode_t mode = ip->i_mode;
			error = posix_acl_equiv_mode(acl, &mode);
			if (error < 0) {
				return (error);
			} else {
				/*
				 * The mode bits will have been set by
				 * ->zfs_setattr()->zfs_acl_chmod_setattr()
				 * using the ZFS ACL conversion.  If they
				 * differ from the Posix ACL conversion dirty
				 * the inode to write the Posix mode bits.
				 */
				if (ip->i_mode != mode) {
					ip->i_mode = mode;
					ip->i_ctime = current_fs_time(sb);
					mark_inode_dirty(ip);
				}

				if (error == 0)
					acl = NULL;
			}
		}
		break;

	case ACL_TYPE_DEFAULT:
		name = POSIX_ACL_XATTR_DEFAULT;
		if (!S_ISDIR(ip->i_mode))
			return (acl ? -EACCES : 0);
		break;

	default:
		return (-EINVAL);
	}

	if (acl) {
		size = posix_acl_xattr_size(acl->a_count);
		value = kmem_alloc(size, KM_SLEEP);

		error = zpl_acl_to_xattr(acl, value, size);
		if (error < 0) {
			kmem_free(value, size);
			return (error);
		}
	}

	error = zpl_xattr_set(ip, name, value, size, 0);
	if (value)
		kmem_free(value, size);

	if (!error) {
		if (acl)
			zpl_set_cached_acl(ip, type, acl);
		else
			zpl_forget_cached_acl(ip, type);
	}

	return (error);
}
Пример #10
0
/*
 * Extended user attributes
 *
 * "Extended user attributes may be assigned to files and directories for
 * storing arbitrary additional information such as the mime type,
 * character set or encoding of a file.  The access permissions for user
 * attributes are defined by the file permission bits: read permission
 * is required to retrieve the attribute value, and writer permission is
 * required to change it.
 *
 * The file permission bits of regular files and directories are
 * interpreted differently from the file permission bits of special
 * files and symbolic links.  For regular files and directories the file
 * permission bits define access to the file's contents, while for
 * device special files they define access to the device described by
 * the special file.  The file permissions of symbolic links are not
 * used in access checks.  These differences would allow users to
 * consume filesystem resources in a way not controllable by disk quotas
 * for group or world writable special files and directories.
 *
 * For this reason, extended user attributes are allowed only for
 * regular files and directories, and access to extended user attributes
 * is restricted to the owner and to users with appropriate capabilities
 * for directories with the sticky bit set (see the chmod(1) manual page
 * for an explanation of the sticky bit)." - xattr(7)
 *
 * ZFS allows extended user attributes to be disabled administratively
 * by setting the 'xattr=off' property on the dataset.
 */
static int
__zpl_xattr_user_list(struct inode *ip, char *list, size_t list_size,
    const char *name, size_t name_len)
{
	return (ITOZSB(ip)->z_flags & ZSB_XATTR);
}