blob: 66dd64b88b2e2ebb05ba89ef250eaaaabe0c4afa [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/*
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
Randy Dunlap16f7e0f2006-01-11 12:17:46 -080015#include <linux/capability.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070016#include <linux/fs.h>
Dave Hansen42a74f22008-02-15 14:37:46 -080017#include <linux/mount.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070018#include <linux/sched.h>
19#include <linux/xattr.h>
20#include <asm/uaccess.h>
21#include "hfsplus_fs.h"
22
Christoph Hellwig94744562010-10-01 05:41:31 +020023static int hfsplus_ioctl_getflags(struct file *file, int __user *user_flags)
Linus Torvalds1da177e2005-04-16 15:20:36 -070024{
Christoph Hellwig94744562010-10-01 05:41:31 +020025 struct inode *inode = file->f_path.dentry->d_inode;
26 unsigned int flags = 0;
27
28 if (HFSPLUS_I(inode).rootflags & HFSPLUS_FLG_IMMUTABLE)
29 flags |= FS_IMMUTABLE_FL;
30 if (HFSPLUS_I(inode).rootflags & HFSPLUS_FLG_APPEND)
31 flags |= FS_APPEND_FL;
32 if (HFSPLUS_I(inode).userflags & HFSPLUS_FLG_NODUMP)
33 flags |= FS_NODUMP_FL;
34
35 return put_user(flags, user_flags);
36}
37
38static int hfsplus_ioctl_setflags(struct file *file, int __user *user_flags)
39{
40 struct inode *inode = file->f_path.dentry->d_inode;
Linus Torvalds1da177e2005-04-16 15:20:36 -070041 unsigned int flags;
Christoph Hellwig94744562010-10-01 05:41:31 +020042 int err = 0;
43
Christoph Hellwig94744562010-10-01 05:41:31 +020044 err = mnt_want_write(file->f_path.mnt);
45 if (err)
Christoph Hellwig63338162010-10-01 05:41:35 +020046 goto out;
Christoph Hellwig94744562010-10-01 05:41:31 +020047
48 if (!is_owner_or_cap(inode)) {
49 err = -EACCES;
50 goto out_drop_write;
51 }
52
53 if (get_user(flags, user_flags)) {
54 err = -EFAULT;
55 goto out_drop_write;
56 }
57
Christoph Hellwig63338162010-10-01 05:41:35 +020058 mutex_lock(&inode->i_mutex);
59
Christoph Hellwig94744562010-10-01 05:41:31 +020060 if (flags & (FS_IMMUTABLE_FL|FS_APPEND_FL) ||
61 HFSPLUS_I(inode).rootflags & (HFSPLUS_FLG_IMMUTABLE|HFSPLUS_FLG_APPEND)) {
62 if (!capable(CAP_LINUX_IMMUTABLE)) {
63 err = -EPERM;
Christoph Hellwig63338162010-10-01 05:41:35 +020064 goto out_unlock_inode;
Christoph Hellwig94744562010-10-01 05:41:31 +020065 }
66 }
67
68 /* don't silently ignore unsupported ext2 flags */
69 if (flags & ~(FS_IMMUTABLE_FL|FS_APPEND_FL|FS_NODUMP_FL)) {
70 err = -EOPNOTSUPP;
Christoph Hellwig63338162010-10-01 05:41:35 +020071 goto out_unlock_inode;
Christoph Hellwig94744562010-10-01 05:41:31 +020072 }
73 if (flags & FS_IMMUTABLE_FL) {
74 inode->i_flags |= S_IMMUTABLE;
75 HFSPLUS_I(inode).rootflags |= HFSPLUS_FLG_IMMUTABLE;
76 } else {
77 inode->i_flags &= ~S_IMMUTABLE;
78 HFSPLUS_I(inode).rootflags &= ~HFSPLUS_FLG_IMMUTABLE;
79 }
80 if (flags & FS_APPEND_FL) {
81 inode->i_flags |= S_APPEND;
82 HFSPLUS_I(inode).rootflags |= HFSPLUS_FLG_APPEND;
83 } else {
84 inode->i_flags &= ~S_APPEND;
85 HFSPLUS_I(inode).rootflags &= ~HFSPLUS_FLG_APPEND;
86 }
87 if (flags & FS_NODUMP_FL)
88 HFSPLUS_I(inode).userflags |= HFSPLUS_FLG_NODUMP;
89 else
90 HFSPLUS_I(inode).userflags &= ~HFSPLUS_FLG_NODUMP;
91
92 inode->i_ctime = CURRENT_TIME_SEC;
93 mark_inode_dirty(inode);
94
Christoph Hellwig63338162010-10-01 05:41:35 +020095out_unlock_inode:
96 mutex_lock(&inode->i_mutex);
Christoph Hellwig94744562010-10-01 05:41:31 +020097out_drop_write:
98 mnt_drop_write(file->f_path.mnt);
Christoph Hellwig63338162010-10-01 05:41:35 +020099out:
Christoph Hellwig94744562010-10-01 05:41:31 +0200100 return err;
101}
102
103long hfsplus_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
104{
105 void __user *argp = (void __user *)arg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700106
107 switch (cmd) {
108 case HFSPLUS_IOC_EXT2_GETFLAGS:
Christoph Hellwig94744562010-10-01 05:41:31 +0200109 return hfsplus_ioctl_getflags(file, argp);
110 case HFSPLUS_IOC_EXT2_SETFLAGS:
111 return hfsplus_ioctl_setflags(file, argp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700112 default:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700113 return -ENOTTY;
114 }
115}
116
117int hfsplus_setxattr(struct dentry *dentry, const char *name,
118 const void *value, size_t size, int flags)
119{
120 struct inode *inode = dentry->d_inode;
121 struct hfs_find_data fd;
122 hfsplus_cat_entry entry;
123 struct hfsplus_cat_file *file;
124 int res;
125
126 if (!S_ISREG(inode->i_mode) || HFSPLUS_IS_RSRC(inode))
127 return -EOPNOTSUPP;
128
Christoph Hellwigdd73a012010-10-01 05:42:59 +0200129 res = hfs_find_init(HFSPLUS_SB(inode->i_sb)->cat_tree, &fd);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700130 if (res)
131 return res;
132 res = hfsplus_find_cat(inode->i_sb, inode->i_ino, &fd);
133 if (res)
134 goto out;
135 hfs_bnode_read(fd.bnode, &entry, fd.entryoffset,
136 sizeof(struct hfsplus_cat_file));
137 file = &entry.file;
138
139 if (!strcmp(name, "hfs.type")) {
140 if (size == 4)
141 memcpy(&file->user_info.fdType, value, 4);
142 else
143 res = -ERANGE;
144 } else if (!strcmp(name, "hfs.creator")) {
145 if (size == 4)
146 memcpy(&file->user_info.fdCreator, value, 4);
147 else
148 res = -ERANGE;
149 } else
150 res = -EOPNOTSUPP;
151 if (!res)
152 hfs_bnode_write(fd.bnode, &entry, fd.entryoffset,
153 sizeof(struct hfsplus_cat_file));
154out:
155 hfs_find_exit(&fd);
156 return res;
157}
158
159ssize_t hfsplus_getxattr(struct dentry *dentry, const char *name,
160 void *value, size_t size)
161{
162 struct inode *inode = dentry->d_inode;
163 struct hfs_find_data fd;
164 hfsplus_cat_entry entry;
165 struct hfsplus_cat_file *file;
166 ssize_t res = 0;
167
168 if (!S_ISREG(inode->i_mode) || HFSPLUS_IS_RSRC(inode))
169 return -EOPNOTSUPP;
170
171 if (size) {
Christoph Hellwigdd73a012010-10-01 05:42:59 +0200172 res = hfs_find_init(HFSPLUS_SB(inode->i_sb)->cat_tree, &fd);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700173 if (res)
174 return res;
175 res = hfsplus_find_cat(inode->i_sb, inode->i_ino, &fd);
176 if (res)
177 goto out;
178 hfs_bnode_read(fd.bnode, &entry, fd.entryoffset,
179 sizeof(struct hfsplus_cat_file));
180 }
181 file = &entry.file;
182
183 if (!strcmp(name, "hfs.type")) {
184 if (size >= 4) {
185 memcpy(value, &file->user_info.fdType, 4);
186 res = 4;
187 } else
188 res = size ? -ERANGE : 4;
189 } else if (!strcmp(name, "hfs.creator")) {
190 if (size >= 4) {
191 memcpy(value, &file->user_info.fdCreator, 4);
192 res = 4;
193 } else
194 res = size ? -ERANGE : 4;
195 } else
196 res = -ENODATA;
197out:
198 if (size)
199 hfs_find_exit(&fd);
200 return res;
201}
202
203#define HFSPLUS_ATTRLIST_SIZE (sizeof("hfs.creator")+sizeof("hfs.type"))
204
205ssize_t hfsplus_listxattr(struct dentry *dentry, char *buffer, size_t size)
206{
207 struct inode *inode = dentry->d_inode;
208
209 if (!S_ISREG(inode->i_mode) || HFSPLUS_IS_RSRC(inode))
210 return -EOPNOTSUPP;
211
212 if (!buffer || !size)
213 return HFSPLUS_ATTRLIST_SIZE;
214 if (size < HFSPLUS_ATTRLIST_SIZE)
215 return -ERANGE;
216 strcpy(buffer, "hfs.type");
217 strcpy(buffer + sizeof("hfs.type"), "hfs.creator");
218
219 return HFSPLUS_ATTRLIST_SIZE;
220}