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