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/mount.h> 18 #include <linux/sched.h> 19 #include <linux/xattr.h> 20 #include <asm/uaccess.h> 21 #include "hfsplus_fs.h" 22 23 static int hfsplus_ioctl_getflags(struct file *file, int __user *user_flags) 24 { 25 struct inode *inode = file->f_path.dentry->d_inode; 26 struct hfsplus_inode_info *hip = HFSPLUS_I(inode); 27 unsigned int flags = 0; 28 29 if (inode->i_flags & S_IMMUTABLE) 30 flags |= FS_IMMUTABLE_FL; 31 if (inode->i_flags & S_APPEND) 32 flags |= FS_APPEND_FL; 33 if (hip->userflags & HFSPLUS_FLG_NODUMP) 34 flags |= FS_NODUMP_FL; 35 36 return put_user(flags, user_flags); 37 } 38 39 static int hfsplus_ioctl_setflags(struct file *file, int __user *user_flags) 40 { 41 struct inode *inode = file->f_path.dentry->d_inode; 42 struct hfsplus_inode_info *hip = HFSPLUS_I(inode); 43 unsigned int flags; 44 int err = 0; 45 46 err = mnt_want_write(file->f_path.mnt); 47 if (err) 48 goto out; 49 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 60 mutex_lock(&inode->i_mutex); 61 62 if ((flags & (FS_IMMUTABLE_FL|FS_APPEND_FL)) || 63 inode->i_flags & (S_IMMUTABLE|S_APPEND)) { 64 if (!capable(CAP_LINUX_IMMUTABLE)) { 65 err = -EPERM; 66 goto out_unlock_inode; 67 } 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; 73 goto out_unlock_inode; 74 } 75 76 if (flags & FS_IMMUTABLE_FL) 77 inode->i_flags |= S_IMMUTABLE; 78 else 79 inode->i_flags &= ~S_IMMUTABLE; 80 81 if (flags & FS_APPEND_FL) 82 inode->i_flags |= S_APPEND; 83 else 84 inode->i_flags &= ~S_APPEND; 85 86 if (flags & FS_NODUMP_FL) 87 hip->userflags |= HFSPLUS_FLG_NODUMP; 88 else 89 hip->userflags &= ~HFSPLUS_FLG_NODUMP; 90 91 inode->i_ctime = CURRENT_TIME_SEC; 92 mark_inode_dirty(inode); 93 94 out_unlock_inode: 95 mutex_unlock(&inode->i_mutex); 96 out_drop_write: 97 mnt_drop_write(file->f_path.mnt); 98 out: 99 return err; 100 } 101 102 long hfsplus_ioctl(struct file *file, unsigned int cmd, unsigned long arg) 103 { 104 void __user *argp = (void __user *)arg; 105 106 switch (cmd) { 107 case HFSPLUS_IOC_EXT2_GETFLAGS: 108 return hfsplus_ioctl_getflags(file, argp); 109 case HFSPLUS_IOC_EXT2_SETFLAGS: 110 return hfsplus_ioctl_setflags(file, argp); 111 default: 112 return -ENOTTY; 113 } 114 } 115 116 int hfsplus_setxattr(struct dentry *dentry, const char *name, 117 const void *value, size_t size, int flags) 118 { 119 struct inode *inode = dentry->d_inode; 120 struct hfs_find_data fd; 121 hfsplus_cat_entry entry; 122 struct hfsplus_cat_file *file; 123 int res; 124 125 if (!S_ISREG(inode->i_mode) || HFSPLUS_IS_RSRC(inode)) 126 return -EOPNOTSUPP; 127 128 res = hfs_find_init(HFSPLUS_SB(inode->i_sb)->cat_tree, &fd); 129 if (res) 130 return res; 131 res = hfsplus_find_cat(inode->i_sb, inode->i_ino, &fd); 132 if (res) 133 goto out; 134 hfs_bnode_read(fd.bnode, &entry, fd.entryoffset, 135 sizeof(struct hfsplus_cat_file)); 136 file = &entry.file; 137 138 if (!strcmp(name, "hfs.type")) { 139 if (size == 4) 140 memcpy(&file->user_info.fdType, value, 4); 141 else 142 res = -ERANGE; 143 } else if (!strcmp(name, "hfs.creator")) { 144 if (size == 4) 145 memcpy(&file->user_info.fdCreator, value, 4); 146 else 147 res = -ERANGE; 148 } else 149 res = -EOPNOTSUPP; 150 if (!res) { 151 hfs_bnode_write(fd.bnode, &entry, fd.entryoffset, 152 sizeof(struct hfsplus_cat_file)); 153 hfsplus_mark_inode_dirty(inode, HFSPLUS_I_CAT_DIRTY); 154 } 155 out: 156 hfs_find_exit(&fd); 157 return res; 158 } 159 160 ssize_t hfsplus_getxattr(struct dentry *dentry, const char *name, 161 void *value, size_t size) 162 { 163 struct inode *inode = dentry->d_inode; 164 struct hfs_find_data fd; 165 hfsplus_cat_entry entry; 166 struct hfsplus_cat_file *file; 167 ssize_t res = 0; 168 169 if (!S_ISREG(inode->i_mode) || HFSPLUS_IS_RSRC(inode)) 170 return -EOPNOTSUPP; 171 172 if (size) { 173 res = hfs_find_init(HFSPLUS_SB(inode->i_sb)->cat_tree, &fd); 174 if (res) 175 return res; 176 res = hfsplus_find_cat(inode->i_sb, inode->i_ino, &fd); 177 if (res) 178 goto out; 179 hfs_bnode_read(fd.bnode, &entry, fd.entryoffset, 180 sizeof(struct hfsplus_cat_file)); 181 } 182 file = &entry.file; 183 184 if (!strcmp(name, "hfs.type")) { 185 if (size >= 4) { 186 memcpy(value, &file->user_info.fdType, 4); 187 res = 4; 188 } else 189 res = size ? -ERANGE : 4; 190 } else if (!strcmp(name, "hfs.creator")) { 191 if (size >= 4) { 192 memcpy(value, &file->user_info.fdCreator, 4); 193 res = 4; 194 } else 195 res = size ? -ERANGE : 4; 196 } else 197 res = -EOPNOTSUPP; 198 out: 199 if (size) 200 hfs_find_exit(&fd); 201 return res; 202 } 203 204 #define HFSPLUS_ATTRLIST_SIZE (sizeof("hfs.creator")+sizeof("hfs.type")) 205 206 ssize_t hfsplus_listxattr(struct dentry *dentry, char *buffer, size_t size) 207 { 208 struct inode *inode = dentry->d_inode; 209 210 if (!S_ISREG(inode->i_mode) || HFSPLUS_IS_RSRC(inode)) 211 return -EOPNOTSUPP; 212 213 if (!buffer || !size) 214 return HFSPLUS_ATTRLIST_SIZE; 215 if (size < HFSPLUS_ATTRLIST_SIZE) 216 return -ERANGE; 217 strcpy(buffer, "hfs.type"); 218 strcpy(buffer + sizeof("hfs.type"), "hfs.creator"); 219 220 return HFSPLUS_ATTRLIST_SIZE; 221 } 222