示例#1
0
/*
 * Create the EMD file for a directory if it doesn't
 * already exist. Returns 0 or an error code.
 *
 * Note: the caller must hold a lock on the parent directory.
 */
int umsdos_make_emd(struct dentry *parent)
{
	struct dentry *demd = umsdos_get_emd_dentry(parent);
	int err = PTR_ERR(demd);

	if (IS_ERR(demd)) {
		printk("umsdos_make_emd: can't get dentry in %s, err=%d\n",
			parent->d_name.name, err);
		goto out;
	}

	/* already created? */
	err = 0;
	if (demd->d_inode)
		goto out_set;

Printk(("umsdos_make_emd: creating EMD %s/%s\n",
parent->d_name.name, demd->d_name.name));

	err = msdos_create(parent->d_inode, demd, S_IFREG | 0777);
	if (err) {
		printk (KERN_WARNING
			"umsdos_make_emd: create %s/%s failed, err=%d\n",
			parent->d_name.name, demd->d_name.name, err);
		goto out_dput;
	}
out_set:
	parent->d_inode->u.umsdos_i.i_emd_dir = demd->d_inode->i_ino;

out_dput:
	dput(demd);
out:
	return err;
}
示例#2
0
/*
 * Check whether a directory has an EMD file.
 *
 * Note: the caller must hold a lock on the parent directory.
 */
int umsdos_have_emd(struct dentry *dir)
{
	struct dentry *demd = umsdos_get_emd_dentry (dir);
	int found = 0;

	if (!IS_ERR(demd)) {
		if (demd->d_inode)
			found = 1;
		dput(demd);
	}
	return found;
}
示例#3
0
文件: rdir.c 项目: dmgerman/original
/* #Specification: pseudo root / rmdir /DOS
 * The pseudo sub-directory /DOS can't be removed!
 * This is done even if the pseudo root is not a Umsdos
 * directory anymore (very unlikely), but an accident (under
 * MS-DOS) is always possible.
 * 
 * EPERM is returned.
 */
static int UMSDOS_rrmdir ( struct inode *dir, struct dentry *dentry)
{
	int ret, empty;

	ret = -EPERM;
	if (umsdos_is_pseudodos (dir, dentry))
		goto out;

	ret = -EBUSY;
	if (!d_unhashed(dentry))
		goto out;

	ret = msdos_rmdir (dir, dentry);
	if (ret != -ENOTEMPTY)
		goto out;

	empty = umsdos_isempty (dentry);
	if (empty == 1) {
		struct dentry *demd;
		/* We have to remove the EMD file. */
		demd = umsdos_get_emd_dentry(dentry);
		ret = PTR_ERR(demd);
		if (!IS_ERR(demd)) {
			ret = 0;
			if (demd->d_inode)
				ret = msdos_unlink (dentry->d_inode, demd);
			if (!ret)
				d_delete(demd);
			dput(demd);
		}
	}
	if (ret)
		goto out;

	/* now retry the original ... */
	ret = msdos_rmdir (dir, dentry);

out:
	return ret;
}
示例#4
0
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;
}
示例#5
0
/* #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;
}
示例#6
0
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;
}
示例#7
0
/*
 * 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;
}
示例#8
0
static int umsdos_readdir_x (struct inode *dir, struct file *filp,
				void *dirbuf, struct umsdos_dirent *u_entry,
				filldir_t filldir)
{
	struct dentry *demd;
	off_t start_fpos;
	int ret = 0;
	loff_t pos;

	umsdos_startlookup (dir);

	if (filp->f_pos == UMSDOS_SPECIAL_DIRFPOS && dir == pseudo_root) {

		/*
		 * We don't need to simulate this pseudo directory
		 * when umsdos_readdir_x is called for internal operation
		 * of umsdos. This is why dirent_in_fs is tested
		 */
		/* #Specification: pseudo root / directory /DOS
		 * When umsdos operates in pseudo root mode (C:\linux is the
		 * linux root), it simulate a directory /DOS which points to
		 * the real root of the file system.
		 */

		Printk ((KERN_WARNING "umsdos_readdir_x: pseudo_root thing UMSDOS_SPECIAL_DIRFPOS\n"));
		if (filldir (dirbuf, "DOS", 3, 
				UMSDOS_SPECIAL_DIRFPOS, UMSDOS_ROOT_INO, DT_DIR) == 0) {
			filp->f_pos++;
		}
		goto out_end;
	}

	if (filp->f_pos < 2 || 
	    (dir->i_ino != UMSDOS_ROOT_INO && filp->f_pos == 32)) {
	
		int last_f_pos = filp->f_pos;
		struct UMSDOS_DIR_ONCE bufk;

		Printk (("umsdos_readdir_x: . or .. /mn/?\n"));

		bufk.dirbuf = dirbuf;
		bufk.filldir = filldir;
		bufk.count = 0;

		ret = fat_readdir (filp, &bufk, umsdos_dir_once);
		if (last_f_pos > 0 && filp->f_pos > last_f_pos)
			filp->f_pos = UMSDOS_SPECIAL_DIRFPOS;
		if (u_entry != NULL)
			u_entry->flags = 0;
		goto out_end;
	}

	Printk (("umsdos_readdir_x: normal file /mn/?\n"));

	/* get the EMD dentry */
	demd = umsdos_get_emd_dentry(filp->f_dentry);
	ret = PTR_ERR(demd);
	if (IS_ERR(demd))
		goto out_end;
	ret = -EIO;
	if (!demd->d_inode) {
		printk(KERN_WARNING 
			"umsdos_readir_x: EMD file %s/%s not found\n",
			demd->d_parent->d_name.name, demd->d_name.name);
		goto out_dput;
	}

	pos = filp->f_pos;
	start_fpos = filp->f_pos;

	if (pos <= UMSDOS_SPECIAL_DIRFPOS + 1)
		pos = 0;
	ret = 0;
	while (pos < demd->d_inode->i_size) {
		off_t cur_f_pos = pos;
		struct dentry *dret;
		struct inode *inode;
		struct umsdos_dirent entry;
		struct umsdos_info info;

		ret = -EIO;
		if (umsdos_emd_dir_readentry (demd, &pos, &entry) != 0)
			break;
		if (entry.name_len == 0)
			continue;
#ifdef UMSDOS_DEBUG_VERBOSE
if (entry.flags & UMSDOS_HLINK)
printk("umsdos_readdir_x: %s/%s is hardlink\n",
filp->f_dentry->d_name.name, entry.name);
#endif

		umsdos_parse (entry.name, entry.name_len, &info);
		info.f_pos = cur_f_pos;
		umsdos_manglename (&info);
		/*
		 * Do a real lookup on the short name.
		 */
		dret = umsdos_covered(filp->f_dentry, info.fake.fname,
						 info.fake.len);
		ret = PTR_ERR(dret);
		if (IS_ERR(dret))
			break;
		/*
		 * If the file wasn't found, remove it from the EMD.
		 */
		inode = dret->d_inode;
		if (!inode)
			goto remove_name;
#ifdef UMSDOS_DEBUG_VERBOSE
if (inode->u.umsdos_i.i_is_hlink)
printk("umsdos_readdir_x: %s/%s already resolved, ino=%ld\n",
dret->d_parent->d_name.name, dret->d_name.name, inode->i_ino);
#endif

Printk (("Found %s/%s, ino=%ld, flags=%x\n",
dret->d_parent->d_name.name, info.fake.fname, dret->d_inode->i_ino,
entry.flags));
		/* check whether to resolve a hard-link */
		if ((entry.flags & UMSDOS_HLINK) &&
		    !inode->u.umsdos_i.i_is_hlink) {
			dret = umsdos_solve_hlink (dret);
			ret = PTR_ERR(dret);
			if (IS_ERR(dret))
				break;
			inode = dret->d_inode;
			if (!inode) {
printk("umsdos_readdir_x: %s/%s negative after link\n",
dret->d_parent->d_name.name, dret->d_name.name);
				goto clean_up;
			}
		}

		/* #Specification:  pseudo root / reading real root
		 * The pseudo root (/linux) is logically
		 * erased from the real root.  This means that
		 * ls /DOS, won't show "linux". This avoids
		 * infinite recursion (/DOS/linux/DOS/linux/...) while
		 * walking the file system.
		 */
		if (inode != pseudo_root && !(entry.flags & UMSDOS_HIDDEN)) {
			if (filldir (dirbuf, entry.name, entry.name_len,
				 cur_f_pos, inode->i_ino, DT_UNKNOWN) < 0) {
				pos = cur_f_pos;
			}
Printk(("umsdos_readdir_x: got %s/%s, ino=%ld\n",
dret->d_parent->d_name.name, dret->d_name.name, inode->i_ino));
			if (u_entry != NULL)
				*u_entry = entry;
			dput(dret);
			ret = 0;
			break;
		}
	clean_up:
		dput(dret);
		continue;

	remove_name:
		/* #Specification:  umsdos / readdir / not in MSDOS
		 * During a readdir operation, if the file is not
		 * in the MS-DOS directory any more, the entry is
		 * removed from the EMD file silently.
		 */
#ifdef UMSDOS_PARANOIA
printk("umsdos_readdir_x: %s/%s out of sync, erasing\n",
filp->f_dentry->d_name.name, info.entry.name);
#endif
		ret = umsdos_delentry(filp->f_dentry, &info, 
					S_ISDIR(info.entry.mode));
		if (ret)
			printk(KERN_WARNING 
				"umsdos_readdir_x: delentry %s, err=%d\n",
				info.entry.name, ret);
		goto clean_up;
	}
	/*
	 * If the fillbuf has failed, f_pos is back to 0.
	 * To avoid getting back into the . and .. state
	 * (see comments at the beginning), we put back
	 * the special offset.
	 */
	filp->f_pos = pos;
	if (filp->f_pos == 0)
		filp->f_pos = start_fpos;
out_dput:
	dput(demd);

out_end:
	umsdos_endlookup (dir);
	
	Printk ((KERN_DEBUG "read dir %p pos %Ld ret %d\n",
		dir, filp->f_pos, ret));
	return ret;
}
示例#9
0
struct dentry *umsdos_solve_hlink (struct dentry *hlink)
{
	/* root is our root for resolving pseudo-hardlink */
	struct dentry *base = hlink->d_sb->s_root;
	struct dentry *dentry_dst;
	char *path, *pt;
	int len;
	struct address_space *mapping = hlink->d_inode->i_mapping;
	struct page *page;

	page=read_cache_page(mapping,0,(filler_t *)mapping->a_ops->readpage,NULL);
	dentry_dst=(struct dentry *)page;
	if (IS_ERR(page))
		goto out;
	wait_on_page(page);
	if (!Page_Uptodate(page))
		goto async_fail;

	dentry_dst = ERR_PTR(-ENOMEM);
	path = (char *) kmalloc (PATH_MAX, GFP_KERNEL);
	if (path == NULL)
		goto out_release;
	memcpy(path, kmap(page), hlink->d_inode->i_size);
	kunmap(page);
	page_cache_release(page);

	len = hlink->d_inode->i_size;

	/* start at root dentry */
	dentry_dst = dget(base);
	path[len] = '\0';
	
	pt = path;
	if (*path == '/')
		pt++; /* skip leading '/' */
	
	if (base->d_inode == pseudo_root)
		pt += (UMSDOS_PSDROOT_LEN + 1);
	
	while (1) {
		struct dentry *dir = dentry_dst, *demd;
		char *start = pt;
		int real;

		while (*pt != '\0' && *pt != '/') pt++;
		len = (int) (pt - start);
		if (*pt == '/') *pt++ = '\0';

		real = 1;
		demd = umsdos_get_emd_dentry(dir);
		if (!IS_ERR(demd)) {
			if (demd->d_inode)
				real = 0;
			dput(demd);
		}

#ifdef UMSDOS_DEBUG_VERBOSE
printk ("umsdos_solve_hlink: dir %s/%s, name=%s, real=%d\n",
dir->d_parent->d_name.name, dir->d_name.name, start, real);
#endif
		dentry_dst = umsdos_lookup_dentry(dir, start, len, real);
/* XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX */
		if (real)
			d_drop(dir);
		dput (dir);
		if (IS_ERR(dentry_dst))
			break;
		/* not found? stop search ... */
		if (!dentry_dst->d_inode) {
			break;
		}
		if (*pt == '\0')	/* we're finished! */
			break;
	} /* end while */

	if (!IS_ERR(dentry_dst)) {
		struct inode *inode = dentry_dst->d_inode;
		if (inode) {
			inode->u.umsdos_i.i_is_hlink = 1;
#ifdef UMSDOS_DEBUG_VERBOSE
printk ("umsdos_solve_hlink: resolved link %s/%s, ino=%ld\n",
dentry_dst->d_parent->d_name.name, dentry_dst->d_name.name, inode->i_ino);
#endif
		} else {
#ifdef UMSDOS_DEBUG_VERBOSE
printk ("umsdos_solve_hlink: resolved link %s/%s negative!\n",
dentry_dst->d_parent->d_name.name, dentry_dst->d_name.name);
#endif
		}
	} else
		printk(KERN_WARNING
			"umsdos_solve_hlink: err=%ld\n", PTR_ERR(dentry_dst));
	kfree (path);

out:
	dput(hlink);	/* original hlink no longer needed */
	return dentry_dst;

async_fail:
	dentry_dst = ERR_PTR(-EIO);
out_release:
	page_cache_release(page);
	goto out;
}