1/* 2 * linux/fs/umsdos/ioctl.c 3 * 4 * Written 1993 by Jacques Gelinas 5 * 6 * Extended MS-DOS ioctl directory handling functions 7 */ 8 9#include <asm/uaccess.h> 10#include <linux/errno.h> 11#include <linux/mm.h> 12#include <linux/kernel.h> 13#include <linux/sched.h> 14#include <linux/fs.h> 15#include <linux/msdos_fs.h> 16#include <linux/umsdos_fs.h> 17 18struct UMSDOS_DIR_ONCE { 19 struct dirent *ent; 20 int count; 21}; 22 23/* 24 * Record a single entry the first call. 25 * Return -EINVAL the next one. 26 */ 27static int umsdos_ioctl_fill ( 28 void *buf, 29 const char *name, 30 int name_len, 31 loff_t offset, 32 ino_t ino, 33 unsigned type) 34{ 35 int ret = -EINVAL; 36 struct UMSDOS_DIR_ONCE *d = (struct UMSDOS_DIR_ONCE *) buf; 37 38 if (d->count == 0) { 39 copy_to_user (d->ent->d_name, name, name_len); 40 put_user ('\0', d->ent->d_name + name_len); 41 put_user (name_len, &d->ent->d_reclen); 42 put_user (ino, &d->ent->d_ino); 43 put_user (offset, &d->ent->d_off); 44 d->count = 1; 45 ret = 0; 46 } 47 return ret; 48} 49 50 51/* 52 * Perform special function on a directory 53 */ 54/* #Specification: ioctl / prototypes 55 * The official prototype for the umsdos ioctl on directory 56 * is: 57 * 58 * int ioctl ( 59 * int fd, // File handle of the directory 60 * int cmd, // command 61 * struct umsdos_ioctl *data) 62 * 63 * The struct and the commands are defined in linux/umsdos_fs.h. 64 * 65 * umsdos_progs/umsdosio.c provide an interface in C++ to all 66 * these ioctl. umsdos_progs/udosctl is a small utility showing 67 * all this. 68 * 69 * These ioctl generally allow one to work on the EMD or the 70 * DOS directory independently. These are essential to implement 71 * the synchronise. 72 */ 73int UMSDOS_ioctl_dir(struct inode *dir, struct file *filp, unsigned int cmd, 74 unsigned long data_ptr) 75{ 76 struct dentry *dentry = filp->f_dentry; 77 struct umsdos_ioctl *idata = (struct umsdos_ioctl *) data_ptr; 78 int ret; 79 struct umsdos_ioctl data; 80 81Printk(("UMSDOS_ioctl_dir: %s/%s, cmd=%d, data=%08lx\n", 82dentry->d_parent->d_name.name, dentry->d_name.name, cmd, data_ptr)); 83 84 /* forward non-umsdos ioctls - this hopefully doesn't cause conflicts */ 85 if (cmd != UMSDOS_GETVERSION 86 && cmd != UMSDOS_READDIR_DOS 87 && cmd != UMSDOS_READDIR_EMD 88 && cmd != UMSDOS_INIT_EMD 89 && cmd != UMSDOS_CREAT_EMD 90 && cmd != UMSDOS_RENAME_DOS 91 && cmd != UMSDOS_UNLINK_EMD 92 && cmd != UMSDOS_UNLINK_DOS 93 && cmd != UMSDOS_RMDIR_DOS 94 && cmd != UMSDOS_STAT_DOS 95 && cmd != UMSDOS_DOS_SETUP) 96 return fat_dir_ioctl (dir, filp, cmd, data_ptr); 97 98 /* #Specification: ioctl / access 99 * Only root (effective id) is allowed to do IOCTL on directory 100 * in UMSDOS. EPERM is returned for other user. 101 */ 102 /* 103 * Well, not all cases require write access, but it simplifies 104 * the code, and let's face it, there is only one client (umssync) 105 * for all this. 106 */ 107 ret = verify_area (VERIFY_WRITE, (void *) data_ptr, 108 sizeof (struct umsdos_ioctl)); 109 if (ret < 0) 110 goto out; 111 112 ret = -EPERM; 113 if (current->euid != 0 && cmd != UMSDOS_GETVERSION) 114 goto out; 115 116 ret = -EINVAL; 117 if (cmd == UMSDOS_GETVERSION) { 118 /* #Specification: ioctl / UMSDOS_GETVERSION 119 * The field version and release of the structure 120 * umsdos_ioctl are filled with the version and release 121 * number of the fs code in the kernel. This will allow 122 * some form of checking. Users won't be able to run 123 * incompatible utility such as the synchroniser (umssync). 124 * umsdos_progs/umsdosio.c enforce this checking. 125 * 126 * Return always 0. 127 */ 128 put_user (UMSDOS_VERSION, &idata->version); 129 put_user (UMSDOS_RELEASE, &idata->release); 130 ret = 0; 131 goto out; 132 } 133 if (cmd == UMSDOS_READDIR_DOS) { 134 /* #Specification: ioctl / UMSDOS_READDIR_DOS 135 * One entry is read from the DOS directory at the current 136 * file position. The entry is put as is in the dos_dirent 137 * field of struct umsdos_ioctl. 138 * 139 * Return > 0 if success. 140 */ 141 struct UMSDOS_DIR_ONCE bufk; 142 143 bufk.count = 0; 144 bufk.ent = &idata->dos_dirent; 145 146 fat_readdir (filp, &bufk, umsdos_ioctl_fill); 147 148 ret = bufk.count == 1 ? 1 : 0; 149 goto out; 150 } 151 if (cmd == UMSDOS_READDIR_EMD) { 152 /* #Specification: ioctl / UMSDOS_READDIR_EMD 153 * One entry is read from the EMD at the current 154 * file position. The entry is put as is in the umsdos_dirent 155 * field of struct umsdos_ioctl. The corresponding mangled 156 * DOS entry name is put in the dos_dirent field. 157 * 158 * All entries are read including hidden links. Blank 159 * entries are skipped. 160 * 161 * Return > 0 if success. 162 */ 163 struct dentry *demd; 164 loff_t pos = filp->f_pos; 165 166 /* The absence of the EMD is simply seen as an EOF */ 167 demd = umsdos_get_emd_dentry(dentry); 168 ret = PTR_ERR(demd); 169 if (IS_ERR(demd)) 170 goto out; 171 ret = 0; 172 if (!demd->d_inode) 173 goto read_dput; 174 175 while (pos < demd->d_inode->i_size) { 176 off_t f_pos = pos; 177 struct umsdos_dirent entry; 178 struct umsdos_info info; 179 180 ret = umsdos_emd_dir_readentry (demd, &pos, &entry); 181 182 if (ret == -ENAMETOOLONG) { 183 printk (KERN_INFO "Fixing EMD entry with invalid size -- zeroing out\n"); 184 memset (&info, 0, sizeof (info)); 185 info.f_pos = f_pos; 186 info.recsize = UMSDOS_REC_SIZE; 187 ret = umsdos_writeentry (dentry, &info, 1); 188 continue; 189 } 190 191 if (ret) 192 break; 193 if (entry.name_len <= 0) 194 continue; 195 196 umsdos_parse (entry.name, entry.name_len, &info); 197 info.f_pos = f_pos; 198 umsdos_manglename (&info); 199 ret = -EFAULT; 200 if (copy_to_user (&idata->umsdos_dirent, &entry, 201 sizeof (entry))) 202 break; 203 if (copy_to_user (&idata->dos_dirent.d_name, 204 info.fake.fname, 205 info.fake.len + 1)) 206 break; 207 ret = entry.name_len; 208 break; 209 } 210 /* update the original f_pos */ 211 filp->f_pos = pos; 212 read_dput: 213 d_drop(demd); 214 dput(demd); 215 goto out; 216 } 217 if (cmd == UMSDOS_INIT_EMD) { 218 /* #Specification: ioctl / UMSDOS_INIT_EMD 219 * The UMSDOS_INIT_EMD command makes sure the EMD 220 * exists for a directory. If it does not, it is 221 * created. Also, it makes sure the directory function 222 * table (struct inode_operations) is set to the UMSDOS 223 * semantic. This mean that umssync may be applied to 224 * an "opened" msdos directory, and it will change behavior 225 * on the fly. 226 * 227 * Return 0 if success. 228 */ 229 230 ret = umsdos_make_emd(dentry); 231Printk(("UMSDOS_ioctl_dir: INIT_EMD %s/%s, ret=%d\n", 232dentry->d_parent->d_name.name, dentry->d_name.name, ret)); 233 umsdos_setup_dir (dentry); 234 goto out; 235 } 236 237 ret = -EFAULT; 238 if (copy_from_user (&data, idata, sizeof (data))) 239 goto out; 240 241 if (cmd == UMSDOS_CREAT_EMD) { 242 /* #Specification: ioctl / UMSDOS_CREAT_EMD 243 * The umsdos_dirent field of the struct umsdos_ioctl is used 244 * as is to create a new entry in the EMD of the directory. 245 * The DOS directory is not modified. 246 * No validation is done (yet). 247 * 248 * Return 0 if success. 249 */ 250 struct umsdos_info info; 251 252 /* This makes sure info.entry and info in general 253 * is correctly initialised 254 */ 255 memcpy (&info.entry, &data.umsdos_dirent, 256 sizeof (data.umsdos_dirent)); 257 umsdos_parse (data.umsdos_dirent.name 258 ,data.umsdos_dirent.name_len, &info); 259 ret = umsdos_newentry (dentry, &info); 260 goto out; 261 } 262 else if (cmd == UMSDOS_RENAME_DOS) { 263 struct dentry *old_dentry, *new_dentry; 264 265 /* #Specification: ioctl / UMSDOS_RENAME_DOS 266 * A file or directory is renamed in a DOS directory 267 * (not moved across directory). The source name 268 * is in the dos_dirent.name field and the destination 269 * is in umsdos_dirent.name field. 270 * 271 * This ioctl allows umssync to rename a mangled file 272 * name before syncing it back in the EMD. 273 */ 274 old_dentry = umsdos_lookup_dentry (dentry, 275 data.dos_dirent.d_name, 276 data.dos_dirent.d_reclen ,1); 277 ret = PTR_ERR(old_dentry); 278 if (IS_ERR(old_dentry)) 279 goto out; 280 new_dentry = umsdos_lookup_dentry (dentry, 281 data.umsdos_dirent.name, 282 data.umsdos_dirent.name_len, 1); 283 ret = PTR_ERR(new_dentry); 284 if (!IS_ERR(new_dentry)) { 285printk("umsdos_ioctl: renaming %s/%s to %s/%s\n", 286old_dentry->d_parent->d_name.name, old_dentry->d_name.name, 287new_dentry->d_parent->d_name.name, new_dentry->d_name.name); 288 ret = msdos_rename (dir, old_dentry, dir, new_dentry); 289 d_drop(new_dentry); 290 d_drop(old_dentry); 291 dput(new_dentry); 292 } 293 dput(old_dentry); 294 goto out; 295 } 296 else if (cmd == UMSDOS_UNLINK_EMD) { 297 /* #Specification: ioctl / UMSDOS_UNLINK_EMD 298 * The umsdos_dirent field of the struct umsdos_ioctl is used 299 * as is to remove an entry from the EMD of the directory. 300 * No validation is done (yet). The mode field is used 301 * to validate S_ISDIR or S_ISREG. 302 * 303 * Return 0 if success. 304 */ 305 struct umsdos_info info; 306 307 /* This makes sure info.entry and info in general 308 * is correctly initialised 309 */ 310 memcpy (&info.entry, &data.umsdos_dirent, 311 sizeof (data.umsdos_dirent)); 312 umsdos_parse (data.umsdos_dirent.name, 313 data.umsdos_dirent.name_len, &info); 314 ret = umsdos_delentry (dentry, &info, 315 S_ISDIR (data.umsdos_dirent.mode)); 316 if (ret) { 317 printk(KERN_WARNING 318 "umsdos_ioctl: delentry %s/%s failed, ret=%d\n", 319 dentry->d_name.name, info.entry.name, ret); 320 } 321 goto out; 322 } 323 else if (cmd == UMSDOS_UNLINK_DOS) { 324 struct dentry *temp; 325 326 /* #Specification: ioctl / UMSDOS_UNLINK_DOS 327 * The dos_dirent field of the struct umsdos_ioctl is used to 328 * execute a msdos_unlink operation. The d_name and d_reclen 329 * fields are used. 330 * 331 * Return 0 if success. 332 */ 333 temp = umsdos_lookup_dentry(dentry, data.dos_dirent.d_name, 334 data.dos_dirent.d_reclen, 1); 335 ret = PTR_ERR(temp); 336 if (IS_ERR(temp)) 337 goto out; 338 ret = -ENOENT; 339 if (temp->d_inode) { 340 ret = -EISDIR; 341 if (!S_ISDIR(temp->d_inode->i_mode)) 342 ret = msdos_unlink (dir, temp); 343 if (!ret) 344 d_delete(temp); 345 } 346 dput (temp); 347 goto out; 348 } 349 else if (cmd == UMSDOS_RMDIR_DOS) { 350 struct dentry *temp; 351 352 /* #Specification: ioctl / UMSDOS_RMDIR_DOS 353 * The dos_dirent field of the struct umsdos_ioctl is used to 354 * execute a msdos_rmdir operation. The d_name and d_reclen 355 * fields are used. 356 * 357 * Return 0 if success. 358 */ 359 temp = umsdos_lookup_dentry(dentry, data.dos_dirent.d_name, 360 data.dos_dirent.d_reclen, 1); 361 ret = PTR_ERR(temp); 362 if (IS_ERR(temp)) 363 goto out; 364 ret = -ENOENT; 365 if (temp->d_inode) { 366 ret = -ENOTDIR; 367 if (S_ISDIR(temp->d_inode->i_mode)) 368 ret = msdos_rmdir (dir, temp); 369 if (!ret) 370 d_delete(temp); 371 } 372 dput (temp); 373 goto out; 374 375 } else if (cmd == UMSDOS_STAT_DOS) { 376 /* #Specification: ioctl / UMSDOS_STAT_DOS 377 * The dos_dirent field of the struct umsdos_ioctl is 378 * used to execute a stat operation in the DOS directory. 379 * The d_name and d_reclen fields are used. 380 * 381 * The following field of umsdos_ioctl.stat are filled. 382 * 383 * st_ino,st_mode,st_size,st_atime,st_mtime,st_ctime, 384 * Return 0 if success. 385 */ 386 struct dentry *dret; 387 struct inode *inode; 388 389 dret = umsdos_lookup_dentry(dentry, data.dos_dirent.d_name, 390 data.dos_dirent.d_reclen, 1); 391 ret = PTR_ERR(dret); 392 if (IS_ERR(dret)) 393 goto out; 394 ret = -ENOENT; 395 inode = dret->d_inode; 396 if (inode) { 397 data.stat.st_ino = inode->i_ino; 398 data.stat.st_mode = inode->i_mode; 399 data.stat.st_size = inode->i_size; 400 data.stat.st_atime = inode->i_atime; 401 data.stat.st_ctime = inode->i_ctime; 402 data.stat.st_mtime = inode->i_mtime; 403 ret = -EFAULT; 404 if (!copy_to_user (&idata->stat, &data.stat, 405 sizeof (data.stat))) 406 ret = 0; 407 } 408 dput(dret); 409 goto out; 410 } 411 else if (cmd == UMSDOS_DOS_SETUP) { 412 /* #Specification: ioctl / UMSDOS_DOS_SETUP 413 * The UMSDOS_DOS_SETUP ioctl allow changing the 414 * default permission of the MS-DOS filesystem driver 415 * on the fly. The MS-DOS driver applies global permissions 416 * to every file and directory. Normally these permissions 417 * are controlled by a mount option. This is not 418 * available for root partition, so a special utility 419 * (umssetup) is provided to do this, normally in 420 * /etc/rc.local. 421 * 422 * Be aware that this applies ONLY to MS-DOS directories 423 * (those without EMD --linux-.---). Umsdos directory 424 * have independent (standard) permission for each 425 * and every file. 426 * 427 * The field umsdos_dirent provide the information needed. 428 * umsdos_dirent.uid and gid sets the owner and group. 429 * umsdos_dirent.mode set the permissions flags. 430 */ 431 dir->i_sb->u.msdos_sb.options.fs_uid = data.umsdos_dirent.uid; 432 dir->i_sb->u.msdos_sb.options.fs_gid = data.umsdos_dirent.gid; 433 dir->i_sb->u.msdos_sb.options.fs_umask = data.umsdos_dirent.mode; 434 ret = 0; 435 } 436out: 437 Printk (("ioctl %d, returning %d\n", cmd, ret)); 438 return ret; 439} 440