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 /* 24 * "Blessing" an HFS+ filesystem writes metadata to the superblock informing 25 * the platform firmware which file to boot from 26 */ 27 static int hfsplus_ioctl_bless(struct file *file, int __user *user_flags) 28 { 29 struct dentry *dentry = file->f_path.dentry; 30 struct inode *inode = dentry->d_inode; 31 struct hfsplus_sb_info *sbi = HFSPLUS_SB(inode->i_sb); 32 struct hfsplus_vh *vh = sbi->s_vhdr; 33 struct hfsplus_vh *bvh = sbi->s_backup_vhdr; 34 35 if (!capable(CAP_SYS_ADMIN)) 36 return -EPERM; 37 38 mutex_lock(&sbi->vh_mutex); 39 40 /* Directory containing the bootable system */ 41 vh->finder_info[0] = bvh->finder_info[0] = 42 cpu_to_be32(parent_ino(dentry)); 43 44 /* Bootloader */ 45 vh->finder_info[1] = bvh->finder_info[1] = cpu_to_be32(inode->i_ino); 46 47 /* Per spec, the OS X system folder - same as finder_info[0] here */ 48 vh->finder_info[5] = bvh->finder_info[5] = 49 cpu_to_be32(parent_ino(dentry)); 50 51 mutex_unlock(&sbi->vh_mutex); 52 return 0; 53 } 54 55 static int hfsplus_ioctl_getflags(struct file *file, int __user *user_flags) 56 { 57 struct inode *inode = file->f_path.dentry->d_inode; 58 struct hfsplus_inode_info *hip = HFSPLUS_I(inode); 59 unsigned int flags = 0; 60 61 if (inode->i_flags & S_IMMUTABLE) 62 flags |= FS_IMMUTABLE_FL; 63 if (inode->i_flags & S_APPEND) 64 flags |= FS_APPEND_FL; 65 if (hip->userflags & HFSPLUS_FLG_NODUMP) 66 flags |= FS_NODUMP_FL; 67 68 return put_user(flags, user_flags); 69 } 70 71 static int hfsplus_ioctl_setflags(struct file *file, int __user *user_flags) 72 { 73 struct inode *inode = file->f_path.dentry->d_inode; 74 struct hfsplus_inode_info *hip = HFSPLUS_I(inode); 75 unsigned int flags; 76 int err = 0; 77 78 err = mnt_want_write_file(file); 79 if (err) 80 goto out; 81 82 if (!inode_owner_or_capable(inode)) { 83 err = -EACCES; 84 goto out_drop_write; 85 } 86 87 if (get_user(flags, user_flags)) { 88 err = -EFAULT; 89 goto out_drop_write; 90 } 91 92 mutex_lock(&inode->i_mutex); 93 94 if ((flags & (FS_IMMUTABLE_FL|FS_APPEND_FL)) || 95 inode->i_flags & (S_IMMUTABLE|S_APPEND)) { 96 if (!capable(CAP_LINUX_IMMUTABLE)) { 97 err = -EPERM; 98 goto out_unlock_inode; 99 } 100 } 101 102 /* don't silently ignore unsupported ext2 flags */ 103 if (flags & ~(FS_IMMUTABLE_FL|FS_APPEND_FL|FS_NODUMP_FL)) { 104 err = -EOPNOTSUPP; 105 goto out_unlock_inode; 106 } 107 108 if (flags & FS_IMMUTABLE_FL) 109 inode->i_flags |= S_IMMUTABLE; 110 else 111 inode->i_flags &= ~S_IMMUTABLE; 112 113 if (flags & FS_APPEND_FL) 114 inode->i_flags |= S_APPEND; 115 else 116 inode->i_flags &= ~S_APPEND; 117 118 if (flags & FS_NODUMP_FL) 119 hip->userflags |= HFSPLUS_FLG_NODUMP; 120 else 121 hip->userflags &= ~HFSPLUS_FLG_NODUMP; 122 123 inode->i_ctime = CURRENT_TIME_SEC; 124 mark_inode_dirty(inode); 125 126 out_unlock_inode: 127 mutex_unlock(&inode->i_mutex); 128 out_drop_write: 129 mnt_drop_write_file(file); 130 out: 131 return err; 132 } 133 134 long hfsplus_ioctl(struct file *file, unsigned int cmd, unsigned long arg) 135 { 136 void __user *argp = (void __user *)arg; 137 138 switch (cmd) { 139 case HFSPLUS_IOC_EXT2_GETFLAGS: 140 return hfsplus_ioctl_getflags(file, argp); 141 case HFSPLUS_IOC_EXT2_SETFLAGS: 142 return hfsplus_ioctl_setflags(file, argp); 143 case HFSPLUS_IOC_BLESS: 144 return hfsplus_ioctl_bless(file, argp); 145 default: 146 return -ENOTTY; 147 } 148 } 149 150 int hfsplus_setxattr(struct dentry *dentry, const char *name, 151 const void *value, size_t size, int flags) 152 { 153 struct inode *inode = dentry->d_inode; 154 struct hfs_find_data fd; 155 hfsplus_cat_entry entry; 156 struct hfsplus_cat_file *file; 157 int res; 158 159 if (!S_ISREG(inode->i_mode) || HFSPLUS_IS_RSRC(inode)) 160 return -EOPNOTSUPP; 161 162 res = hfs_find_init(HFSPLUS_SB(inode->i_sb)->cat_tree, &fd); 163 if (res) 164 return res; 165 res = hfsplus_find_cat(inode->i_sb, inode->i_ino, &fd); 166 if (res) 167 goto out; 168 hfs_bnode_read(fd.bnode, &entry, fd.entryoffset, 169 sizeof(struct hfsplus_cat_file)); 170 file = &entry.file; 171 172 if (!strcmp(name, "hfs.type")) { 173 if (size == 4) 174 memcpy(&file->user_info.fdType, value, 4); 175 else 176 res = -ERANGE; 177 } else if (!strcmp(name, "hfs.creator")) { 178 if (size == 4) 179 memcpy(&file->user_info.fdCreator, value, 4); 180 else 181 res = -ERANGE; 182 } else 183 res = -EOPNOTSUPP; 184 if (!res) { 185 hfs_bnode_write(fd.bnode, &entry, fd.entryoffset, 186 sizeof(struct hfsplus_cat_file)); 187 hfsplus_mark_inode_dirty(inode, HFSPLUS_I_CAT_DIRTY); 188 } 189 out: 190 hfs_find_exit(&fd); 191 return res; 192 } 193 194 ssize_t hfsplus_getxattr(struct dentry *dentry, const char *name, 195 void *value, size_t size) 196 { 197 struct inode *inode = dentry->d_inode; 198 struct hfs_find_data fd; 199 hfsplus_cat_entry entry; 200 struct hfsplus_cat_file *file; 201 ssize_t res = 0; 202 203 if (!S_ISREG(inode->i_mode) || HFSPLUS_IS_RSRC(inode)) 204 return -EOPNOTSUPP; 205 206 if (size) { 207 res = hfs_find_init(HFSPLUS_SB(inode->i_sb)->cat_tree, &fd); 208 if (res) 209 return res; 210 res = hfsplus_find_cat(inode->i_sb, inode->i_ino, &fd); 211 if (res) 212 goto out; 213 hfs_bnode_read(fd.bnode, &entry, fd.entryoffset, 214 sizeof(struct hfsplus_cat_file)); 215 } 216 file = &entry.file; 217 218 if (!strcmp(name, "hfs.type")) { 219 if (size >= 4) { 220 memcpy(value, &file->user_info.fdType, 4); 221 res = 4; 222 } else 223 res = size ? -ERANGE : 4; 224 } else if (!strcmp(name, "hfs.creator")) { 225 if (size >= 4) { 226 memcpy(value, &file->user_info.fdCreator, 4); 227 res = 4; 228 } else 229 res = size ? -ERANGE : 4; 230 } else 231 res = -EOPNOTSUPP; 232 out: 233 if (size) 234 hfs_find_exit(&fd); 235 return res; 236 } 237 238 #define HFSPLUS_ATTRLIST_SIZE (sizeof("hfs.creator")+sizeof("hfs.type")) 239 240 ssize_t hfsplus_listxattr(struct dentry *dentry, char *buffer, size_t size) 241 { 242 struct inode *inode = dentry->d_inode; 243 244 if (!S_ISREG(inode->i_mode) || HFSPLUS_IS_RSRC(inode)) 245 return -EOPNOTSUPP; 246 247 if (!buffer || !size) 248 return HFSPLUS_ATTRLIST_SIZE; 249 if (size < HFSPLUS_ATTRLIST_SIZE) 250 return -ERANGE; 251 strcpy(buffer, "hfs.type"); 252 strcpy(buffer + sizeof("hfs.type"), "hfs.creator"); 253 254 return HFSPLUS_ATTRLIST_SIZE; 255 } 256