xref: /linux/fs/hfsplus/ioctl.c (revision 8fa5723aa7e053d498336b48448b292fc2e0458b)
1 /*
2  *  linux/fs/hfsplus/ioctl.c
3  *
4  * Copyright (C) 2003
5  * Ethan Benson <erbenson@alaska.net>
6  * partially derived from linux/fs/ext2/ioctl.c
7  * Copyright (C) 1993, 1994, 1995
8  * Remy Card (card@masi.ibp.fr)
9  * Laboratoire MASI - Institut Blaise Pascal
10  * Universite Pierre et Marie Curie (Paris VI)
11  *
12  * hfsplus ioctls
13  */
14 
15 #include <linux/capability.h>
16 #include <linux/fs.h>
17 #include <linux/mount.h>
18 #include <linux/sched.h>
19 #include <linux/xattr.h>
20 #include <asm/uaccess.h>
21 #include "hfsplus_fs.h"
22 
23 int hfsplus_ioctl(struct inode *inode, struct file *filp, unsigned int cmd,
24 		  unsigned long arg)
25 {
26 	unsigned int flags;
27 
28 	switch (cmd) {
29 	case HFSPLUS_IOC_EXT2_GETFLAGS:
30 		flags = 0;
31 		if (HFSPLUS_I(inode).rootflags & HFSPLUS_FLG_IMMUTABLE)
32 			flags |= FS_IMMUTABLE_FL; /* EXT2_IMMUTABLE_FL */
33 		if (HFSPLUS_I(inode).rootflags & HFSPLUS_FLG_APPEND)
34 			flags |= FS_APPEND_FL; /* EXT2_APPEND_FL */
35 		if (HFSPLUS_I(inode).userflags & HFSPLUS_FLG_NODUMP)
36 			flags |= FS_NODUMP_FL; /* EXT2_NODUMP_FL */
37 		return put_user(flags, (int __user *)arg);
38 	case HFSPLUS_IOC_EXT2_SETFLAGS: {
39 		int err = 0;
40 		err = mnt_want_write(filp->f_path.mnt);
41 		if (err)
42 			return err;
43 
44 		if (!is_owner_or_cap(inode)) {
45 			err = -EACCES;
46 			goto setflags_out;
47 		}
48 		if (get_user(flags, (int __user *)arg)) {
49 			err = -EFAULT;
50 			goto setflags_out;
51 		}
52 		if (flags & (FS_IMMUTABLE_FL|FS_APPEND_FL) ||
53 		    HFSPLUS_I(inode).rootflags & (HFSPLUS_FLG_IMMUTABLE|HFSPLUS_FLG_APPEND)) {
54 			if (!capable(CAP_LINUX_IMMUTABLE)) {
55 				err = -EPERM;
56 				goto setflags_out;
57 			}
58 		}
59 
60 		/* don't silently ignore unsupported ext2 flags */
61 		if (flags & ~(FS_IMMUTABLE_FL|FS_APPEND_FL|FS_NODUMP_FL)) {
62 			err = -EOPNOTSUPP;
63 			goto setflags_out;
64 		}
65 		if (flags & FS_IMMUTABLE_FL) { /* EXT2_IMMUTABLE_FL */
66 			inode->i_flags |= S_IMMUTABLE;
67 			HFSPLUS_I(inode).rootflags |= HFSPLUS_FLG_IMMUTABLE;
68 		} else {
69 			inode->i_flags &= ~S_IMMUTABLE;
70 			HFSPLUS_I(inode).rootflags &= ~HFSPLUS_FLG_IMMUTABLE;
71 		}
72 		if (flags & FS_APPEND_FL) { /* EXT2_APPEND_FL */
73 			inode->i_flags |= S_APPEND;
74 			HFSPLUS_I(inode).rootflags |= HFSPLUS_FLG_APPEND;
75 		} else {
76 			inode->i_flags &= ~S_APPEND;
77 			HFSPLUS_I(inode).rootflags &= ~HFSPLUS_FLG_APPEND;
78 		}
79 		if (flags & FS_NODUMP_FL) /* EXT2_NODUMP_FL */
80 			HFSPLUS_I(inode).userflags |= HFSPLUS_FLG_NODUMP;
81 		else
82 			HFSPLUS_I(inode).userflags &= ~HFSPLUS_FLG_NODUMP;
83 
84 		inode->i_ctime = CURRENT_TIME_SEC;
85 		mark_inode_dirty(inode);
86 setflags_out:
87 		mnt_drop_write(filp->f_path.mnt);
88 		return err;
89 	}
90 	default:
91 		return -ENOTTY;
92 	}
93 }
94 
95 int hfsplus_setxattr(struct dentry *dentry, const char *name,
96 		     const void *value, size_t size, int flags)
97 {
98 	struct inode *inode = dentry->d_inode;
99 	struct hfs_find_data fd;
100 	hfsplus_cat_entry entry;
101 	struct hfsplus_cat_file *file;
102 	int res;
103 
104 	if (!S_ISREG(inode->i_mode) || HFSPLUS_IS_RSRC(inode))
105 		return -EOPNOTSUPP;
106 
107 	res = hfs_find_init(HFSPLUS_SB(inode->i_sb).cat_tree, &fd);
108 	if (res)
109 		return res;
110 	res = hfsplus_find_cat(inode->i_sb, inode->i_ino, &fd);
111 	if (res)
112 		goto out;
113 	hfs_bnode_read(fd.bnode, &entry, fd.entryoffset,
114 			sizeof(struct hfsplus_cat_file));
115 	file = &entry.file;
116 
117 	if (!strcmp(name, "hfs.type")) {
118 		if (size == 4)
119 			memcpy(&file->user_info.fdType, value, 4);
120 		else
121 			res = -ERANGE;
122 	} else if (!strcmp(name, "hfs.creator")) {
123 		if (size == 4)
124 			memcpy(&file->user_info.fdCreator, value, 4);
125 		else
126 			res = -ERANGE;
127 	} else
128 		res = -EOPNOTSUPP;
129 	if (!res)
130 		hfs_bnode_write(fd.bnode, &entry, fd.entryoffset,
131 				sizeof(struct hfsplus_cat_file));
132 out:
133 	hfs_find_exit(&fd);
134 	return res;
135 }
136 
137 ssize_t hfsplus_getxattr(struct dentry *dentry, const char *name,
138 			 void *value, size_t size)
139 {
140 	struct inode *inode = dentry->d_inode;
141 	struct hfs_find_data fd;
142 	hfsplus_cat_entry entry;
143 	struct hfsplus_cat_file *file;
144 	ssize_t res = 0;
145 
146 	if (!S_ISREG(inode->i_mode) || HFSPLUS_IS_RSRC(inode))
147 		return -EOPNOTSUPP;
148 
149 	if (size) {
150 		res = hfs_find_init(HFSPLUS_SB(inode->i_sb).cat_tree, &fd);
151 		if (res)
152 			return res;
153 		res = hfsplus_find_cat(inode->i_sb, inode->i_ino, &fd);
154 		if (res)
155 			goto out;
156 		hfs_bnode_read(fd.bnode, &entry, fd.entryoffset,
157 				sizeof(struct hfsplus_cat_file));
158 	}
159 	file = &entry.file;
160 
161 	if (!strcmp(name, "hfs.type")) {
162 		if (size >= 4) {
163 			memcpy(value, &file->user_info.fdType, 4);
164 			res = 4;
165 		} else
166 			res = size ? -ERANGE : 4;
167 	} else if (!strcmp(name, "hfs.creator")) {
168 		if (size >= 4) {
169 			memcpy(value, &file->user_info.fdCreator, 4);
170 			res = 4;
171 		} else
172 			res = size ? -ERANGE : 4;
173 	} else
174 		res = -ENODATA;
175 out:
176 	if (size)
177 		hfs_find_exit(&fd);
178 	return res;
179 }
180 
181 #define HFSPLUS_ATTRLIST_SIZE (sizeof("hfs.creator")+sizeof("hfs.type"))
182 
183 ssize_t hfsplus_listxattr(struct dentry *dentry, char *buffer, size_t size)
184 {
185 	struct inode *inode = dentry->d_inode;
186 
187 	if (!S_ISREG(inode->i_mode) || HFSPLUS_IS_RSRC(inode))
188 		return -EOPNOTSUPP;
189 
190 	if (!buffer || !size)
191 		return HFSPLUS_ATTRLIST_SIZE;
192 	if (size < HFSPLUS_ATTRLIST_SIZE)
193 		return -ERANGE;
194 	strcpy(buffer, "hfs.type");
195 	strcpy(buffer + sizeof("hfs.type"), "hfs.creator");
196 
197 	return HFSPLUS_ATTRLIST_SIZE;
198 }
199