/* * linux/fs/umsdos/ioctl.c * * Written 1993 by Jacques Gelinas * * Extended MS-DOS ioctl directory handling functions */ #include #include #include #include #include #include #include #include #define PRINTK(x) #define Printk(x) printk x struct UMSDOS_DIR_ONCE { struct dirent *ent; int count; }; /* * Record a single entry the first call. * Return -EINVAL the next one. */ static int umsdos_ioctl_fill ( void *buf, const char *name, int name_len, off_t offset, ino_t ino) { int ret = -EINVAL; struct UMSDOS_DIR_ONCE *d = (struct UMSDOS_DIR_ONCE *) buf; if (d->count == 0) { copy_to_user (d->ent->d_name, name, name_len); put_user ('\0', d->ent->d_name + name_len); put_user (name_len, &d->ent->d_reclen); put_user (ino, &d->ent->d_ino); put_user (offset, &d->ent->d_off); d->count = 1; ret = 0; } return ret; } /* * Perform special function on a directory */ int UMSDOS_ioctl_dir ( struct inode *dir, struct file *filp, unsigned int cmd, unsigned long data) { int ret = -EPERM; int err; /* forward non-umsdos ioctls - this hopefully doesn't cause conflicts */ if (cmd != UMSDOS_GETVERSION && cmd != UMSDOS_READDIR_DOS && cmd != UMSDOS_READDIR_EMD && cmd != UMSDOS_INIT_EMD && cmd != UMSDOS_CREAT_EMD && cmd != UMSDOS_RENAME_DOS && cmd != UMSDOS_UNLINK_EMD && cmd != UMSDOS_UNLINK_DOS && cmd != UMSDOS_RMDIR_DOS && cmd != UMSDOS_STAT_DOS && cmd != UMSDOS_DOS_SETUP) return fat_dir_ioctl (dir, filp, cmd, data); /* #Specification: ioctl / acces * Only root (effective id) is allowed to do IOCTL on directory * in UMSDOS. EPERM is returned for other user. */ /* * Well, not all cases require write access, but it simplifies * 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 = -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. * * 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. */ 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; bufk.count = 0; bufk.ent = &idata->dos_dirent; 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); 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; ret = umsdos_emd_dir_readentry (emd_dir, filp, &entry); if (ret < 0) { break; } else if (entry.name_len > 0) { 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 = emd_dir != NULL; iput (emd_dir); /* FIXME?? */ dir->i_op = ret ? &umsdos_dir_inode_operations : &umsdos_rdir_inode_operations; } else { struct umsdos_ioctl data; 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; /* 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 */ /* #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_delentry (dir, &info ,S_ISDIR (data.umsdos_dirent.mode)); } else if (cmd == UMSDOS_UNLINK_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. */ 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); } else if (cmd == UMSDOS_RMDIR_DOS) { struct dentry *dentry, *dp; /* #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); } 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; 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; ret = 0; } } } Printk (("ioctl return %d\n", ret)); return ret; }