ioctl.c 5.1 KB
Newer Older
L
Linus Torvalds 已提交
1 2 3 4 5 6 7 8 9 10 11 12 13 14
/*
 *  linux/fs/hfsplus/ioctl.c
 *
 * Copyright (C) 2003
 * Ethan Benson <erbenson@alaska.net>
 * partially derived from linux/fs/ext2/ioctl.c
 * Copyright (C) 1993, 1994, 1995
 * Remy Card (card@masi.ibp.fr)
 * Laboratoire MASI - Institut Blaise Pascal
 * Universite Pierre et Marie Curie (Paris VI)
 *
 * hfsplus ioctls
 */

15
#include <linux/capability.h>
L
Linus Torvalds 已提交
16
#include <linux/fs.h>
17
#include <linux/mount.h>
L
Linus Torvalds 已提交
18 19 20 21 22
#include <linux/sched.h>
#include <linux/xattr.h>
#include <asm/uaccess.h>
#include "hfsplus_fs.h"

C
Christoph Hellwig 已提交
23
static int hfsplus_ioctl_getflags(struct file *file, int __user *user_flags)
L
Linus Torvalds 已提交
24
{
C
Christoph Hellwig 已提交
25
	struct inode *inode = file->f_path.dentry->d_inode;
26
	struct hfsplus_inode_info *hip = HFSPLUS_I(inode);
C
Christoph Hellwig 已提交
27 28
	unsigned int flags = 0;

29
	if (inode->i_flags & S_IMMUTABLE)
C
Christoph Hellwig 已提交
30
		flags |= FS_IMMUTABLE_FL;
31
	if (inode->i_flags & S_APPEND)
C
Christoph Hellwig 已提交
32
		flags |= FS_APPEND_FL;
33
	if (hip->userflags & HFSPLUS_FLG_NODUMP)
C
Christoph Hellwig 已提交
34 35 36 37 38 39 40 41
		flags |= FS_NODUMP_FL;

	return put_user(flags, user_flags);
}

static int hfsplus_ioctl_setflags(struct file *file, int __user *user_flags)
{
	struct inode *inode = file->f_path.dentry->d_inode;
42
	struct hfsplus_inode_info *hip = HFSPLUS_I(inode);
L
Linus Torvalds 已提交
43
	unsigned int flags;
C
Christoph Hellwig 已提交
44
	int err = 0;
L
Linus Torvalds 已提交
45

C
Christoph Hellwig 已提交
46 47
	err = mnt_want_write(file->f_path.mnt);
	if (err)
48
		goto out;
L
Linus Torvalds 已提交
49

C
Christoph Hellwig 已提交
50 51 52 53
	if (!is_owner_or_cap(inode)) {
		err = -EACCES;
		goto out_drop_write;
	}
L
Linus Torvalds 已提交
54

C
Christoph Hellwig 已提交
55 56 57 58 59
	if (get_user(flags, user_flags)) {
		err = -EFAULT;
		goto out_drop_write;
	}

60 61
	mutex_lock(&inode->i_mutex);

62 63
	if ((flags & (FS_IMMUTABLE_FL|FS_APPEND_FL)) ||
	    inode->i_flags & (S_IMMUTABLE|S_APPEND)) {
C
Christoph Hellwig 已提交
64 65
		if (!capable(CAP_LINUX_IMMUTABLE)) {
			err = -EPERM;
66
			goto out_unlock_inode;
L
Linus Torvalds 已提交
67 68
		}
	}
C
Christoph Hellwig 已提交
69 70 71 72

	/* don't silently ignore unsupported ext2 flags */
	if (flags & ~(FS_IMMUTABLE_FL|FS_APPEND_FL|FS_NODUMP_FL)) {
		err = -EOPNOTSUPP;
73
		goto out_unlock_inode;
C
Christoph Hellwig 已提交
74
	}
75 76

	if (flags & FS_IMMUTABLE_FL)
C
Christoph Hellwig 已提交
77
		inode->i_flags |= S_IMMUTABLE;
78
	else
C
Christoph Hellwig 已提交
79
		inode->i_flags &= ~S_IMMUTABLE;
80 81

	if (flags & FS_APPEND_FL)
C
Christoph Hellwig 已提交
82
		inode->i_flags |= S_APPEND;
83
	else
C
Christoph Hellwig 已提交
84
		inode->i_flags &= ~S_APPEND;
85

C
Christoph Hellwig 已提交
86
	if (flags & FS_NODUMP_FL)
87
		hip->userflags |= HFSPLUS_FLG_NODUMP;
C
Christoph Hellwig 已提交
88
	else
89
		hip->userflags &= ~HFSPLUS_FLG_NODUMP;
C
Christoph Hellwig 已提交
90 91 92 93

	inode->i_ctime = CURRENT_TIME_SEC;
	mark_inode_dirty(inode);

94
out_unlock_inode:
95
	mutex_unlock(&inode->i_mutex);
C
Christoph Hellwig 已提交
96 97
out_drop_write:
	mnt_drop_write(file->f_path.mnt);
98
out:
C
Christoph Hellwig 已提交
99 100 101 102 103 104 105 106 107 108 109 110
	return err;
}

long hfsplus_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
	void __user *argp = (void __user *)arg;

	switch (cmd) {
	case HFSPLUS_IOC_EXT2_GETFLAGS:
		return hfsplus_ioctl_getflags(file, argp);
	case HFSPLUS_IOC_EXT2_SETFLAGS:
		return hfsplus_ioctl_setflags(file, argp);
L
Linus Torvalds 已提交
111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127
	default:
		return -ENOTTY;
	}
}

int hfsplus_setxattr(struct dentry *dentry, const char *name,
		     const void *value, size_t size, int flags)
{
	struct inode *inode = dentry->d_inode;
	struct hfs_find_data fd;
	hfsplus_cat_entry entry;
	struct hfsplus_cat_file *file;
	int res;

	if (!S_ISREG(inode->i_mode) || HFSPLUS_IS_RSRC(inode))
		return -EOPNOTSUPP;

128
	res = hfs_find_init(HFSPLUS_SB(inode->i_sb)->cat_tree, &fd);
L
Linus Torvalds 已提交
129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149
	if (res)
		return res;
	res = hfsplus_find_cat(inode->i_sb, inode->i_ino, &fd);
	if (res)
		goto out;
	hfs_bnode_read(fd.bnode, &entry, fd.entryoffset,
			sizeof(struct hfsplus_cat_file));
	file = &entry.file;

	if (!strcmp(name, "hfs.type")) {
		if (size == 4)
			memcpy(&file->user_info.fdType, value, 4);
		else
			res = -ERANGE;
	} else if (!strcmp(name, "hfs.creator")) {
		if (size == 4)
			memcpy(&file->user_info.fdCreator, value, 4);
		else
			res = -ERANGE;
	} else
		res = -EOPNOTSUPP;
C
Christoph Hellwig 已提交
150
	if (!res) {
L
Linus Torvalds 已提交
151 152
		hfs_bnode_write(fd.bnode, &entry, fd.entryoffset,
				sizeof(struct hfsplus_cat_file));
C
Christoph Hellwig 已提交
153 154
		hfsplus_mark_inode_dirty(inode, HFSPLUS_I_CAT_DIRTY);
	}
L
Linus Torvalds 已提交
155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172
out:
	hfs_find_exit(&fd);
	return res;
}

ssize_t hfsplus_getxattr(struct dentry *dentry, const char *name,
			 void *value, size_t size)
{
	struct inode *inode = dentry->d_inode;
	struct hfs_find_data fd;
	hfsplus_cat_entry entry;
	struct hfsplus_cat_file *file;
	ssize_t res = 0;

	if (!S_ISREG(inode->i_mode) || HFSPLUS_IS_RSRC(inode))
		return -EOPNOTSUPP;

	if (size) {
173
		res = hfs_find_init(HFSPLUS_SB(inode->i_sb)->cat_tree, &fd);
L
Linus Torvalds 已提交
174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196
		if (res)
			return res;
		res = hfsplus_find_cat(inode->i_sb, inode->i_ino, &fd);
		if (res)
			goto out;
		hfs_bnode_read(fd.bnode, &entry, fd.entryoffset,
				sizeof(struct hfsplus_cat_file));
	}
	file = &entry.file;

	if (!strcmp(name, "hfs.type")) {
		if (size >= 4) {
			memcpy(value, &file->user_info.fdType, 4);
			res = 4;
		} else
			res = size ? -ERANGE : 4;
	} else if (!strcmp(name, "hfs.creator")) {
		if (size >= 4) {
			memcpy(value, &file->user_info.fdCreator, 4);
			res = 4;
		} else
			res = size ? -ERANGE : 4;
	} else
197
		res = -EOPNOTSUPP;
L
Linus Torvalds 已提交
198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221
out:
	if (size)
		hfs_find_exit(&fd);
	return res;
}

#define HFSPLUS_ATTRLIST_SIZE (sizeof("hfs.creator")+sizeof("hfs.type"))

ssize_t hfsplus_listxattr(struct dentry *dentry, char *buffer, size_t size)
{
	struct inode *inode = dentry->d_inode;

	if (!S_ISREG(inode->i_mode) || HFSPLUS_IS_RSRC(inode))
		return -EOPNOTSUPP;

	if (!buffer || !size)
		return HFSPLUS_ATTRLIST_SIZE;
	if (size < HFSPLUS_ATTRLIST_SIZE)
		return -ERANGE;
	strcpy(buffer, "hfs.type");
	strcpy(buffer + sizeof("hfs.type"), "hfs.creator");

	return HFSPLUS_ATTRLIST_SIZE;
}