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