Fedora kernel-2.6.17-1.2142_FC4 patched with stable patch-2.6.17.4-vs2.0.2-rc26.diff
[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 |= EXT2_FLAG_IMMUTABLE; /* EXT2_IMMUTABLE_FL */
33                 if (HFSPLUS_I(inode).rootflags & HFSPLUS_FLG_APPEND)
34                         flags |= EXT2_FLAG_APPEND; /* EXT2_APPEND_FL */
35                 if (HFSPLUS_I(inode).userflags & HFSPLUS_FLG_NODUMP)
36                         flags |= EXT2_FLAG_NODUMP; /* 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 & (EXT2_FLAG_IMMUTABLE|EXT2_FLAG_APPEND) ||
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 & ~(EXT2_FLAG_IMMUTABLE|EXT2_FLAG_APPEND|
57                               EXT2_FLAG_NODUMP))
58                         return -EOPNOTSUPP;
59
60                 if (flags & EXT2_FLAG_IMMUTABLE) { /* EXT2_IMMUTABLE_FL */
61                         inode->i_flags |= S_IMMUTABLE;
62                         HFSPLUS_I(inode).rootflags |= HFSPLUS_FLG_IMMUTABLE;
63                 } else {
64                         inode->i_flags &= ~S_IMMUTABLE;
65                         HFSPLUS_I(inode).rootflags &= ~HFSPLUS_FLG_IMMUTABLE;
66                 }
67                 if (flags & EXT2_FLAG_APPEND) { /* EXT2_APPEND_FL */
68                         inode->i_flags |= S_APPEND;
69                         HFSPLUS_I(inode).rootflags |= HFSPLUS_FLG_APPEND;
70                 } else {
71                         inode->i_flags &= ~S_APPEND;
72                         HFSPLUS_I(inode).rootflags &= ~HFSPLUS_FLG_APPEND;
73                 }
74                 if (flags & EXT2_FLAG_NODUMP) /* EXT2_NODUMP_FL */
75                         HFSPLUS_I(inode).userflags |= HFSPLUS_FLG_NODUMP;
76                 else
77                         HFSPLUS_I(inode).userflags &= ~HFSPLUS_FLG_NODUMP;
78
79                 inode->i_ctime = CURRENT_TIME_SEC;
80                 mark_inode_dirty(inode);
81                 return 0;
82         }
83         default:
84                 return -ENOTTY;
85         }
86 }
87
88 int hfsplus_setxattr(struct dentry *dentry, const char *name,
89                      const void *value, size_t size, int flags)
90 {
91         struct inode *inode = dentry->d_inode;
92         struct hfs_find_data fd;
93         hfsplus_cat_entry entry;
94         struct hfsplus_cat_file *file;
95         int res;
96
97         if (!S_ISREG(inode->i_mode) || HFSPLUS_IS_RSRC(inode))
98                 return -EOPNOTSUPP;
99
100         res = hfs_find_init(HFSPLUS_SB(inode->i_sb).cat_tree, &fd);
101         if (res)
102                 return res;
103         res = hfsplus_find_cat(inode->i_sb, inode->i_ino, &fd);
104         if (res)
105                 goto out;
106         hfs_bnode_read(fd.bnode, &entry, fd.entryoffset,
107                         sizeof(struct hfsplus_cat_file));
108         file = &entry.file;
109
110         if (!strcmp(name, "hfs.type")) {
111                 if (size == 4)
112                         memcpy(&file->user_info.fdType, value, 4);
113                 else
114                         res = -ERANGE;
115         } else if (!strcmp(name, "hfs.creator")) {
116                 if (size == 4)
117                         memcpy(&file->user_info.fdCreator, value, 4);
118                 else
119                         res = -ERANGE;
120         } else
121                 res = -EOPNOTSUPP;
122         if (!res)
123                 hfs_bnode_write(fd.bnode, &entry, fd.entryoffset,
124                                 sizeof(struct hfsplus_cat_file));
125 out:
126         hfs_find_exit(&fd);
127         return res;
128 }
129
130 ssize_t hfsplus_getxattr(struct dentry *dentry, const char *name,
131                          void *value, size_t size)
132 {
133         struct inode *inode = dentry->d_inode;
134         struct hfs_find_data fd;
135         hfsplus_cat_entry entry;
136         struct hfsplus_cat_file *file;
137         ssize_t res = 0;
138
139         if (!S_ISREG(inode->i_mode) || HFSPLUS_IS_RSRC(inode))
140                 return -EOPNOTSUPP;
141
142         if (size) {
143                 res = hfs_find_init(HFSPLUS_SB(inode->i_sb).cat_tree, &fd);
144                 if (res)
145                         return res;
146                 res = hfsplus_find_cat(inode->i_sb, inode->i_ino, &fd);
147                 if (res)
148                         goto out;
149                 hfs_bnode_read(fd.bnode, &entry, fd.entryoffset,
150                                 sizeof(struct hfsplus_cat_file));
151         }
152         file = &entry.file;
153
154         if (!strcmp(name, "hfs.type")) {
155                 if (size >= 4) {
156                         memcpy(value, &file->user_info.fdType, 4);
157                         res = 4;
158                 } else
159                         res = size ? -ERANGE : 4;
160         } else if (!strcmp(name, "hfs.creator")) {
161                 if (size >= 4) {
162                         memcpy(value, &file->user_info.fdCreator, 4);
163                         res = 4;
164                 } else
165                         res = size ? -ERANGE : 4;
166         } else
167                 res = -ENODATA;
168 out:
169         if (size)
170                 hfs_find_exit(&fd);
171         return res;
172 }
173
174 #define HFSPLUS_ATTRLIST_SIZE (sizeof("hfs.creator")+sizeof("hfs.type"))
175
176 ssize_t hfsplus_listxattr(struct dentry *dentry, char *buffer, size_t size)
177 {
178         struct inode *inode = dentry->d_inode;
179
180         if (!S_ISREG(inode->i_mode) || HFSPLUS_IS_RSRC(inode))
181                 return -EOPNOTSUPP;
182
183         if (!buffer || !size)
184                 return HFSPLUS_ATTRLIST_SIZE;
185         if (size < HFSPLUS_ATTRLIST_SIZE)
186                 return -ERANGE;
187         strcpy(buffer, "hfs.type");
188         strcpy(buffer + sizeof("hfs.type"), "hfs.creator");
189
190         return HFSPLUS_ATTRLIST_SIZE;
191 }