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/uaccess.h> 20 #include "hfsplus_fs.h" 21 22 /* 23 * "Blessing" an HFS+ filesystem writes metadata to the superblock informing 24 * the platform firmware which file to boot from 25 */ 26 static int hfsplus_ioctl_bless(struct file *file, int __user *user_flags) 27 { 28 struct dentry *dentry = file->f_path.dentry; 29 struct inode *inode = d_inode(dentry); 30 struct hfsplus_sb_info *sbi = HFSPLUS_SB(inode->i_sb); 31 struct hfsplus_vh *vh = sbi->s_vhdr; 32 struct hfsplus_vh *bvh = sbi->s_backup_vhdr; 33 u32 cnid = (unsigned long)dentry->d_fsdata; 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 /* 45 * Bootloader. Just using the inode here breaks in the case of 46 * hard links - the firmware wants the ID of the hard link file, 47 * but the inode points at the indirect inode 48 */ 49 vh->finder_info[1] = bvh->finder_info[1] = cpu_to_be32(cnid); 50 51 /* Per spec, the OS X system folder - same as finder_info[0] here */ 52 vh->finder_info[5] = bvh->finder_info[5] = 53 cpu_to_be32(parent_ino(dentry)); 54 55 mutex_unlock(&sbi->vh_mutex); 56 return 0; 57 } 58 59 static int hfsplus_ioctl_getflags(struct file *file, int __user *user_flags) 60 { 61 struct inode *inode = file_inode(file); 62 struct hfsplus_inode_info *hip = HFSPLUS_I(inode); 63 unsigned int flags = 0; 64 65 if (inode->i_flags & S_IMMUTABLE) 66 flags |= FS_IMMUTABLE_FL; 67 if (inode->i_flags & S_APPEND) 68 flags |= FS_APPEND_FL; 69 if (hip->userflags & HFSPLUS_FLG_NODUMP) 70 flags |= FS_NODUMP_FL; 71 72 return put_user(flags, user_flags); 73 } 74 75 static int hfsplus_ioctl_setflags(struct file *file, int __user *user_flags) 76 { 77 struct inode *inode = file_inode(file); 78 struct hfsplus_inode_info *hip = HFSPLUS_I(inode); 79 unsigned int flags, new_fl = 0; 80 int err = 0; 81 82 err = mnt_want_write_file(file); 83 if (err) 84 goto out; 85 86 if (!inode_owner_or_capable(inode)) { 87 err = -EACCES; 88 goto out_drop_write; 89 } 90 91 if (get_user(flags, user_flags)) { 92 err = -EFAULT; 93 goto out_drop_write; 94 } 95 96 inode_lock(inode); 97 98 if ((flags & (FS_IMMUTABLE_FL|FS_APPEND_FL)) || 99 inode->i_flags & (S_IMMUTABLE|S_APPEND)) { 100 if (!capable(CAP_LINUX_IMMUTABLE)) { 101 err = -EPERM; 102 goto out_unlock_inode; 103 } 104 } 105 106 /* don't silently ignore unsupported ext2 flags */ 107 if (flags & ~(FS_IMMUTABLE_FL|FS_APPEND_FL|FS_NODUMP_FL)) { 108 err = -EOPNOTSUPP; 109 goto out_unlock_inode; 110 } 111 112 if (flags & FS_IMMUTABLE_FL) 113 new_fl |= S_IMMUTABLE; 114 115 if (flags & FS_APPEND_FL) 116 new_fl |= S_APPEND; 117 118 inode_set_flags(inode, new_fl, S_IMMUTABLE | S_APPEND); 119 120 if (flags & FS_NODUMP_FL) 121 hip->userflags |= HFSPLUS_FLG_NODUMP; 122 else 123 hip->userflags &= ~HFSPLUS_FLG_NODUMP; 124 125 inode->i_ctime = current_time(inode); 126 mark_inode_dirty(inode); 127 128 out_unlock_inode: 129 inode_unlock(inode); 130 out_drop_write: 131 mnt_drop_write_file(file); 132 out: 133 return err; 134 } 135 136 long hfsplus_ioctl(struct file *file, unsigned int cmd, unsigned long arg) 137 { 138 void __user *argp = (void __user *)arg; 139 140 switch (cmd) { 141 case HFSPLUS_IOC_EXT2_GETFLAGS: 142 return hfsplus_ioctl_getflags(file, argp); 143 case HFSPLUS_IOC_EXT2_SETFLAGS: 144 return hfsplus_ioctl_setflags(file, argp); 145 case HFSPLUS_IOC_BLESS: 146 return hfsplus_ioctl_bless(file, argp); 147 default: 148 return -ENOTTY; 149 } 150 } 151