Merge branch 'upstream'
[linux-2.6] / fs / hfsplus / ioctl.c
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/sched.h>
18 #include <linux/xattr.h>
19 #include <asm/uaccess.h>
20 #include "hfsplus_fs.h"
21
22 int hfsplus_ioctl(struct inode *inode, struct file *filp, unsigned int cmd,
23                   unsigned long arg)
24 {
25         unsigned int flags;
26
27         switch (cmd) {
28         case HFSPLUS_IOC_EXT2_GETFLAGS:
29                 flags = 0;
30                 if (HFSPLUS_I(inode).rootflags & HFSPLUS_FLG_IMMUTABLE)
31                         flags |= EXT2_FLAG_IMMUTABLE; /* EXT2_IMMUTABLE_FL */
32                 if (HFSPLUS_I(inode).rootflags & HFSPLUS_FLG_APPEND)
33                         flags |= EXT2_FLAG_APPEND; /* EXT2_APPEND_FL */
34                 if (HFSPLUS_I(inode).userflags & HFSPLUS_FLG_NODUMP)
35                         flags |= EXT2_FLAG_NODUMP; /* EXT2_NODUMP_FL */
36                 return put_user(flags, (int __user *)arg);
37         case HFSPLUS_IOC_EXT2_SETFLAGS: {
38                 if (IS_RDONLY(inode))
39                         return -EROFS;
40
41                 if ((current->fsuid != inode->i_uid) && !capable(CAP_FOWNER))
42                         return -EACCES;
43
44                 if (get_user(flags, (int __user *)arg))
45                         return -EFAULT;
46
47                 if (flags & (EXT2_FLAG_IMMUTABLE|EXT2_FLAG_APPEND) ||
48                     HFSPLUS_I(inode).rootflags & (HFSPLUS_FLG_IMMUTABLE|HFSPLUS_FLG_APPEND)) {
49                         if (!capable(CAP_LINUX_IMMUTABLE))
50                                 return -EPERM;
51                 }
52
53                 /* don't silently ignore unsupported ext2 flags */
54                 if (flags & ~(EXT2_FLAG_IMMUTABLE|EXT2_FLAG_APPEND|
55                               EXT2_FLAG_NODUMP))
56                         return -EOPNOTSUPP;
57
58                 if (flags & EXT2_FLAG_IMMUTABLE) { /* EXT2_IMMUTABLE_FL */
59                         inode->i_flags |= S_IMMUTABLE;
60                         HFSPLUS_I(inode).rootflags |= HFSPLUS_FLG_IMMUTABLE;
61                 } else {
62                         inode->i_flags &= ~S_IMMUTABLE;
63                         HFSPLUS_I(inode).rootflags &= ~HFSPLUS_FLG_IMMUTABLE;
64                 }
65                 if (flags & EXT2_FLAG_APPEND) { /* EXT2_APPEND_FL */
66                         inode->i_flags |= S_APPEND;
67                         HFSPLUS_I(inode).rootflags |= HFSPLUS_FLG_APPEND;
68                 } else {
69                         inode->i_flags &= ~S_APPEND;
70                         HFSPLUS_I(inode).rootflags &= ~HFSPLUS_FLG_APPEND;
71                 }
72                 if (flags & EXT2_FLAG_NODUMP) /* EXT2_NODUMP_FL */
73                         HFSPLUS_I(inode).userflags |= HFSPLUS_FLG_NODUMP;
74                 else
75                         HFSPLUS_I(inode).userflags &= ~HFSPLUS_FLG_NODUMP;
76
77                 inode->i_ctime = CURRENT_TIME_SEC;
78                 mark_inode_dirty(inode);
79                 return 0;
80         }
81         default:
82                 return -ENOTTY;
83         }
84 }
85
86 int hfsplus_setxattr(struct dentry *dentry, const char *name,
87                      const void *value, size_t size, int flags)
88 {
89         struct inode *inode = dentry->d_inode;
90         struct hfs_find_data fd;
91         hfsplus_cat_entry entry;
92         struct hfsplus_cat_file *file;
93         int res;
94
95         if (!S_ISREG(inode->i_mode) || HFSPLUS_IS_RSRC(inode))
96                 return -EOPNOTSUPP;
97
98         res = hfs_find_init(HFSPLUS_SB(inode->i_sb).cat_tree, &fd);
99         if (res)
100                 return res;
101         res = hfsplus_find_cat(inode->i_sb, inode->i_ino, &fd);
102         if (res)
103                 goto out;
104         hfs_bnode_read(fd.bnode, &entry, fd.entryoffset,
105                         sizeof(struct hfsplus_cat_file));
106         file = &entry.file;
107
108         if (!strcmp(name, "hfs.type")) {
109                 if (size == 4)
110                         memcpy(&file->user_info.fdType, value, 4);
111                 else
112                         res = -ERANGE;
113         } else if (!strcmp(name, "hfs.creator")) {
114                 if (size == 4)
115                         memcpy(&file->user_info.fdCreator, value, 4);
116                 else
117                         res = -ERANGE;
118         } else
119                 res = -EOPNOTSUPP;
120         if (!res)
121                 hfs_bnode_write(fd.bnode, &entry, fd.entryoffset,
122                                 sizeof(struct hfsplus_cat_file));
123 out:
124         hfs_find_exit(&fd);
125         return res;
126 }
127
128 ssize_t hfsplus_getxattr(struct dentry *dentry, const char *name,
129                          void *value, size_t size)
130 {
131         struct inode *inode = dentry->d_inode;
132         struct hfs_find_data fd;
133         hfsplus_cat_entry entry;
134         struct hfsplus_cat_file *file;
135         ssize_t res = 0;
136
137         if (!S_ISREG(inode->i_mode) || HFSPLUS_IS_RSRC(inode))
138                 return -EOPNOTSUPP;
139
140         if (size) {
141                 res = hfs_find_init(HFSPLUS_SB(inode->i_sb).cat_tree, &fd);
142                 if (res)
143                         return res;
144                 res = hfsplus_find_cat(inode->i_sb, inode->i_ino, &fd);
145                 if (res)
146                         goto out;
147                 hfs_bnode_read(fd.bnode, &entry, fd.entryoffset,
148                                 sizeof(struct hfsplus_cat_file));
149         }
150         file = &entry.file;
151
152         if (!strcmp(name, "hfs.type")) {
153                 if (size >= 4) {
154                         memcpy(value, &file->user_info.fdType, 4);
155                         res = 4;
156                 } else
157                         res = size ? -ERANGE : 4;
158         } else if (!strcmp(name, "hfs.creator")) {
159                 if (size >= 4) {
160                         memcpy(value, &file->user_info.fdCreator, 4);
161                         res = 4;
162                 } else
163                         res = size ? -ERANGE : 4;
164         } else
165                 res = -ENODATA;
166 out:
167         if (size)
168                 hfs_find_exit(&fd);
169         return res;
170 }
171
172 #define HFSPLUS_ATTRLIST_SIZE (sizeof("hfs.creator")+sizeof("hfs.type"))
173
174 ssize_t hfsplus_listxattr(struct dentry *dentry, char *buffer, size_t size)
175 {
176         struct inode *inode = dentry->d_inode;
177
178         if (!S_ISREG(inode->i_mode) || HFSPLUS_IS_RSRC(inode))
179                 return -EOPNOTSUPP;
180
181         if (!buffer || !size)
182                 return HFSPLUS_ATTRLIST_SIZE;
183         if (size < HFSPLUS_ATTRLIST_SIZE)
184                 return -ERANGE;
185         strcpy(buffer, "hfs.type");
186         strcpy(buffer + sizeof("hfs.type"), "hfs.creator");
187
188         return HFSPLUS_ATTRLIST_SIZE;
189 }