blob: c9ac443d202ab2bdfd2dde6101f4fad446de541a [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;
Christoph Hellwig6af502d2010-10-01 05:43:31 +020026 struct hfsplus_inode_info *hip = HFSPLUS_I(inode);
Christoph Hellwig94744562010-10-01 05:41:31 +020027 unsigned int flags = 0;
28
Christoph Hellwig6af502d2010-10-01 05:43:31 +020029 if (hip->rootflags & HFSPLUS_FLG_IMMUTABLE)
Christoph Hellwig94744562010-10-01 05:41:31 +020030 flags |= FS_IMMUTABLE_FL;
Christoph Hellwig6af502d2010-10-01 05:43:31 +020031 if (hip->rootflags & HFSPLUS_FLG_APPEND)
Christoph Hellwig94744562010-10-01 05:41:31 +020032 flags |= FS_APPEND_FL;
Christoph Hellwig6af502d2010-10-01 05:43:31 +020033 if (hip->userflags & HFSPLUS_FLG_NODUMP)
Christoph Hellwig94744562010-10-01 05:41:31 +020034 flags |= FS_NODUMP_FL;
35
36 return put_user(flags, user_flags);
37}
38
39static int hfsplus_ioctl_setflags(struct file *file, int __user *user_flags)
40{
41 struct inode *inode = file->f_path.dentry->d_inode;
Christoph Hellwig6af502d2010-10-01 05:43:31 +020042 struct hfsplus_inode_info *hip = HFSPLUS_I(inode);
Linus Torvalds1da177e2005-04-16 15:20:36 -070043 unsigned int flags;
Christoph Hellwig94744562010-10-01 05:41:31 +020044 int err = 0;
45
Christoph Hellwig94744562010-10-01 05:41:31 +020046 err = mnt_want_write(file->f_path.mnt);
47 if (err)
Christoph Hellwig63338162010-10-01 05:41:35 +020048 goto out;
Christoph Hellwig94744562010-10-01 05:41:31 +020049
50 if (!is_owner_or_cap(inode)) {
51 err = -EACCES;
52 goto out_drop_write;
53 }
54
55 if (get_user(flags, user_flags)) {
56 err = -EFAULT;
57 goto out_drop_write;
58 }
59
Christoph Hellwig63338162010-10-01 05:41:35 +020060 mutex_lock(&inode->i_mutex);
61
Christoph Hellwig94744562010-10-01 05:41:31 +020062 if (flags & (FS_IMMUTABLE_FL|FS_APPEND_FL) ||
Christoph Hellwig6af502d2010-10-01 05:43:31 +020063 hip->rootflags & (HFSPLUS_FLG_IMMUTABLE|HFSPLUS_FLG_APPEND)) {
Christoph Hellwig94744562010-10-01 05:41:31 +020064 if (!capable(CAP_LINUX_IMMUTABLE)) {
65 err = -EPERM;
Christoph Hellwig63338162010-10-01 05:41:35 +020066 goto out_unlock_inode;
Christoph Hellwig94744562010-10-01 05:41:31 +020067 }
68 }
69
70 /* don't silently ignore unsupported ext2 flags */
71 if (flags & ~(FS_IMMUTABLE_FL|FS_APPEND_FL|FS_NODUMP_FL)) {
72 err = -EOPNOTSUPP;
Christoph Hellwig63338162010-10-01 05:41:35 +020073 goto out_unlock_inode;
Christoph Hellwig94744562010-10-01 05:41:31 +020074 }
75 if (flags & FS_IMMUTABLE_FL) {
76 inode->i_flags |= S_IMMUTABLE;
Christoph Hellwig6af502d2010-10-01 05:43:31 +020077 hip->rootflags |= HFSPLUS_FLG_IMMUTABLE;
Christoph Hellwig94744562010-10-01 05:41:31 +020078 } else {
79 inode->i_flags &= ~S_IMMUTABLE;
Christoph Hellwig6af502d2010-10-01 05:43:31 +020080 hip->rootflags &= ~HFSPLUS_FLG_IMMUTABLE;
Christoph Hellwig94744562010-10-01 05:41:31 +020081 }
82 if (flags & FS_APPEND_FL) {
83 inode->i_flags |= S_APPEND;
Christoph Hellwig6af502d2010-10-01 05:43:31 +020084 hip->rootflags |= HFSPLUS_FLG_APPEND;
Christoph Hellwig94744562010-10-01 05:41:31 +020085 } else {
86 inode->i_flags &= ~S_APPEND;
Christoph Hellwig6af502d2010-10-01 05:43:31 +020087 hip->rootflags &= ~HFSPLUS_FLG_APPEND;
Christoph Hellwig94744562010-10-01 05:41:31 +020088 }
89 if (flags & FS_NODUMP_FL)
Christoph Hellwig6af502d2010-10-01 05:43:31 +020090 hip->userflags |= HFSPLUS_FLG_NODUMP;
Christoph Hellwig94744562010-10-01 05:41:31 +020091 else
Christoph Hellwig6af502d2010-10-01 05:43:31 +020092 hip->userflags &= ~HFSPLUS_FLG_NODUMP;
Christoph Hellwig94744562010-10-01 05:41:31 +020093
94 inode->i_ctime = CURRENT_TIME_SEC;
95 mark_inode_dirty(inode);
96
Christoph Hellwig63338162010-10-01 05:41:35 +020097out_unlock_inode:
98 mutex_lock(&inode->i_mutex);
Christoph Hellwig94744562010-10-01 05:41:31 +020099out_drop_write:
100 mnt_drop_write(file->f_path.mnt);
Christoph Hellwig63338162010-10-01 05:41:35 +0200101out:
Christoph Hellwig94744562010-10-01 05:41:31 +0200102 return err;
103}
104
105long hfsplus_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
106{
107 void __user *argp = (void __user *)arg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700108
109 switch (cmd) {
110 case HFSPLUS_IOC_EXT2_GETFLAGS:
Christoph Hellwig94744562010-10-01 05:41:31 +0200111 return hfsplus_ioctl_getflags(file, argp);
112 case HFSPLUS_IOC_EXT2_SETFLAGS:
113 return hfsplus_ioctl_setflags(file, argp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700114 default:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700115 return -ENOTTY;
116 }
117}
118
119int hfsplus_setxattr(struct dentry *dentry, const char *name,
120 const void *value, size_t size, int flags)
121{
122 struct inode *inode = dentry->d_inode;
123 struct hfs_find_data fd;
124 hfsplus_cat_entry entry;
125 struct hfsplus_cat_file *file;
126 int res;
127
128 if (!S_ISREG(inode->i_mode) || HFSPLUS_IS_RSRC(inode))
129 return -EOPNOTSUPP;
130
Christoph Hellwigdd73a012010-10-01 05:42:59 +0200131 res = hfs_find_init(HFSPLUS_SB(inode->i_sb)->cat_tree, &fd);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700132 if (res)
133 return res;
134 res = hfsplus_find_cat(inode->i_sb, inode->i_ino, &fd);
135 if (res)
136 goto out;
137 hfs_bnode_read(fd.bnode, &entry, fd.entryoffset,
138 sizeof(struct hfsplus_cat_file));
139 file = &entry.file;
140
141 if (!strcmp(name, "hfs.type")) {
142 if (size == 4)
143 memcpy(&file->user_info.fdType, value, 4);
144 else
145 res = -ERANGE;
146 } else if (!strcmp(name, "hfs.creator")) {
147 if (size == 4)
148 memcpy(&file->user_info.fdCreator, value, 4);
149 else
150 res = -ERANGE;
151 } else
152 res = -EOPNOTSUPP;
153 if (!res)
154 hfs_bnode_write(fd.bnode, &entry, fd.entryoffset,
155 sizeof(struct hfsplus_cat_file));
156out:
157 hfs_find_exit(&fd);
158 return res;
159}
160
161ssize_t hfsplus_getxattr(struct dentry *dentry, const char *name,
162 void *value, size_t size)
163{
164 struct inode *inode = dentry->d_inode;
165 struct hfs_find_data fd;
166 hfsplus_cat_entry entry;
167 struct hfsplus_cat_file *file;
168 ssize_t res = 0;
169
170 if (!S_ISREG(inode->i_mode) || HFSPLUS_IS_RSRC(inode))
171 return -EOPNOTSUPP;
172
173 if (size) {
Christoph Hellwigdd73a012010-10-01 05:42:59 +0200174 res = hfs_find_init(HFSPLUS_SB(inode->i_sb)->cat_tree, &fd);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700175 if (res)
176 return res;
177 res = hfsplus_find_cat(inode->i_sb, inode->i_ino, &fd);
178 if (res)
179 goto out;
180 hfs_bnode_read(fd.bnode, &entry, fd.entryoffset,
181 sizeof(struct hfsplus_cat_file));
182 }
183 file = &entry.file;
184
185 if (!strcmp(name, "hfs.type")) {
186 if (size >= 4) {
187 memcpy(value, &file->user_info.fdType, 4);
188 res = 4;
189 } else
190 res = size ? -ERANGE : 4;
191 } else if (!strcmp(name, "hfs.creator")) {
192 if (size >= 4) {
193 memcpy(value, &file->user_info.fdCreator, 4);
194 res = 4;
195 } else
196 res = size ? -ERANGE : 4;
197 } else
198 res = -ENODATA;
199out:
200 if (size)
201 hfs_find_exit(&fd);
202 return res;
203}
204
205#define HFSPLUS_ATTRLIST_SIZE (sizeof("hfs.creator")+sizeof("hfs.type"))
206
207ssize_t hfsplus_listxattr(struct dentry *dentry, char *buffer, size_t size)
208{
209 struct inode *inode = dentry->d_inode;
210
211 if (!S_ISREG(inode->i_mode) || HFSPLUS_IS_RSRC(inode))
212 return -EOPNOTSUPP;
213
214 if (!buffer || !size)
215 return HFSPLUS_ATTRLIST_SIZE;
216 if (size < HFSPLUS_ATTRLIST_SIZE)
217 return -ERANGE;
218 strcpy(buffer, "hfs.type");
219 strcpy(buffer + sizeof("hfs.type"), "hfs.creator");
220
221 return HFSPLUS_ATTRLIST_SIZE;
222}