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