1 /* 2 * This file is part of UBIFS. 3 * 4 * Copyright (C) 2006-2008 Nokia Corporation. 5 * Copyright (C) 2006, 2007 University of Szeged, Hungary 6 * 7 * This program is free software; you can redistribute it and/or modify it 8 * under the terms of the GNU General Public License version 2 as published by 9 * the Free Software Foundation. 10 * 11 * This program is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 14 * more details. 15 * 16 * You should have received a copy of the GNU General Public License along with 17 * this program; if not, write to the Free Software Foundation, Inc., 51 18 * Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 19 * 20 * Authors: Zoltan Sogor 21 * Artem Bityutskiy (Битюцкий Артём) 22 * Adrian Hunter 23 */ 24 25 /* This file implements EXT2-compatible extended attribute ioctl() calls */ 26 27 #include <linux/compat.h> 28 #include <linux/mount.h> 29 #include "ubifs.h" 30 31 /* Need to be kept consistent with checked flags in ioctl2ubifs() */ 32 #define UBIFS_SUPPORTED_IOCTL_FLAGS \ 33 (FS_COMPR_FL | FS_SYNC_FL | FS_APPEND_FL | \ 34 FS_IMMUTABLE_FL | FS_DIRSYNC_FL) 35 36 /** 37 * ubifs_set_inode_flags - set VFS inode flags. 38 * @inode: VFS inode to set flags for 39 * 40 * This function propagates flags from UBIFS inode object to VFS inode object. 41 */ 42 void ubifs_set_inode_flags(struct inode *inode) 43 { 44 unsigned int flags = ubifs_inode(inode)->flags; 45 46 inode->i_flags &= ~(S_SYNC | S_APPEND | S_IMMUTABLE | S_DIRSYNC | 47 S_ENCRYPTED); 48 if (flags & UBIFS_SYNC_FL) 49 inode->i_flags |= S_SYNC; 50 if (flags & UBIFS_APPEND_FL) 51 inode->i_flags |= S_APPEND; 52 if (flags & UBIFS_IMMUTABLE_FL) 53 inode->i_flags |= S_IMMUTABLE; 54 if (flags & UBIFS_DIRSYNC_FL) 55 inode->i_flags |= S_DIRSYNC; 56 if (flags & UBIFS_CRYPT_FL) 57 inode->i_flags |= S_ENCRYPTED; 58 } 59 60 /* 61 * ioctl2ubifs - convert ioctl inode flags to UBIFS inode flags. 62 * @ioctl_flags: flags to convert 63 * 64 * This function converts ioctl flags (@FS_COMPR_FL, etc) to UBIFS inode flags 65 * (@UBIFS_COMPR_FL, etc). 66 */ 67 static int ioctl2ubifs(int ioctl_flags) 68 { 69 int ubifs_flags = 0; 70 71 if (ioctl_flags & FS_COMPR_FL) 72 ubifs_flags |= UBIFS_COMPR_FL; 73 if (ioctl_flags & FS_SYNC_FL) 74 ubifs_flags |= UBIFS_SYNC_FL; 75 if (ioctl_flags & FS_APPEND_FL) 76 ubifs_flags |= UBIFS_APPEND_FL; 77 if (ioctl_flags & FS_IMMUTABLE_FL) 78 ubifs_flags |= UBIFS_IMMUTABLE_FL; 79 if (ioctl_flags & FS_DIRSYNC_FL) 80 ubifs_flags |= UBIFS_DIRSYNC_FL; 81 82 return ubifs_flags; 83 } 84 85 /* 86 * ubifs2ioctl - convert UBIFS inode flags to ioctl inode flags. 87 * @ubifs_flags: flags to convert 88 * 89 * This function converts UBIFS inode flags (@UBIFS_COMPR_FL, etc) to ioctl 90 * flags (@FS_COMPR_FL, etc). 91 */ 92 static int ubifs2ioctl(int ubifs_flags) 93 { 94 int ioctl_flags = 0; 95 96 if (ubifs_flags & UBIFS_COMPR_FL) 97 ioctl_flags |= FS_COMPR_FL; 98 if (ubifs_flags & UBIFS_SYNC_FL) 99 ioctl_flags |= FS_SYNC_FL; 100 if (ubifs_flags & UBIFS_APPEND_FL) 101 ioctl_flags |= FS_APPEND_FL; 102 if (ubifs_flags & UBIFS_IMMUTABLE_FL) 103 ioctl_flags |= FS_IMMUTABLE_FL; 104 if (ubifs_flags & UBIFS_DIRSYNC_FL) 105 ioctl_flags |= FS_DIRSYNC_FL; 106 107 return ioctl_flags; 108 } 109 110 static int setflags(struct inode *inode, int flags) 111 { 112 int oldflags, err, release; 113 struct ubifs_inode *ui = ubifs_inode(inode); 114 struct ubifs_info *c = inode->i_sb->s_fs_info; 115 struct ubifs_budget_req req = { .dirtied_ino = 1, 116 .dirtied_ino_d = ui->data_len }; 117 118 err = ubifs_budget_space(c, &req); 119 if (err) 120 return err; 121 122 /* 123 * The IMMUTABLE and APPEND_ONLY flags can only be changed by 124 * the relevant capability. 125 */ 126 mutex_lock(&ui->ui_mutex); 127 oldflags = ubifs2ioctl(ui->flags); 128 if ((flags ^ oldflags) & (FS_APPEND_FL | FS_IMMUTABLE_FL)) { 129 if (!capable(CAP_LINUX_IMMUTABLE)) { 130 err = -EPERM; 131 goto out_unlock; 132 } 133 } 134 135 ui->flags = ioctl2ubifs(flags); 136 ubifs_set_inode_flags(inode); 137 inode->i_ctime = current_time(inode); 138 release = ui->dirty; 139 mark_inode_dirty_sync(inode); 140 mutex_unlock(&ui->ui_mutex); 141 142 if (release) 143 ubifs_release_budget(c, &req); 144 if (IS_SYNC(inode)) 145 err = write_inode_now(inode, 1); 146 return err; 147 148 out_unlock: 149 ubifs_err(c, "can't modify inode %lu attributes", inode->i_ino); 150 mutex_unlock(&ui->ui_mutex); 151 ubifs_release_budget(c, &req); 152 return err; 153 } 154 155 long ubifs_ioctl(struct file *file, unsigned int cmd, unsigned long arg) 156 { 157 int flags, err; 158 struct inode *inode = file_inode(file); 159 160 switch (cmd) { 161 case FS_IOC_GETFLAGS: 162 flags = ubifs2ioctl(ubifs_inode(inode)->flags); 163 164 dbg_gen("get flags: %#x, i_flags %#x", flags, inode->i_flags); 165 return put_user(flags, (int __user *) arg); 166 167 case FS_IOC_SETFLAGS: { 168 if (IS_RDONLY(inode)) 169 return -EROFS; 170 171 if (!inode_owner_or_capable(inode)) 172 return -EACCES; 173 174 if (get_user(flags, (int __user *) arg)) 175 return -EFAULT; 176 177 if (flags & ~UBIFS_SUPPORTED_IOCTL_FLAGS) 178 return -EOPNOTSUPP; 179 180 if (!S_ISDIR(inode->i_mode)) 181 flags &= ~FS_DIRSYNC_FL; 182 183 /* 184 * Make sure the file-system is read-write and make sure it 185 * will not become read-only while we are changing the flags. 186 */ 187 err = mnt_want_write_file(file); 188 if (err) 189 return err; 190 dbg_gen("set flags: %#x, i_flags %#x", flags, inode->i_flags); 191 err = setflags(inode, flags); 192 mnt_drop_write_file(file); 193 return err; 194 } 195 case FS_IOC_SET_ENCRYPTION_POLICY: { 196 struct ubifs_info *c = inode->i_sb->s_fs_info; 197 198 err = ubifs_enable_encryption(c); 199 if (err) 200 return err; 201 202 return fscrypt_ioctl_set_policy(file, (const void __user *)arg); 203 } 204 case FS_IOC_GET_ENCRYPTION_POLICY: 205 return fscrypt_ioctl_get_policy(file, (void __user *)arg); 206 207 default: 208 return -ENOTTY; 209 } 210 } 211 212 #ifdef CONFIG_COMPAT 213 long ubifs_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg) 214 { 215 switch (cmd) { 216 case FS_IOC32_GETFLAGS: 217 cmd = FS_IOC_GETFLAGS; 218 break; 219 case FS_IOC32_SETFLAGS: 220 cmd = FS_IOC_SETFLAGS; 221 break; 222 case FS_IOC_SET_ENCRYPTION_POLICY: 223 case FS_IOC_GET_ENCRYPTION_POLICY: 224 break; 225 default: 226 return -ENOIOCTLCMD; 227 } 228 return ubifs_ioctl(file, cmd, (unsigned long)compat_ptr(arg)); 229 } 230 #endif 231