int umsdos_isempty (struct dentry *dentry) { struct dentry *demd; int ret = 2; struct file filp; demd = umsdos_get_emd_dentry(dentry); if (IS_ERR(demd)) goto out; /* If the EMD file does not exist, it is certainly empty. :-) */ if (!demd->d_inode) goto out_dput; fill_new_filp (&filp, demd); filp.f_flags = O_RDONLY; ret = 1; while (filp.f_pos < demd->d_inode->i_size) { struct umsdos_dirent entry; if (umsdos_emd_dir_readentry (&filp, &entry) != 0) { ret = 0; break; } if (entry.name_len != 0) { ret = 0; break; } } out_dput: dput(demd); out: Printk(("umsdos_isempty: checked %s/%s, empty=%d\n", dentry->d_parent->d_name.name, dentry->d_name.name, ret)); return ret; }
/* #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) { 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; Printk(("UMSDOS_ioctl_dir: %s/%s, cmd=%d, data=%08lx\n", dentry->d_parent->d_name.name, dentry->d_name.name, cmd, data_ptr)); /* 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_ptr); /* #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. */ ret = verify_area (VERIFY_WRITE, (void *) data_ptr, sizeof (struct umsdos_ioctl)); if (ret < 0) goto out; 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. * * 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. * * 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; 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; /* 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; ret = 0; if (!demd->d_inode) goto read_dput; fill_new_filp(&new_filp, demd); new_filp.f_pos = filp->f_pos; while (new_filp.f_pos < demd->d_inode->i_size) { off_t f_pos = new_filp.f_pos; struct umsdos_dirent entry; struct umsdos_info info; ret = umsdos_emd_dir_readentry (&new_filp, &entry); if (ret) break; if (entry.name_len <= 0) continue; 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; read_dput: 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); Printk(("UMSDOS_ioctl_dir: INIT_EMD %s/%s, ret=%d\n", dentry->d_parent->d_name.name, dentry->d_name.name, ret)); dir->i_op = (ret == 0) ? &umsdos_dir_inode_operations : &umsdos_rdir_inode_operations; goto out; } ret = -EFAULT; if (copy_from_user (&data, idata, sizeof (data))) goto out; 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 (dentry, &info); goto out; } else if (cmd == UMSDOS_RENAME_DOS) { struct dentry *old_dentry, *new_dentry; /* FIXME */ /* #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 mangled 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 ,1); 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, 1); 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); } 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; /* 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)); if (ret) { printk(KERN_WARNING "umsdos_ioctl: delentry %s/%s failed, ret=%d\n", dentry->d_name.name, info.entry.name, ret); } goto out; } else if (cmd == UMSDOS_UNLINK_DOS) { struct dentry *temp; /* #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, 1); ret = PTR_ERR(temp); if (IS_ERR(temp)) goto out; ret = -ENOENT; if (temp->d_inode) { ret = -EISDIR; if (!S_ISDIR(temp->d_inode->i_mode)) ret = msdos_unlink (dir, 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_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, 1); ret = PTR_ERR(temp); if (IS_ERR(temp)) goto out; ret = -ENOENT; if (temp->d_inode) { ret = -ENOTDIR; if (S_ISDIR(temp->d_inode->i_mode)) ret = msdos_rmdir (dir, 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 dentry *dret; struct inode *inode; dret = umsdos_lookup_dentry(dentry, data.dos_dirent.d_name, data.dos_dirent.d_reclen, 1); 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; } 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; } out: Printk (("ioctl %d, returning %d\n", cmd, ret)); return ret; }
static int umsdos_find (struct dentry *parent, struct umsdos_info *info) { struct umsdos_dirent *entry = &info->entry; int recsize = info->recsize; struct dentry *demd; struct inode *emd_dir; int ret = -ENOENT; struct find_buffer buf; struct { off_t posok; /* Position available to store the entry */ int found; /* A valid empty position has been found. */ off_t one; /* One empty position -> maybe <- large enough */ int onesize; /* size of empty region starting at one */ } empty; Printk (("umsdos_find: locating %s in %s/%s\n", entry->name, parent->d_parent->d_name.name, parent->d_name.name)); /* * Lookup the EMD file in the parent directory. */ demd = umsdos_get_emd_dentry(parent); ret = PTR_ERR(demd); if (IS_ERR(demd)) goto out; /* make sure there's an EMD file ... */ ret = -ENOENT; emd_dir = demd->d_inode; if (!emd_dir) goto out_dput; Printk(("umsdos_find: found EMD file %s/%s, ino=%p\n", demd->d_parent->d_name.name, demd->d_name.name, emd_dir)); fill_new_filp (&buf.filp, demd); buf.pos = 0; buf.size = 0; empty.found = 0; empty.posok = emd_dir->i_size; empty.onesize = 0; while (1) { struct umsdos_dirent *rentry = (struct umsdos_dirent *) (buf.buffer + buf.pos); int file_pos = buf.filp.f_pos - buf.size + buf.pos; if (buf.pos == buf.size) { ret = umsdos_fillbuf (&buf); if (ret < 0) { /* Not found, so note where it can be added */ info->f_pos = empty.posok; break; } } else if (rentry->name_len == 0) { /* We are looking for an empty section at least */ /* as large as recsize. */ if (entry->name_len == 0) { info->f_pos = file_pos; ret = 0; break; } else if (!empty.found) { if (empty.onesize == 0) { /* This is the first empty record of a section. */ empty.one = file_pos; } /* grow the empty section */ empty.onesize += UMSDOS_REC_SIZE; if (empty.onesize == recsize) { /* Here is a large enough section. */ empty.posok = empty.one; empty.found = 1; } } buf.pos += UMSDOS_REC_SIZE; } else { int entry_size = umsdos_evalrecsize (rentry->name_len); if (buf.pos + entry_size > buf.size) { ret = umsdos_fillbuf (&buf); if (ret < 0) { /* Not found, so note where it can be added */ info->f_pos = empty.posok; break; } } else { empty.onesize = 0; /* Reset the free slot search. */ if (entry->name_len == rentry->name_len && memcmp (entry->name, rentry->name, rentry->name_len) == 0) { info->f_pos = file_pos; *entry = *rentry; ret = 0; break; } else { buf.pos += entry_size; } } } } Printk(("umsdos_find: ready to mangle %s, len=%d, pos=%ld\n", entry->name, entry->name_len, (long)info->f_pos)); umsdos_manglename (info); out_dput: dput(demd); out: Printk (("umsdos_find: returning %d\n", ret)); return ret; }
/* * Write an entry in the EMD file. * Return 0 if OK, -EIO if some error. * * Note: the caller must hold a lock on the parent directory. */ static int umsdos_writeentry (struct dentry *parent, struct umsdos_info *info, int free_entry) { struct inode *dir = parent->d_inode; struct umsdos_dirent *entry = &info->entry; struct dentry *emd_dentry; int ret; struct umsdos_dirent entry0; struct file filp; emd_dentry = umsdos_get_emd_dentry(parent); ret = PTR_ERR(emd_dentry); if (IS_ERR(emd_dentry)) goto out; /* make sure there's an EMD file */ ret = -EIO; if (!emd_dentry->d_inode) { printk(KERN_WARNING "umsdos_writeentry: no EMD file in %s/%s\n", parent->d_parent->d_name.name, parent->d_name.name); goto out_dput; } if (free_entry) { /* #Specification: EMD file / empty entries * Unused entries in the EMD file are identified * by the name_len field equal to 0. However to * help future extension (or bug correction :-( ), * empty entries are filled with 0. */ memset (&entry0, 0, sizeof (entry0)); entry = &entry0; } else if (entry->name_len > 0) { memset (entry->name + entry->name_len, '\0', sizeof (entry->name) - entry->name_len); /* #Specification: EMD file / spare bytes * 10 bytes are unused in each record of the EMD. They * are set to 0 all the time, so it will be possible * to do new stuff and rely on the state of those * bytes in old EMD files. */ memset (entry->spare, 0, sizeof (entry->spare)); } fill_new_filp (&filp, emd_dentry); filp.f_pos = info->f_pos; filp.f_reada = 0; filp.f_flags = O_RDWR; /* write the entry and update the parent timestamps */ ret = umsdos_emd_dir_write (&filp, (char *) entry, info->recsize); if (!ret) { dir->i_ctime = dir->i_mtime = CURRENT_TIME; mark_inode_dirty(dir); } else printk ("UMSDOS: problem with EMD file: can't write\n"); out_dput: dput(emd_dentry); out: Printk (("umsdos_writeentry /mn/: returning %d...\n", ret)); return ret; }