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