示例#1
0
static int UMSDOS_readdir (struct file *filp, void *dirbuf, filldir_t filldir)
{
	struct inode *dir = filp->f_dentry->d_inode;
	int ret = 0, count = 0;
	struct UMSDOS_DIR_ONCE bufk;

	bufk.dirbuf = dirbuf;
	bufk.filldir = filldir;
	bufk.stop = 0;

	Printk (("UMSDOS_readdir in\n"));
	while (ret == 0 && bufk.stop == 0) {
		struct umsdos_dirent entry;

		bufk.count = 0;
		ret = umsdos_readdir_x (dir, filp, &bufk, &entry, 
					umsdos_dir_once);
		if (bufk.count == 0)
			break;
		count += bufk.count;
	}
	Printk (("UMSDOS_readdir out %d count %d pos %Ld\n", 
		ret, count, filp->f_pos));
	return count ? : ret;
}
示例#2
0
int umsdos_emd_dir_readentry (struct file *filp, struct umsdos_dirent *entry)
{
	int ret;

	Printk ((KERN_DEBUG "umsdos_emd_dir_readentry /mn/: entering.\n"));

	ret = umsdos_emd_dir_read (filp, (char *) entry, UMSDOS_REC_SIZE);
	if (ret == 0) {	/* if no error */
		/* Variable size record. Maybe, we have to read some more */
		int recsize = umsdos_evalrecsize (entry->name_len);

		if (recsize > UMSDOS_REC_SIZE) {
Printk ((KERN_DEBUG "umsdos_emd_dir_readentry /mn/: %d > %d!\n",
recsize, UMSDOS_REC_SIZE));
			ret = umsdos_emd_dir_read (filp, 
					((char *) entry) + UMSDOS_REC_SIZE,
					recsize - UMSDOS_REC_SIZE);
		}
	}
	Printk (("umsdos_emd_dir_readentry /mn/: ret=%d.\n", ret));
	if (entry && ret == 0) {
Printk (("umsdos_emd_dir_readentry /mn/: returning len=%d,name=%.*s\n",
(int) entry->name_len, (int) entry->name_len, entry->name));
	}
	return ret;
}
示例#3
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;
}
示例#4
0
文件: inode.c 项目: rohsaini/mkunity
/*
	Load an inode from disk.
*/
void UMSDOS_read_inode(struct inode *inode)
{
	PRINTK (("read inode %x ino = %d ",inode,inode->i_ino));
	msdos_read_inode(inode);
	PRINTK (("ino = %d %d\n",inode->i_ino,inode->i_count));
	if (S_ISDIR(inode->i_mode)
		&& (inode->u.umsdos_i.u.dir_info.creating != 0
			|| inode->u.umsdos_i.u.dir_info.looking != 0
			|| waitqueue_active(&inode->u.umsdos_i.u.dir_info.p))){
		Printk (("read inode %d %d %p\n"
			,inode->u.umsdos_i.u.dir_info.creating
			,inode->u.umsdos_i.u.dir_info.looking
			,inode->u.umsdos_i.u.dir_info.p));
	}
	/* #Specification: Inode / post initialisation
		To completely initialise an inode, we need access to the owner
		directory, so we can locate more info in the EMD file. This is
		not available the first time the inode is access, we use
		a value in the inode to tell if it has been finally initialised.

		At first, we have tried testing i_count but it was causing
		problem. It is possible that two or more process use the
		newly accessed inode. While the first one block during
		the initialisation (probably while reading the EMD file), the
		others believe all is well because i_count > 1. They go banana
		with a broken inode. See umsdos_lookup_patch and umsdos_patch_inode.
	*/
	umsdos_patch_inode(inode,NULL,0);
}
示例#5
0
/*
 * Truncate a file to 0 length.
 */
static void UMSDOS_truncate (struct inode *inode)
{
    Printk (("UMSDOS_truncate\n"));
    if (!IS_RDONLY (inode)) {
        fat_truncate (inode);
        inode->i_ctime = inode->i_mtime = CURRENT_TIME;
        mark_inode_dirty(inode);
    }
}
示例#6
0
int umsdos_newentry (struct dentry *parent, struct umsdos_info *info)
{
	int err, ret = -EEXIST;

	err = umsdos_find (parent, info);
	if (err && err == -ENOENT) {
		ret = umsdos_writeentry (parent, info, 0);
		Printk (("umsdos_writeentry EMD ret = %d\n", ret));
	}
	return ret;
}
示例#7
0
struct dentry *UMSDOS_lookup (struct inode *dir, struct dentry *dentry)
{
	struct dentry *ret;

	ret = umsdos_lookup_x (dir, dentry, 0);

	/* Create negative dentry if not found. */
	if (ret == ERR_PTR(-ENOENT)) {
		Printk ((KERN_DEBUG 
			"UMSDOS_lookup: converting -ENOENT to negative\n"));
		d_add (dentry, NULL);
		dentry->d_op = &umsdos_dentry_operations;
		ret = NULL;
	}
	return ret;
}
示例#8
0
ssize_t umsdos_emd_dir_write (	struct file *filp,
				char *buf,	
				size_t count)
{
	int written;

#ifdef __BIG_ENDIAN
	struct umsdos_dirent *d = (struct umsdos_dirent *) buf;

	d->nlink = cpu_to_le16 (d->nlink);
	d->uid = cpu_to_le16 (d->uid);
	d->gid = cpu_to_le16 (d->gid);
	d->atime = cpu_to_le32 (d->atime);
	d->mtime = cpu_to_le32 (d->mtime);
	d->ctime = cpu_to_le32 (d->ctime);
	d->rdev = cpu_to_le16 (d->rdev);
	d->mode = cpu_to_le16 (d->mode);
#endif

	filp->f_flags = 0;
Printk (("umsdos_emd_dir_write /mn/: calling write_kmem with %p, %p, %d, %Ld\n",
filp, buf, count, filp->f_pos));
	written = umsdos_file_write_kmem (filp, buf, count);

#ifdef __BIG_ENDIAN
	d->nlink = le16_to_cpu (d->nlink);
	d->uid = le16_to_cpu (d->uid);
	d->gid = le16_to_cpu (d->gid);
	d->atime = le32_to_cpu (d->atime);
	d->mtime = le32_to_cpu (d->mtime);
	d->ctime = le32_to_cpu (d->ctime);
	d->rdev = le16_to_cpu (d->rdev);
	d->mode = le16_to_cpu (d->mode);
#endif

#ifdef UMSDOS_PARANOIA
if (written != count)
printk(KERN_ERR "umsdos_emd_dir_write: ERROR: written (%d) != count (%d)\n",
written, count);
#endif

	return (written != count) ? -EIO : 0;
}
示例#9
0
文件: rdir.c 项目: dmgerman/original
/* #Specification: pseudo root / DOS/..
 * In the real root directory (c:\), the directory ..
 * is the pseudo root (c:\linux).
 */
struct dentry *umsdos_rlookup_x ( struct inode *dir, struct dentry *dentry, int nopseudo)
{
	struct dentry *ret;

	if (saved_root && dir == saved_root->d_inode && !nopseudo &&
	    dentry->d_name.len == UMSDOS_PSDROOT_LEN &&
	    memcmp (dentry->d_name.name, UMSDOS_PSDROOT_NAME, UMSDOS_PSDROOT_LEN) == 0) {
		/* #Specification: pseudo root / DOS/linux
		 * Even in the real root directory (c:\), the directory
		 * /linux won't show
		 */
		 
		ret = ERR_PTR(-ENOENT);
		goto out;
	}

	ret = msdos_lookup (dir, dentry);
	if (ret) {
		printk(KERN_WARNING
			"umsdos_rlookup_x: %s/%s failed, ret=%ld\n",
			dentry->d_parent->d_name.name, dentry->d_name.name,
			PTR_ERR(ret));
		goto out;
	}
	if (dentry->d_inode) {
		/* We must install the proper function table
		 * depending on whether this is an MS-DOS or 
		 * a UMSDOS directory
		 */
Printk ((KERN_DEBUG "umsdos_rlookup_x: patch_dentry_inode %s/%s\n",
dentry->d_parent->d_name.name, dentry->d_name.name));
		umsdos_patch_dentry_inode(dentry, 0);

	}
out:
	/* always install our dentry ops ... */
	dentry->d_op = &umsdos_dentry_operations;
	return ret;
}
示例#10
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;
}
示例#11
0
int umsdos_findentry (struct dentry *parent, struct umsdos_info *info,
			int expect)
{		
	int ret;

	ret = umsdos_find (parent, info);
	if (ret)
		goto out;

	switch (expect) {
	case 1:
		if (S_ISDIR (info->entry.mode))
			ret = -EISDIR;
		break;
	case 2:
		if (!S_ISDIR (info->entry.mode))
			ret = -ENOTDIR;
	}

out:
	Printk (("umsdos_findentry: returning %d\n", ret));
	return ret;
}
示例#12
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;
}
示例#13
0
文件: inode.c 项目: rohsaini/mkunity
/*
	Read the super block of an Extended MS-DOS FS.
*/
struct super_block *UMSDOS_read_super(
	struct super_block *s,
	void *data,
	int silent)
{
	/* #Specification: mount / options
		Umsdos run on top of msdos. Currently, it supports no
		mount option, but happily pass all option received to
		the msdos driver. I am not sure if all msdos mount option
		make sense with Umsdos. Here are at least those who
		are useful.
			uid=
			gid=

		These options affect the operation of umsdos in directories
		which do not have an EMD file. They behave like normal
		msdos directory, with all limitation of msdos.
	*/
	struct super_block *sb;
	MOD_INC_USE_COUNT;
	sb = msdos_read_super(s,data,silent);
	printk ("UMSDOS Beta 0.6 (compatibility level %d.%d, fast msdos)\n"
		,UMSDOS_VERSION,UMSDOS_RELEASE);
	if (sb != NULL){
		MSDOS_SB(sb)->options.dotsOK = 0;  /* disable hidden==dotfile */
		sb->s_op = &umsdos_sops;
		PRINTK (("umsdos_read_super %p\n",sb->s_mounted));
		umsdos_setup_dir_inode (sb->s_mounted);
		PRINTK (("End umsdos_read_super\n"));
		if (s == super_blocks){
			/* #Specification: pseudo root / mount
				When a umsdos fs is mounted, a special handling is done
				if it is the root partition. We check for the presence
				of the file /linux/etc/init or /linux/etc/rc or
				/linux/sbin/init. If one is there, we do a chroot("/linux").

				We check both because (see init/main.c) the kernel
				try to exec init at different place and if it fails
				it tries /bin/sh /etc/rc. To be consistent with
				init/main.c, many more test would have to be done
				to locate init. Any complain ?

				The chroot is done manually in init/main.c but the
				info (the inode) is located at mount time and store
				in a global variable (pseudo_root) which is used at
				different place in the umsdos driver. There is no
				need to store this variable elsewhere because it
				will always be one, not one per mount.

				This feature allows the installation
				of a linux system within a DOS system in a subdirectory.
	
				A user may install its linux stuff in c:\linux
				avoiding any clash with existing DOS file and subdirectory.
				When linux boots, it hides this fact, showing a normal
				root directory with /etc /bin /tmp ...

				The word "linux" is hardcoded in /usr/include/linux/umsdos_fs.h
				in the macro UMSDOS_PSDROOT_NAME.
			*/

			struct inode *pseudo;
			Printk (("Mounting root\n"));
			if (umsdos_real_lookup (sb->s_mounted,UMSDOS_PSDROOT_NAME
					,UMSDOS_PSDROOT_LEN,&pseudo)==0
				&& S_ISDIR(pseudo->i_mode)){
				struct inode *etc = NULL;
				struct inode *sbin = NULL;
				int pseudo_ok = 0;
				Printk (("/%s is there\n",UMSDOS_PSDROOT_NAME));
				if (umsdos_real_lookup (pseudo,"etc",3,&etc)==0
					&& S_ISDIR(etc->i_mode)){
					struct inode *init = NULL;
					struct inode *rc = NULL;
					Printk (("/%s/etc is there\n",UMSDOS_PSDROOT_NAME));
					if ((umsdos_real_lookup (etc,"init",4,&init)==0
							&& S_ISREG(init->i_mode))
						|| (umsdos_real_lookup (etc,"rc",2,&rc)==0
							&& S_ISREG(rc->i_mode))){
						pseudo_ok = 1;
					}
					iput (init);
					iput (rc);
				}
				if (!pseudo_ok
					&& umsdos_real_lookup (pseudo,"sbin",4,&sbin)==0
					&& S_ISDIR(sbin->i_mode)){
					struct inode *init = NULL;
					Printk (("/%s/sbin is there\n",UMSDOS_PSDROOT_NAME));
					if (umsdos_real_lookup (sbin,"init",4,&init)==0
							&& S_ISREG(init->i_mode)){
						pseudo_ok = 1;
					}
					iput (init);
				}
				if (pseudo_ok){
					umsdos_setup_dir_inode (pseudo);
					Printk (("Activating pseudo root /%s\n",UMSDOS_PSDROOT_NAME));
					pseudo_root = pseudo;
					pseudo->i_count++;
					pseudo = NULL;
				}
				iput (sbin);
				iput (etc);
			}
			iput (pseudo);
		}
	} else {
		MOD_DEC_USE_COUNT;
	}
	return sb;
}
示例#14
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;
}
示例#15
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;
}
示例#16
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;
}
示例#17
0
struct dentry *umsdos_lookup_x (struct inode *dir, struct dentry *dentry, int nopseudo)
{				
	struct dentry *dret = NULL;
	struct inode *inode;
	int ret = -ENOENT;
	struct umsdos_info info;

#ifdef UMSDOS_DEBUG_VERBOSE
printk("umsdos_lookup_x: looking for %s/%s\n", 
dentry->d_parent->d_name.name, dentry->d_name.name);
#endif

	umsdos_startlookup (dir);
	if (umsdos_is_pseudodos (dir, dentry)) {
		/* #Specification: pseudo root / lookup(DOS)
		 * A lookup of DOS in the pseudo root will always succeed
		 * and return the inode of the real root.
		 */
		Printk ((KERN_DEBUG "umsdos_lookup_x: following /DOS\n"));
		inode = saved_root->d_inode;
		goto out_add;
	}

	ret = umsdos_parse (dentry->d_name.name, dentry->d_name.len, &info);
	if (ret) {
printk("umsdos_lookup_x: %s/%s parse failed, ret=%d\n", 
dentry->d_parent->d_name.name, dentry->d_name.name, ret);
		goto out;
	}

	ret = umsdos_findentry (dentry->d_parent, &info, 0);
	if (ret) {
if (ret != -ENOENT)
printk("umsdos_lookup_x: %s/%s findentry failed, ret=%d\n", 
dentry->d_parent->d_name.name, dentry->d_name.name, ret);
		goto out;
	}
Printk (("lookup %.*s pos %lu ret %d len %d ", 
info.fake.len, info.fake.fname, info.f_pos, ret, info.fake.len));

	/* do a real lookup to get the short name ... */
	dret = umsdos_covered(dentry->d_parent, info.fake.fname, info.fake.len);
	ret = PTR_ERR(dret);
	if (IS_ERR(dret)) {
printk("umsdos_lookup_x: %s/%s real lookup failed, ret=%d\n", 
dentry->d_parent->d_name.name, dentry->d_name.name, ret);
		goto out;
	}
	inode = dret->d_inode;
	if (!inode)
		goto out_remove;
	umsdos_lookup_patch_new(dret, &info);
#ifdef UMSDOS_DEBUG_VERBOSE
printk("umsdos_lookup_x: found %s/%s, ino=%ld\n", 
dret->d_parent->d_name.name, dret->d_name.name, dret->d_inode->i_ino);
#endif

	/* Check for a hard link */
	if ((info.entry.flags & UMSDOS_HLINK) &&
	    !inode->u.umsdos_i.i_is_hlink) {
		dret = umsdos_solve_hlink (dret);
		ret = PTR_ERR(dret);
		if (IS_ERR(dret))
			goto out;
		ret = -ENOENT;
		inode = dret->d_inode;
		if (!inode) {
printk("umsdos_lookup_x: %s/%s negative after link\n", 
dret->d_parent->d_name.name, dret->d_name.name);
			goto out_dput;
		}
	}

	if (inode == pseudo_root && !nopseudo) {
		/* #Specification: pseudo root / dir lookup
		 * For the same reason as readdir, a lookup in /DOS for
		 * the pseudo root directory (linux) will fail.
		 */
		/*
		 * This has to be allowed for resolving hard links
		 * which are recorded independently of the pseudo-root
		 * mode.
		 */
printk("umsdos_lookup_x: skipping DOS/linux\n");
		ret = -ENOENT;
		goto out_dput;
	}

	/*
	 * We've found it OK.  Now hash the dentry with the inode.
	 */
out_add:
	atomic_inc(&inode->i_count);
	d_add (dentry, inode);
	dentry->d_op = &umsdos_dentry_operations;
	ret = 0;

out_dput:
	if (dret && dret != dentry)
		d_drop(dret);
	dput(dret);
out:
	umsdos_endlookup (dir);
	return ERR_PTR(ret);

out_remove:
	printk(KERN_WARNING "UMSDOS:  entry %s/%s out of sync, erased\n",
		dentry->d_parent->d_name.name, dentry->d_name.name);
	umsdos_delentry (dentry->d_parent, &info, S_ISDIR (info.entry.mode));
	ret = -ENOENT;
	goto out_dput;
}