diff options
Diffstat (limited to 'fs/umsdos/ioctl.c')
-rw-r--r-- | fs/umsdos/ioctl.c | 570 |
1 files changed, 313 insertions, 257 deletions
diff --git a/fs/umsdos/ioctl.c b/fs/umsdos/ioctl.c index a379ba368..84e1f5034 100644 --- a/fs/umsdos/ioctl.c +++ b/fs/umsdos/ioctl.c @@ -15,9 +15,6 @@ #include <linux/msdos_fs.h> #include <linux/umsdos_fs.h> -#define PRINTK(x) -#define Printk(x) printk x - struct UMSDOS_DIR_ONCE { struct dirent *ent; int count; @@ -53,14 +50,33 @@ static int umsdos_ioctl_fill ( /* * Perform special function on a directory */ -int UMSDOS_ioctl_dir ( - struct inode *dir, - struct file *filp, - unsigned int cmd, - unsigned long data) +/* #Specification: ioctl / prototypes + * The official prototype for the umsdos ioctl on directory + * is: + * + * int ioctl ( + * int fd, // File handle of the directory + * int cmd, // command + * struct umsdos_ioctl *data) + * + * The struct and the commands are defined in linux/umsdos_fs.h. + * + * umsdos_progs/umsdosio.c provide an interface in C++ to all + * these ioctl. umsdos_progs/udosctl is a small utility showing + * all this. + * + * These ioctl generally allow one to work on the EMD or the + * DOS directory independently. These are essential to implement + * the synchronise. + */ +int UMSDOS_ioctl_dir(struct inode *dir, struct file *filp, unsigned int cmd, + unsigned long data_ptr) { - int ret = -EPERM; - int err; + struct dentry *dentry = filp->f_dentry; + struct umsdos_ioctl *idata = (struct umsdos_ioctl *) data_ptr; + int ret; + struct file new_filp; + struct umsdos_ioctl data; /* forward non-umsdos ioctls - this hopefully doesn't cause conflicts */ if (cmd != UMSDOS_GETVERSION @@ -74,7 +90,7 @@ int UMSDOS_ioctl_dir ( && cmd != UMSDOS_RMDIR_DOS && cmd != UMSDOS_STAT_DOS && cmd != UMSDOS_DOS_SETUP) - return fat_dir_ioctl (dir, filp, cmd, data); + return fat_dir_ioctl (dir, filp, cmd, data_ptr); /* #Specification: ioctl / acces * Only root (effective id) is allowed to do IOCTL on directory @@ -85,275 +101,315 @@ int UMSDOS_ioctl_dir ( * the code, and let's face it, there is only one client (umssync) * for all this. */ - if ((err = verify_area (VERIFY_WRITE, (void *) data, sizeof (struct umsdos_ioctl))) < 0) { - ret = err; - } else if (current->euid == 0 - || cmd == UMSDOS_GETVERSION) { - struct umsdos_ioctl *idata = (struct umsdos_ioctl *) data; + ret = verify_area (VERIFY_WRITE, (void *) data_ptr, + sizeof (struct umsdos_ioctl)); + if (ret < 0) + goto out; - ret = -EINVAL; - /* #Specification: ioctl / prototypes - * The official prototype for the umsdos ioctl on directory - * is: - * - * int ioctl ( - * int fd, // File handle of the directory - * int cmd, // command - * struct umsdos_ioctl *data) - * - * The struct and the commands are defined in linux/umsdos_fs.h. + ret = -EPERM; + if (current->euid != 0 && cmd != UMSDOS_GETVERSION) + goto out; + + ret = -EINVAL; + if (cmd == UMSDOS_GETVERSION) { + /* #Specification: ioctl / UMSDOS_GETVERSION + * The field version and release of the structure + * umsdos_ioctl are filled with the version and release + * number of the fs code in the kernel. This will allow + * some form of checking. Users won't be able to run + * incompatible utility such as the synchroniser (umssync). + * umsdos_progs/umsdosio.c enforce this checking. * - * umsdos_progs/umsdosio.c provide an interface in C++ to all - * these ioctl. umsdos_progs/udosctl is a small utility showing - * all this. + * Return always 0. + */ + put_user (UMSDOS_VERSION, &idata->version); + put_user (UMSDOS_RELEASE, &idata->release); + ret = 0; + goto out; + } + if (cmd == UMSDOS_READDIR_DOS) { + /* #Specification: ioctl / UMSDOS_READDIR_DOS + * One entry is read from the DOS directory at the current + * file position. The entry is put as is in the dos_dirent + * field of struct umsdos_ioctl. * - * These ioctl generally allow one to work on the EMD or the - * DOS directory independently. These are essential to implement - * the synchronise. + * Return > 0 if success. */ - Printk (("ioctl %d ", cmd)); - if (cmd == UMSDOS_GETVERSION) { - /* #Specification: ioctl / UMSDOS_GETVERSION - * The field version and release of the structure - * umsdos_ioctl are filled with the version and release - * number of the fs code in the kernel. This will allow - * some form of checking. Users won't be able to run - * incompatible utility such as the synchroniser (umssync). - * umsdos_progs/umsdosio.c enforce this checking. - * - * Return always 0. - */ - put_user (UMSDOS_VERSION, &idata->version); - put_user (UMSDOS_RELEASE, &idata->release); - ret = 0; - } else if (cmd == UMSDOS_READDIR_DOS) { - /* #Specification: ioctl / UMSDOS_READDIR_DOS - * One entry is read from the DOS directory at the current - * file position. The entry is put as is in the dos_dirent - * field of struct umsdos_ioctl. - * - * Return > 0 if success. - */ - struct UMSDOS_DIR_ONCE bufk; + struct UMSDOS_DIR_ONCE bufk; - bufk.count = 0; - bufk.ent = &idata->dos_dirent; + bufk.count = 0; + bufk.ent = &idata->dos_dirent; - fat_readdir (filp, &bufk, umsdos_ioctl_fill); + fat_readdir (filp, &bufk, umsdos_ioctl_fill); - ret = bufk.count == 1 ? 1 : 0; - } else if (cmd == UMSDOS_READDIR_EMD) { - /* #Specification: ioctl / UMSDOS_READDIR_EMD - * One entry is read from the EMD at the current - * file position. The entry is put as is in the umsdos_dirent - * field of struct umsdos_ioctl. The corresponding mangled - * DOS entry name is put in the dos_dirent field. - * - * All entries are read including hidden links. Blank - * entries are skipped. - * - * Return > 0 if success. - */ - struct inode *emd_dir = umsdos_emd_dir_lookup (dir, 0); + ret = bufk.count == 1 ? 1 : 0; + goto out; + } + if (cmd == UMSDOS_READDIR_EMD) { + /* #Specification: ioctl / UMSDOS_READDIR_EMD + * One entry is read from the EMD at the current + * file position. The entry is put as is in the umsdos_dirent + * field of struct umsdos_ioctl. The corresponding mangled + * DOS entry name is put in the dos_dirent field. + * + * All entries are read including hidden links. Blank + * entries are skipped. + * + * Return > 0 if success. + */ + struct dentry *demd; - if (emd_dir != NULL) { - while (1) { - if (filp->f_pos >= emd_dir->i_size) { - ret = 0; - break; - } else { - struct umsdos_dirent entry; - off_t f_pos = filp->f_pos; + /* The absence of the EMD is simply seen as an EOF */ + demd = umsdos_get_emd_dentry(dentry); + ret = PTR_ERR(demd); + if (IS_ERR(demd)) + goto out; + fill_new_filp(&new_filp, demd); + new_filp.f_pos = filp->f_pos; - ret = umsdos_emd_dir_readentry (emd_dir, filp, &entry); - if (ret < 0) { - break; - } else if (entry.name_len > 0) { - struct umsdos_info info; + while (1) { + off_t f_pos = new_filp.f_pos; + struct umsdos_dirent entry; + struct umsdos_info info; - ret = entry.name_len; - umsdos_parse (entry.name, entry.name_len, &info); - info.f_pos = f_pos; - umsdos_manglename (&info); - copy_to_user (&idata->umsdos_dirent, &entry - ,sizeof (entry)); - copy_to_user (&idata->dos_dirent.d_name - ,info.fake.fname, info.fake.len + 1); - break; - } - } - } - iput (emd_dir); /* FIXME? */ - } else { - /* The absence of the EMD is simply seen as an EOF */ - ret = 0; - } - } else if (cmd == UMSDOS_INIT_EMD) { - /* #Specification: ioctl / UMSDOS_INIT_EMD - * The UMSDOS_INIT_EMD command make sure the EMD - * exist for a directory. If it does not, it is - * created. Also, it makes sure the directory functions - * table (struct inode_operations) is set to the UMSDOS - * semantic. This mean that umssync may be applied to - * an "opened" msdos directory, and it will change behavior - * on the fly. - * - * Return 0 if success. - */ - extern struct inode_operations umsdos_rdir_inode_operations; - struct inode *emd_dir = umsdos_emd_dir_lookup (dir, 1); + ret = 0; + if (new_filp.f_pos >= demd->d_inode->i_size) + break; - ret = emd_dir != NULL; - iput (emd_dir); /* FIXME?? */ + ret = umsdos_emd_dir_readentry (&new_filp, &entry); + if (ret < 0) + break; + if (entry.name_len <= 0) + continue; - dir->i_op = ret - ? &umsdos_dir_inode_operations - : &umsdos_rdir_inode_operations; - } else { - struct umsdos_ioctl data; + umsdos_parse (entry.name, entry.name_len, &info); + info.f_pos = f_pos; + umsdos_manglename (&info); + ret = -EFAULT; + if (copy_to_user (&idata->umsdos_dirent, &entry, + sizeof (entry))) + break; + if (copy_to_user (&idata->dos_dirent.d_name, + info.fake.fname, + info.fake.len + 1)) + break; + ret = entry.name_len; + break; + } + /* update the original f_pos */ + filp->f_pos = new_filp.f_pos; + d_drop(demd); + dput(demd); + goto out; + } + if (cmd == UMSDOS_INIT_EMD) { + /* #Specification: ioctl / UMSDOS_INIT_EMD + * The UMSDOS_INIT_EMD command makes sure the EMD + * exists for a directory. If it does not, it is + * created. Also, it makes sure the directory function + * table (struct inode_operations) is set to the UMSDOS + * semantic. This mean that umssync may be applied to + * an "opened" msdos directory, and it will change behavior + * on the fly. + * + * Return 0 if success. + */ + extern struct inode_operations umsdos_rdir_inode_operations; + + ret = umsdos_make_emd(dentry); + dir->i_op = (ret == 0) + ? &umsdos_dir_inode_operations + : &umsdos_rdir_inode_operations; + goto out; + } - copy_from_user (&data, idata, sizeof (data)); - if (cmd == UMSDOS_CREAT_EMD) { - /* #Specification: ioctl / UMSDOS_CREAT_EMD - * The umsdos_dirent field of the struct umsdos_ioctl is used - * as is to create a new entry in the EMD of the directory. - * The DOS directory is not modified. - * No validation is done (yet). - * - * Return 0 if success. - */ - struct umsdos_info info; + ret = -EFAULT; + if (copy_from_user (&data, idata, sizeof (data))) + goto out; - /* This makes sure info.entry and info in general is correctly */ - /* initialised */ - memcpy (&info.entry, &data.umsdos_dirent - ,sizeof (data.umsdos_dirent)); - umsdos_parse (data.umsdos_dirent.name - ,data.umsdos_dirent.name_len, &info); - ret = umsdos_newentry (dir, &info); - } else if (cmd == UMSDOS_RENAME_DOS) { - struct dentry *old_dentry, *new_dentry; /* FIXME */ + if (cmd == UMSDOS_CREAT_EMD) { + /* #Specification: ioctl / UMSDOS_CREAT_EMD + * The umsdos_dirent field of the struct umsdos_ioctl is used + * as is to create a new entry in the EMD of the directory. + * The DOS directory is not modified. + * No validation is done (yet). + * + * Return 0 if success. + */ + struct umsdos_info info; - /* #Specification: ioctl / UMSDOS_RENAME_DOS - * A file or directory is rename in a DOS directory - * (not moved across directory). The source name - * is in the dos_dirent.name field and the destination - * is in umsdos_dirent.name field. - * - * This ioctl allows umssync to rename a mangle file - * name before syncing it back in the EMD. - */ - inc_count (dir); - inc_count (dir); - /* - * ret = msdos_rename (dir - * ,data.dos_dirent.d_name,data.dos_dirent.d_reclen - * ,dir - * ,data.umsdos_dirent.name,data.umsdos_dirent.name_len); - */ - old_dentry = creat_dentry (data.dos_dirent.d_name, data.dos_dirent.d_reclen, NULL, geti_dentry (dir)); /* FIXME: prolly should fill inode part */ - new_dentry = creat_dentry (data.umsdos_dirent.name, data.umsdos_dirent.name_len, NULL, geti_dentry (dir)); - ret = msdos_rename (dir, old_dentry, dir, new_dentry); - } else if (cmd == UMSDOS_UNLINK_EMD) { - /* #Specification: ioctl / UMSDOS_UNLINK_EMD - * The umsdos_dirent field of the struct umsdos_ioctl is used - * as is to remove an entry from the EMD of the directory. - * No validation is done (yet). The mode field is used - * to validate S_ISDIR or S_ISREG. - * - * Return 0 if success. - */ - struct umsdos_info info; + /* This makes sure info.entry and info in general + * is correctly initialised + */ + memcpy (&info.entry, &data.umsdos_dirent, + sizeof (data.umsdos_dirent)); + umsdos_parse (data.umsdos_dirent.name + ,data.umsdos_dirent.name_len, &info); + ret = umsdos_newentry (dentry, &info); + goto out; + } + else if (cmd == UMSDOS_RENAME_DOS) { + struct dentry *old_dentry, *new_dentry; /* FIXME */ - /* This makes sure info.entry and info in general is correctly */ - /* initialised */ - memcpy (&info.entry, &data.umsdos_dirent - ,sizeof (data.umsdos_dirent)); - umsdos_parse (data.umsdos_dirent.name - ,data.umsdos_dirent.name_len, &info); - ret = umsdos_delentry (dir, &info - ,S_ISDIR (data.umsdos_dirent.mode)); - } else if (cmd == UMSDOS_UNLINK_DOS) { - struct dentry *dentry, *dp; + /* #Specification: ioctl / UMSDOS_RENAME_DOS + * A file or directory is renamed in a DOS directory + * (not moved across directory). The source name + * is in the dos_dirent.name field and the destination + * is in umsdos_dirent.name field. + * + * This ioctl allows umssync to rename a mangle file + * name before syncing it back in the EMD. + */ + old_dentry = umsdos_lookup_dentry (dentry, + data.dos_dirent.d_name, + data.dos_dirent.d_reclen); + ret = PTR_ERR(old_dentry); + if (IS_ERR(old_dentry)) + goto out; + new_dentry = umsdos_lookup_dentry (dentry, + data.umsdos_dirent.name, + data.umsdos_dirent.name_len); + ret = PTR_ERR(new_dentry); + if (!IS_ERR(new_dentry)) { +printk("umsdos_ioctl: renaming %s/%s to %s/%s\n", +old_dentry->d_parent->d_name.name, old_dentry->d_name.name, +new_dentry->d_parent->d_name.name, new_dentry->d_name.name); + ret = msdos_rename (dir, old_dentry, dir, new_dentry); + dput(new_dentry); + } + d_drop(old_dentry); + dput(old_dentry); + goto out; + } + else if (cmd == UMSDOS_UNLINK_EMD) { + /* #Specification: ioctl / UMSDOS_UNLINK_EMD + * The umsdos_dirent field of the struct umsdos_ioctl is used + * as is to remove an entry from the EMD of the directory. + * No validation is done (yet). The mode field is used + * to validate S_ISDIR or S_ISREG. + * + * Return 0 if success. + */ + struct umsdos_info info; - /* #Specification: ioctl / UMSDOS_UNLINK_DOS - * The dos_dirent field of the struct umsdos_ioctl is used to - * execute a msdos_unlink operation. The d_name and d_reclen - * fields are used. - * - * Return 0 if success. - */ - inc_count (dir); - dp = creat_dentry (data.dos_dirent.d_name, data.dos_dirent.d_reclen, dir, NULL); - dentry = creat_dentry ("ioctl_unlink", 12, NULL, dp); - ret = msdos_unlink (dir, dentry); + /* This makes sure info.entry and info in general + * is correctly initialised + */ + memcpy (&info.entry, &data.umsdos_dirent, + sizeof (data.umsdos_dirent)); + umsdos_parse (data.umsdos_dirent.name, + data.umsdos_dirent.name_len, &info); + ret = umsdos_delentry (dentry, &info, + S_ISDIR (data.umsdos_dirent.mode)); + goto out; + } + else if (cmd == UMSDOS_UNLINK_DOS) { + struct dentry *temp; - } else if (cmd == UMSDOS_RMDIR_DOS) { - struct dentry *dentry, *dp; + /* #Specification: ioctl / UMSDOS_UNLINK_DOS + * The dos_dirent field of the struct umsdos_ioctl is used to + * execute a msdos_unlink operation. The d_name and d_reclen + * fields are used. + * + * Return 0 if success. + */ + temp = umsdos_lookup_dentry(dentry, data.dos_dirent.d_name, + data.dos_dirent.d_reclen); + ret = PTR_ERR(temp); + if (IS_ERR(temp)) + goto out; + ret = -ENOENT; + if (temp->d_inode) + ret = msdos_unlink (dir, temp); + d_drop(temp); + dput (temp); + goto out; + } + else if (cmd == UMSDOS_RMDIR_DOS) { + struct dentry *temp; - /* #Specification: ioctl / UMSDOS_RMDIR_DOS - * The dos_dirent field of the struct umsdos_ioctl is used to - * execute a msdos_unlink operation. The d_name and d_reclen - * fields are used. - * - * Return 0 if success. - */ - inc_count (dir); - dp = creat_dentry (data.dos_dirent.d_name, data.dos_dirent.d_reclen, dir, NULL); - dentry = creat_dentry ("ioctl_unlink", 12, NULL, dp); - ret = msdos_rmdir (dir, dentry); + /* #Specification: ioctl / UMSDOS_RMDIR_DOS + * The dos_dirent field of the struct umsdos_ioctl is used to + * execute a msdos_rmdir operation. The d_name and d_reclen + * fields are used. + * + * Return 0 if success. + */ + temp = umsdos_lookup_dentry(dentry, data.dos_dirent.d_name, + data.dos_dirent.d_reclen); + ret = PTR_ERR(temp); + if (IS_ERR(temp)) + goto out; + ret = -ENOENT; + if (temp->d_inode) + ret = msdos_rmdir (dir, temp); + d_drop(temp); + dput (temp); + goto out; - } else if (cmd == UMSDOS_STAT_DOS) { - /* #Specification: ioctl / UMSDOS_STAT_DOS - * The dos_dirent field of the struct umsdos_ioctl is - * used to execute a stat operation in the DOS directory. - * The d_name and d_reclen fields are used. - * - * The following field of umsdos_ioctl.stat are filled. - * - * st_ino,st_mode,st_size,st_atime,st_mtime,st_ctime, - * Return 0 if success. - */ - struct inode *inode; + } else if (cmd == UMSDOS_STAT_DOS) { + /* #Specification: ioctl / UMSDOS_STAT_DOS + * The dos_dirent field of the struct umsdos_ioctl is + * used to execute a stat operation in the DOS directory. + * The d_name and d_reclen fields are used. + * + * The following field of umsdos_ioctl.stat are filled. + * + * st_ino,st_mode,st_size,st_atime,st_mtime,st_ctime, + * Return 0 if success. + */ + struct dentry *dret; + struct inode *inode; - ret = compat_umsdos_real_lookup (dir, data.dos_dirent.d_name, data.dos_dirent.d_reclen, &inode); - if (ret == 0) { - data.stat.st_ino = inode->i_ino; - data.stat.st_mode = inode->i_mode; - data.stat.st_size = inode->i_size; - data.stat.st_atime = inode->i_atime; - data.stat.st_ctime = inode->i_ctime; - data.stat.st_mtime = inode->i_mtime; - copy_to_user (&idata->stat, &data.stat, sizeof (data.stat)); - /* iput (inode); FIXME */ - } - } else if (cmd == UMSDOS_DOS_SETUP) { - /* #Specification: ioctl / UMSDOS_DOS_SETUP - * The UMSDOS_DOS_SETUP ioctl allow changing the - * default permission of the MsDOS file system driver - * on the fly. The MsDOS driver apply global permission - * to every file and directory. Normally these permissions - * are controlled by a mount option. This is not - * available for root partition, so a special utility - * (umssetup) is provided to do this, normally in - * /etc/rc.local. - * - * Be aware that this apply ONLY to MsDOS directory - * (those without EMD --linux-.---). Umsdos directory - * have independent (standard) permission for each - * and every file. - * - * The field umsdos_dirent provide the information needed. - * umsdos_dirent.uid and gid sets the owner and group. - * umsdos_dirent.mode set the permissions flags. - */ - dir->i_sb->u.msdos_sb.options.fs_uid = data.umsdos_dirent.uid; - dir->i_sb->u.msdos_sb.options.fs_gid = data.umsdos_dirent.gid; - dir->i_sb->u.msdos_sb.options.fs_umask = data.umsdos_dirent.mode; + dret = umsdos_lookup_dentry(dentry, data.dos_dirent.d_name, + data.dos_dirent.d_reclen); + ret = PTR_ERR(dret); + if (IS_ERR(dret)) + goto out; + ret = -ENOENT; + inode = dret->d_inode; + if (inode) { + data.stat.st_ino = inode->i_ino; + data.stat.st_mode = inode->i_mode; + data.stat.st_size = inode->i_size; + data.stat.st_atime = inode->i_atime; + data.stat.st_ctime = inode->i_ctime; + data.stat.st_mtime = inode->i_mtime; + ret = -EFAULT; + if (!copy_to_user (&idata->stat, &data.stat, + sizeof (data.stat))) ret = 0; - } } + d_drop(dret); + dput(dret); + goto out; + } + else if (cmd == UMSDOS_DOS_SETUP) { + /* #Specification: ioctl / UMSDOS_DOS_SETUP + * The UMSDOS_DOS_SETUP ioctl allow changing the + * default permission of the MS-DOS filesystem driver + * on the fly. The MS-DOS driver applies global permissions + * to every file and directory. Normally these permissions + * are controlled by a mount option. This is not + * available for root partition, so a special utility + * (umssetup) is provided to do this, normally in + * /etc/rc.local. + * + * Be aware that this applies ONLY to MS-DOS directories + * (those without EMD --linux-.---). Umsdos directory + * have independent (standard) permission for each + * and every file. + * + * The field umsdos_dirent provide the information needed. + * umsdos_dirent.uid and gid sets the owner and group. + * umsdos_dirent.mode set the permissions flags. + */ + dir->i_sb->u.msdos_sb.options.fs_uid = data.umsdos_dirent.uid; + dir->i_sb->u.msdos_sb.options.fs_gid = data.umsdos_dirent.gid; + dir->i_sb->u.msdos_sb.options.fs_umask = data.umsdos_dirent.mode; + ret = 0; } - Printk (("ioctl return %d\n", ret)); +out: + Printk (("ioctl %d, returning %d\n", cmd, ret)); return ret; } |