Commit 9f6142d7 authored by Christoph Hellwig's avatar Christoph Hellwig Committed by Linus Torvalds

[PATCH] remove umsdos from tree

UMSDOS has been non-function since early 2.5 and would need a major rewrite
to be resurrected (which I don't hope anyone plans as it's a really
horrible hack)

From: Adrian Bunk <bunk@stusta.de>

With umsdos gone, there's no longer a MAINTAINERS entry required.
Signed-off-by: default avatarAdrian Bunk <bunk@stusta.de>
Signed-off-by: default avatarAndrew Morton <akpm@osdl.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@osdl.org>
parent f5dbb2b7
...@@ -42,8 +42,6 @@ udf.txt ...@@ -42,8 +42,6 @@ udf.txt
- info and mount options for the UDF filesystem. - info and mount options for the UDF filesystem.
ufs.txt ufs.txt
- info on the ufs filesystem. - info on the ufs filesystem.
umsdos.txt
- info on the umsdos extensions to the msdos filesystem.
vfat.txt vfat.txt
- info on using the VFAT filesystem used in Windows NT and Windows 95 - info on using the VFAT filesystem used in Windows NT and Windows 95
vfs.txt vfs.txt
......
Firstly, let me say that UMSDOS is going through some major code changes,
and has some KNOWN BUGS (and quite a few unknown :-). Please read
fs/umsdos/README-WIP.txt for more information on current status. Thanks.
----------------------------------------------------------------------------
Very short explanation for the impatient!
Umsdos is a file system driver that run on top the MSDOS fs driver.
It is written by Jacques Gelinas (jacques@solucorp.qc.ca)
and is currently maintained by Matija Nalis (mnalis@jagor.srce.hr)
Umsdos is not a file system per se, but a twist to make a boring
one into a useful one.
It gives you:
long file names
Permissions and owners
Links
Special files (devices, pipes...)
All that is needed to be a linux root fs.
There is plenty of documentation on it in the source. A formatted document
made from those comments is available from
sunsite.unc.edu:/pub/Linux/system/Filesystems/umsdos.
You mount a DOS partition like this:
mount -t umsdos /dev/hda3 /mnt
^
---------|
All options are passed to the msdos drivers. Option like uid,gid etc are
given to msdos.
The default behavior of Umsdos is to do the same thing as the msdos driver
mostly passing commands to it without much processing. Again, this is
the default. After doing the mount on a DOS partition, nothing special
happens. This is why all mount options are passed to the msdos fs driver.
Umsdos uses a special DOS file --linux-.--- to store the information
which can't be handled by the normal MS-DOS filesystem. This is the trick.
--linux-.--- is optional. There is one per directory.
**** If --linux-.--- is missing, then Umsdos process the directory the
same way the msdos driver does. Short file names, no goodies, default
owner and permissions. So each directory may have or not this
--linux-.---
Now, how to get those --linux-.---.
\begin joke_section
Well send me a directory content
and I will send you one customised for you.
$5 per directory. Add any applicable taxes.
\end joke_section
A utility umssync creates those. The kernel maintains them. It is available
from the same directory above (sunsite) in the file umsdos_progs-0.7.tar.gz.
A compiled version is available in umsdos_progs-0.7.bin.tar.gz.
So in our example, after mounting mnt, we do
umssync .
This will promote this directory (a recursive option is available) to full
umsdos capabilities (long name, etc.). However, an "ls -l" before and after
won't show much difference. The files which were there are still there, but
now you can do all this:
chmod 644 *
chown you.your_group *
ls >THIS_IS.A.VERY.LONG.NAME
ln -s toto tata
ls -l
Once a directory is promoted, all subdirectories created will inherit that
promotion.
What happens if you boot DOS and create files in those promoted directories ?
Umsdos won't notice new files, but will signal removed files (it won't crash).
Using umssync in /etc/rc will make sure the DOS directory is in sync with
the --linux-.---.
It is a good idea to put the following command in your RC file just
after the "mount -a":
mount -a
/sbin/umssync -i+ -c+ -r99 /umsdos_mount_point
(You put one for each umsdos mount point in the fstab)
This will ensure nice operation. A umsdos.fsck is in the making,
so you will be allowed to manage umsdos partitions in the same way
other filesystems are, using the generic fsck front end.
Hope this helps!
...@@ -2244,13 +2244,6 @@ L: linux_udf@hpesjro.fc.hp.com ...@@ -2244,13 +2244,6 @@ L: linux_udf@hpesjro.fc.hp.com
W: http://linux-udf.sourceforge.net W: http://linux-udf.sourceforge.net
S: Maintained S: Maintained
UMSDOS FILESYSTEM
P: Matija Nalis
M: Matija Nalis <mnalis-umsdos@voyager.hr>
L: linux-kernel@vger.kernel.org
W: http://linux.voyager.hr/umsdos/
S: Maintained
UNIFORM CDROM DRIVER UNIFORM CDROM DRIVER
P: Jens Axboe P: Jens Axboe
M: axboe@suse.de M: axboe@suse.de
......
...@@ -578,9 +578,8 @@ config FAT_FS ...@@ -578,9 +578,8 @@ config FAT_FS
tristate tristate
select NLS select NLS
help help
If you want to use one of the FAT-based file systems (the MS-DOS, If you want to use one of the FAT-based file systems (the MS-DOS and
VFAT (Windows 95) and UMSDOS (used to run Linux on top of an VFAT (Windows 95) file systems), then you must say Y or M here
ordinary DOS partition) file systems), then you must say Y or M here
to include FAT support. You will then be able to mount partitions or to include FAT support. You will then be able to mount partitions or
diskettes with FAT-based file systems and transparently access the diskettes with FAT-based file systems and transparently access the
files on them, i.e. MSDOS files will look and behave just like all files on them, i.e. MSDOS files will look and behave just like all
...@@ -612,9 +611,6 @@ config FAT_FS ...@@ -612,9 +611,6 @@ config FAT_FS
fat. Note that if you compile the FAT support as a module, you fat. Note that if you compile the FAT support as a module, you
cannot compile any of the FAT-based file systems into the kernel cannot compile any of the FAT-based file systems into the kernel
-- they will have to be modules as well. -- they will have to be modules as well.
The file system of your root partition (the one containing the
directory /) cannot be a module, so don't say M here if you intend
to use UMSDOS as your root file system.
config MSDOS_FS config MSDOS_FS
tristate "MSDOS fs support" tristate "MSDOS fs support"
...@@ -631,10 +627,6 @@ config MSDOS_FS ...@@ -631,10 +627,6 @@ config MSDOS_FS
transparent, i.e. the MSDOS files look and behave just like all transparent, i.e. the MSDOS files look and behave just like all
other Unix files. other Unix files.
If you want to use UMSDOS, the Unix-like file system on top of a
DOS file system, which allows you to run Linux from within a DOS
partition without repartitioning, you'll have to say Y or M here.
If you have Windows 95 or Windows NT installed on your MSDOS If you have Windows 95 or Windows NT installed on your MSDOS
partitions, you should use the VFAT file system (say Y to "VFAT fs partitions, you should use the VFAT file system (say Y to "VFAT fs
support" below), or you will not be able to see the long filenames support" below), or you will not be able to see the long filenames
...@@ -654,11 +646,6 @@ config VFAT_FS ...@@ -654,11 +646,6 @@ config VFAT_FS
used by Windows 95, Windows 98, Windows NT 4.0, and the Unix used by Windows 95, Windows 98, Windows NT 4.0, and the Unix
programs from the mtools package. programs from the mtools package.
You cannot use the VFAT file system for your Linux root partition
(the one containing the directory /); use UMSDOS instead if you
want to run Linux from within a DOS partition (i.e. say Y to
"Unix like fs on top of std MSDOS fs", below).
The VFAT support enlarges your kernel by about 10 KB and it only The VFAT support enlarges your kernel by about 10 KB and it only
works if you said Y to the "DOS FAT fs support" above. Please read works if you said Y to the "DOS FAT fs support" above. Please read
the file <file:Documentation/filesystems/vfat.txt> for details. If the file <file:Documentation/filesystems/vfat.txt> for details. If
...@@ -689,35 +676,6 @@ config FAT_DEFAULT_IOCHARSET ...@@ -689,35 +676,6 @@ config FAT_DEFAULT_IOCHARSET
If unsure, you shouldn't set "utf8" here. If unsure, you shouldn't set "utf8" here.
See <file:Documentation/filesystems/vfat.txt> for more information. See <file:Documentation/filesystems/vfat.txt> for more information.
config UMSDOS_FS
#dep_tristate ' UMSDOS: Unix-like file system on top of standard MSDOS fs' CONFIG_UMSDOS_FS $CONFIG_MSDOS_FS
# UMSDOS is temprory broken
bool
help
Say Y here if you want to run Linux from within an existing DOS
partition of your hard drive. The advantage of this is that you can
get away without repartitioning your hard drive (which often implies
backing everything up and restoring afterwards) and hence you're
able to quickly try out Linux or show it to your friends; the
disadvantage is that Linux becomes susceptible to DOS viruses and
that UMSDOS is somewhat slower than ext2fs. Another use of UMSDOS
is to write files with long unix filenames to MSDOS floppies; it
also allows Unix-style soft-links and owner/permissions of files on
MSDOS floppies. You will need a program called umssync in order to
make use of UMSDOS; read
<file:Documentation/filesystems/umsdos.txt>.
To get utilities for initializing/checking UMSDOS file system, or
latest patches and/or information, visit the UMSDOS home page at
<http://www.voyager.hr/~mnalis/umsdos/>.
This option enlarges your kernel by about 28 KB and it only works if
you said Y to both "DOS FAT fs support" and "MSDOS fs support"
above. To compile this as a module, choose M here: the module will be
called umsdos. Note that the file system of your root partition
(the one containing the directory /) cannot be a module, so saying M
could be dangerous. If unsure, say N.
config NTFS_FS config NTFS_FS
tristate "NTFS file system support" tristate "NTFS file system support"
select NLS select NLS
......
...@@ -57,7 +57,6 @@ obj-$(CONFIG_HUGETLBFS) += hugetlbfs/ ...@@ -57,7 +57,6 @@ obj-$(CONFIG_HUGETLBFS) += hugetlbfs/
obj-$(CONFIG_CODA_FS) += coda/ obj-$(CONFIG_CODA_FS) += coda/
obj-$(CONFIG_MINIX_FS) += minix/ obj-$(CONFIG_MINIX_FS) += minix/
obj-$(CONFIG_FAT_FS) += fat/ obj-$(CONFIG_FAT_FS) += fat/
obj-$(CONFIG_UMSDOS_FS) += umsdos/
obj-$(CONFIG_MSDOS_FS) += msdos/ obj-$(CONFIG_MSDOS_FS) += msdos/
obj-$(CONFIG_VFAT_FS) += vfat/ obj-$(CONFIG_VFAT_FS) += vfat/
obj-$(CONFIG_BFS_FS) += bfs/ obj-$(CONFIG_BFS_FS) += bfs/
......
#
# Makefile for the umsdos Unix-like filesystem routines.
#
obj-$(CONFIG_UMSDOS) += umsdos.o
umsdos-objs := dir.o inode.o ioctl.o mangle.o namei.o rdir.o emd.o
p:
proto *.c >/usr/include/linux/umsdos_fs.p
doc:
nadoc -i -p umsdos.doc - /tmp/umsdos.mpg
Changes by Matija Nalis (mnalis@jagor.srce.hr) on umsdos dentry fixing
(started by Peter T. Waltenberg <peterw@karaka.chch.cri.nz>)
(Final conversion to dentries Bill Hawes <whawes@star.net>)
There is no warning any more.
Both read-only and read-write stuff is fixed, both in
msdos-compatibile mode, and in umsdos EMD mode, and it seems stable.
Userland NOTE: new umsdos_progs (umssync, umssetup, udosctl & friends) that
will compile and work on 2.2.x+ kernels and glibc based systems, as well as
kernel patches and other umsdos related information may be found at
http://linux.voyager.hr/umsdos/
Information below is getting outdated slowly -- I'll fix it one day when I
get enough time - there are more important things to fix right now.
Legend: those lines marked with '+' on the beggining of line indicates it
passed all of my tests, and performed perfect in all of them.
Current status (010125) - UMSDOS 0.86j:
(1) pure MSDOS (no --linux-.--- EMD file):
READ:
+ readdir - works
+ lookup - works
+ read file - works
WRITE:
+ creat file - works
+ unlink file - works
+ write file - works
+ rename file (same dir) - works
+ rename file (dif. dir) - works
+ rename dir (same dir) - works
+ rename dir (dif. dir) - works
+ mkdir - works
+ rmdir - works
(2) umsdos (with --linux-.--- EMD file):
READ:
+ readdir - works
+ lookup - works
+ permissions/owners stuff - works
+ long file names - works
+ read file - works
+ switching MSDOS/UMSDOS - works
+ switching UMSDOS/MSDOS - works
- pseudo root things - works mostly. See notes below.
+ resolve symlink - works
+ dereference symlink - works
+ dangling symlink - works
+ hard links - works
+ special files (block/char devices, FIFOs, sockets...) - works
+ various umsdos ioctls - works
WRITE:
+ create symlink - works
+ create hardlink - works
+ create file - works
+ create special file - works
+ write to file - works
+ rename file (same dir) - works
+ rename file (dif. dir) - works
+ rename hardlink (same dir) - works
- rename hardlink (dif. dir) - works, but see notes below.
+ rename symlink (same dir) - works
+ rename symlink (dif. dir) - works
+ rename dir (same dir) - works
+ rename dir (dif. dir) - works
+ unlink file - works
+ notify_change (chown,perms) - works
+ notify_change for hardlinks - works
+ unlink hardlink - works
+ mkdir - works
+ rmdir - works
+ umssyncing (many ioctls) - works
- CVF-FAT stuff (compressed DOS filesystem) - there is some support from Frank
Gockel <gockel@sent13.uni-duisburg.de> to use it even under umsdosfs, but I
have no way of testing it -- please let me know if there are problems specific
to umsdos (for instance, it works under msdosfs, but not under umsdosfs).
Some current notes:
Note: creating and using pseudo-hardlinks is always non-perfect, especially
in filesystems that might be externally modified like umsdos. There is
example is specs file about it. Specifically, moving directory which
contains hardlinks will break them.
Note: (about creating hardlinks in pseudoroot mode) - hardlinks created in
pseudoroot mode are now again compatibile with 'normal' hardlinks, and vice
versa. Thanks to Sorin Iordachescu <sorin@rodae.ro> for providing fix.
See http://linux.voyager.hr/umsdos/hlbug.html for more info and upgrade
procedure if you used broken versions...
------------------------------------------------------------------------------
Some general notes:
Good idea when running development kernels is to have SysRq support compiled
in kernel, and use Sync/Emergency-remount-RO if you bump into problems (like
not being able to umount(2) umsdosfs, and because of it root partition also,
or panics which force you to reboot etc.)
I'm unfortunately somewhat out of time to read linux-kernel@vger, but I do
check for messages having "UMSDOS" in the subject, and read them. I might
miss some in all that volume, though. I should reply to any direct e-mail
in few days. If I don't, probably I never got your message.
/*
* linux/fs/umsdos/dir.c
*
* Written 1993 by Jacques Gelinas
* Inspired from linux/fs/msdos/... : Werner Almesberger
*
* Extended MS-DOS directory handling functions
*/
#include <linux/time.h>
#include <linux/string.h>
#include <linux/fs.h>
#include <linux/msdos_fs.h>
#include <linux/errno.h>
#include <linux/stat.h>
#include <linux/limits.h>
#include <linux/umsdos_fs.h>
#include <linux/slab.h>
#include <linux/pagemap.h>
#include <linux/smp_lock.h>
#define UMSDOS_SPECIAL_DIRFPOS 3
extern struct dentry *saved_root;
extern struct inode *pseudo_root;
/* #define UMSDOS_DEBUG_VERBOSE 1 */
/*
* Dentry operations routines
*/
/* nothing for now ... */
static int umsdos_dentry_validate(struct dentry *dentry, struct nameidata *nd)
{
return 1;
}
/* for now, drop everything to force lookups ... */
/* ITYM s/everything/& positive/... */
static int umsdos_dentry_dput(struct dentry *dentry)
{
struct inode *inode = dentry->d_inode;
if (inode) {
return 1;
}
return 0;
}
struct dentry_operations umsdos_dentry_operations =
{
.d_revalidate = umsdos_dentry_validate,
.d_delete = umsdos_dentry_dput,
};
struct UMSDOS_DIR_ONCE {
void *dirbuf;
filldir_t filldir;
int count;
int stop;
};
/*
* Record a single entry the first call.
* Return -EINVAL the next one.
* NOTE: filldir DOES NOT use a dentry
*/
static int umsdos_dir_once ( void *buf,
const char *name,
int len,
loff_t offset,
ino_t ino,
unsigned type)
{
int ret = -EINVAL;
struct UMSDOS_DIR_ONCE *d = (struct UMSDOS_DIR_ONCE *) buf;
if (d->count == 0) {
PRINTK ((KERN_DEBUG "dir_once :%.*s: offset %Ld\n",
len, name, offset));
ret = d->filldir (d->dirbuf, name, len, offset, ino, DT_UNKNOWN);
d->stop = ret < 0;
d->count = 1;
}
return ret;
}
/*
* Read count directory entries from directory filp
* Return a negative value from linux/errno.h.
* Return > 0 if success (the number of bytes written by filldir).
*
* This function is used by the normal readdir VFS entry point,
* and in order to get the directory entry from a file's dentry.
* See umsdos_dentry_to_entry() below.
*/
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 (UMSDOS_I(inode)->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) &&
!UMSDOS_I(inode)->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;
}
/*
* Read count directory entries from directory filp.
* Return a negative value from linux/errno.h.
* Return 0 or positive if successful.
*/
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;
lock_kernel();
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;
}
unlock_kernel();
Printk (("UMSDOS_readdir out %d count %d pos %Ld\n",
ret, count, filp->f_pos));
return count ? : ret;
}
/*
* Complete the inode content with info from the EMD file.
*
* This function modifies the state of a dir inode. It decides
* whether the dir is a UMSDOS or DOS directory. This is done
* deeper in umsdos_patch_inode() called at the end of this function.
*
* Because it is does disk access, umsdos_patch_inode() may block.
* At the same time, another process may get here to initialise
* the same directory inode. There are three cases.
*
* 1) The inode is already initialised. We do nothing.
* 2) The inode is not initialised. We lock access and do it.
* 3) Like 2 but another process has locked the inode, so we try
* to lock it and check right afterward check whether
* initialisation is still needed.
*
*
* Thanks to the "mem" option of the kernel command line, it was
* possible to consistently reproduce this problem by limiting
* my memory to 4 MB and running X.
*
* Do this only if the inode is freshly read, because we will lose
* the current (updated) content.
*
* A lookup of a mount point directory yield the inode into
* the other fs, so we don't care about initialising it. iget()
* does this automatically.
*/
void umsdos_lookup_patch_new(struct dentry *dentry, struct umsdos_info *info)
{
struct inode *inode = dentry->d_inode;
struct umsdos_dirent *entry = &info->entry;
/*
* This part of the initialization depends only on i_patched.
*/
if (UMSDOS_I(inode)->i_patched)
goto out;
UMSDOS_I(inode)->i_patched = 1;
if (S_ISREG (entry->mode))
entry->mtime = inode->i_mtime;
inode->i_mode = entry->mode;
inode->i_rdev = to_kdev_t (entry->rdev);
inode->i_atime = entry->atime;
inode->i_ctime = entry->ctime;
inode->i_mtime = entry->mtime;
inode->i_uid = entry->uid;
inode->i_gid = entry->gid;
/* #Specification: umsdos / i_nlink
* The nlink field of an inode is maintained by the MSDOS file system
* for directory and by UMSDOS for other files. The logic is that
* MSDOS is already figuring out what to do for directories and
* does nothing for other files. For MSDOS, there are no hard links
* so all file carry nlink==1. UMSDOS use some info in the
* EMD file to plug the correct value.
*/
if (!S_ISDIR (entry->mode)) {
if (entry->nlink > 0) {
inode->i_nlink = entry->nlink;
} else {
printk (KERN_ERR
"UMSDOS: lookup_patch entry->nlink < 1 ???\n");
}
}
/*
* The mode may have changed, so patch the inode again.
*/
umsdos_patch_dentry_inode(dentry, info->f_pos);
umsdos_set_dirinfo_new(dentry, info->f_pos);
out:
return;
}
/*
* Return != 0 if an entry is the pseudo DOS entry in the pseudo root.
*/
int umsdos_is_pseudodos (struct inode *dir, struct dentry *dentry)
{
/* #Specification: pseudo root / DOS hard coded
* The pseudo sub-directory DOS in the pseudo root is hard coded.
* The name is DOS. This is done this way to help standardised
* the umsdos layout. The idea is that from now on /DOS is
* a reserved path and nobody will think of using such a path
* for a package.
*/
return dir == pseudo_root
&& dentry->d_name.len == 3
&& dentry->d_name.name[0] == 'D'
&& dentry->d_name.name[1] == 'O'
&& dentry->d_name.name[2] == 'S';
}
/*
* Check whether a file exists in the current directory.
* Return 0 if OK, negative error code if not (ex: -ENOENT).
*
* fills dentry->d_inode with found inode, and increments its count.
* if not found, return -ENOENT.
*/
/* #Specification: umsdos / lookup
* A lookup for a file is done in two steps. First, we
* locate the file in the EMD file. If not present, we
* return an error code (-ENOENT). If it is there, we
* repeat the operation on the msdos file system. If
* this fails, it means that the file system is not in
* sync with the EMD file. We silently remove this
* entry from the EMD file, and return ENOENT.
*/
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) &&
!UMSDOS_I(inode)->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;
}
/*
* Check whether a file exists in the current directory.
* Return 0 if OK, negative error code if not (ex: -ENOENT).
*
* Called by VFS; should fill dentry->d_inode via d_add.
*/
struct dentry *UMSDOS_lookup (struct inode *dir, struct dentry *dentry, struct nameidata *nd)
{
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;
}
struct dentry *umsdos_covered(struct dentry *parent, char *name, int len)
{
struct dentry *result, *dentry;
struct qstr qstr;
qstr.name = name;
qstr.len = len;
qstr.hash = full_name_hash(name, len);
result = ERR_PTR(-ENOMEM);
dentry = d_alloc(parent, &qstr);
if (dentry) {
/* XXXXXXXXXXXXXXXXXXX Race alert! */
result = UMSDOS_rlookup(parent->d_inode, dentry);
d_drop(dentry);
if (result)
goto out_fail;
return dentry;
}
out:
return result;
out_fail:
dput(dentry);
goto out;
}
/*
* Lookup or create a dentry from within the filesystem.
*
* We need to use this instead of lookup_dentry, as the
* directory semaphore lock is already held.
*/
struct dentry *umsdos_lookup_dentry(struct dentry *parent, char *name, int len,
int real)
{
struct dentry *result, *dentry;
struct qstr qstr;
qstr.name = name;
qstr.len = len;
qstr.hash = full_name_hash(name, len);
result = d_lookup(parent, &qstr);
if (!result) {
result = ERR_PTR(-ENOMEM);
dentry = d_alloc(parent, &qstr);
if (dentry) {
result = real ?
UMSDOS_rlookup(parent->d_inode, dentry) :
UMSDOS_lookup(parent->d_inode, dentry);
if (result)
goto out_fail;
return dentry;
}
}
out:
return result;
out_fail:
dput(dentry);
goto out;
}
/*
* Return a path relative to our root.
*/
char * umsdos_d_path(struct dentry *dentry, char * buffer, int len)
{
struct dentry * old_root;
char * path;
read_lock(&current->fs->lock);
old_root = dget(current->fs->root);
read_unlock(&current->fs->lock);
spin_lock(&dcache_lock);
path = __d_path(dentry, current->fs->rootmnt, dentry->d_sb->s_root, current->fs->rootmnt, buffer, len); /* FIXME: current->fs->rootmnt */
spin_unlock(&dcache_lock);
if (*path == '/')
path++; /* skip leading '/' */
if (current->fs->root->d_inode == pseudo_root)
{
*(path-1) = '/';
path -= (UMSDOS_PSDROOT_LEN+1);
memcpy(path, UMSDOS_PSDROOT_NAME, UMSDOS_PSDROOT_LEN);
}
dput(old_root);
return path;
}
/*
* Return the dentry which points to a pseudo-hardlink.
*
* it should try to find file it points to
* if file is found, return new dentry/inode
* The resolved inode will have i_is_hlink set.
*
* Note: the original dentry is always dput(), even if an error occurs.
*/
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_locked(page);
if (!PageUptodate(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) {
UMSDOS_I(inode)->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;
}
struct file_operations umsdos_dir_operations =
{
.read = generic_read_dir,
.readdir = UMSDOS_readdir,
.ioctl = UMSDOS_ioctl_dir,
};
struct inode_operations umsdos_dir_inode_operations =
{
.create = UMSDOS_create,
.lookup = UMSDOS_lookup,
.link = UMSDOS_link,
.unlink = UMSDOS_unlink,
.symlink = UMSDOS_symlink,
.mkdir = UMSDOS_mkdir,
.rmdir = UMSDOS_rmdir,
.mknod = UMSDOS_mknod,
.rename = UMSDOS_rename,
.setattr = UMSDOS_notify_change,
};
/*
* linux/fs/umsdos/emd.c
*
* Written 1993 by Jacques Gelinas
*
* Extended MS-DOS directory handling functions
*/
#include <linux/types.h>
#include <linux/fcntl.h>
#include <linux/kernel.h>
#include <linux/time.h>
#include <linux/errno.h>
#include <linux/string.h>
#include <linux/msdos_fs.h>
#include <linux/umsdos_fs.h>
#include <linux/dcache.h>
#include <linux/pagemap.h>
#include <linux/delay.h>
void put_entry (struct umsdos_dirent *p, struct umsdos_dirent *q)
{
p->name_len = q->name_len;
p->flags = q->flags;
p->nlink = cpu_to_le16(q->nlink);
p->uid = cpu_to_le16(q->uid);
p->gid = cpu_to_le16(q->gid);
p->atime = cpu_to_le32(q->atime);
p->mtime = cpu_to_le32(q->mtime);
p->ctime = cpu_to_le32(q->ctime);
p->rdev = cpu_to_le16(q->rdev);
p->mode = cpu_to_le16(q->mode);
}
static void get_entry(struct umsdos_dirent *p, struct umsdos_dirent *q)
{
p->name_len = q->name_len;
p->name[p->name_len]='\0';
p->flags = q->flags;
p->nlink = le16_to_cpu (q->nlink);
/* FIXME -- 32bit UID/GID issues */
p->uid = le16_to_cpu (q->uid);
p->gid = le16_to_cpu (q->gid);
p->atime = le32_to_cpu (q->atime);
p->mtime = le32_to_cpu (q->mtime);
p->ctime = le32_to_cpu (q->ctime);
p->rdev = le16_to_cpu (q->rdev);
p->mode = le16_to_cpu (q->mode);
}
/*
* Lookup the EMD dentry for a directory.
*
* Note: the caller must hold a lock on the parent directory.
*/
struct dentry *umsdos_get_emd_dentry(struct dentry *parent)
{
struct dentry *demd;
demd = umsdos_lookup_dentry(parent, UMSDOS_EMD_FILE,
UMSDOS_EMD_NAMELEN, 1);
return demd;
}
/*
* 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;
}
/*
* 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, NULL);
if (err) {
printk (KERN_WARNING
"umsdos_make_emd: create %s/%s failed, err=%d\n",
parent->d_name.name, demd->d_name.name, err);
}
out_set:
dput(demd);
out:
return err;
}
/*
* Read an entry from the EMD file.
* Support variable length record.
* Return -EIO if error, 0 if OK.
*
* does not change {d,i}_count
*/
int umsdos_emd_dir_readentry (struct dentry *demd, loff_t *pos, struct umsdos_dirent *entry)
{
struct address_space *mapping = demd->d_inode->i_mapping;
struct page *page;
struct umsdos_dirent *p;
int offs = *pos & ~PAGE_CACHE_MASK;
int recsize;
int ret = 0;
page = read_cache_page(mapping, *pos>>PAGE_CACHE_SHIFT,
(filler_t*)mapping->a_ops->readpage, NULL);
if (IS_ERR(page))
goto sync_fail;
wait_on_page_locked(page);
if (!PageUptodate(page))
goto async_fail;
p = (struct umsdos_dirent*)(kmap(page)+offs);
/* if this is an invalid entry (invalid name length), ignore it */
if( p->name_len > UMSDOS_MAXNAME )
{
printk (KERN_WARNING "Ignoring invalid EMD entry with size %d\n", entry->name_len);
p->name_len = 0;
ret = -ENAMETOOLONG; /* notify umssync(8) code that something is wrong */
/* FIXME: does not work if we did 'ls -l' before 'udosctl uls' ?! */
}
recsize = umsdos_evalrecsize(p->name_len);
if (offs + recsize > PAGE_CACHE_SIZE) {
struct page *page2;
int part = (char *)(page_address(page) + PAGE_CACHE_SIZE) - p->spare;
page2 = read_cache_page(mapping, 1+(*pos>>PAGE_CACHE_SHIFT),
(filler_t*)mapping->a_ops->readpage, NULL);
if (IS_ERR(page2)) {
kunmap(page);
page_cache_release(page);
page = page2;
goto sync_fail;
}
wait_on_page_locked(page2);
if (!PageUptodate(page2)) {
kunmap(page);
page_cache_release(page2);
goto async_fail;
}
memcpy(entry->spare,p->spare,part);
memcpy(entry->spare+part,kmap(page2),
recsize+offs-PAGE_CACHE_SIZE);
kunmap(page2);
page_cache_release(page2);
} else
memcpy(entry->spare,p->spare,((char*)p+recsize)-p->spare);
get_entry(entry, p);
kunmap(page);
page_cache_release(page);
*pos += recsize;
return ret;
async_fail:
page_cache_release(page);
page = ERR_PTR(-EIO);
sync_fail:
return PTR_ERR(page);
}
/*
* 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.
*/
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,*p;
struct address_space *mapping;
struct page *page, *page2 = NULL;
int offs;
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));
}
/* write the entry and update the parent timestamps */
mapping = emd_dentry->d_inode->i_mapping;
offs = info->f_pos & ~PAGE_CACHE_MASK;
ret = -ENOMEM;
page = grab_cache_page(mapping, info->f_pos>>PAGE_CACHE_SHIFT);
if (!page)
goto out_dput;
p = (struct umsdos_dirent *) (page_address(page) + offs);
if (offs + info->recsize > PAGE_CACHE_SIZE) {
ret = mapping->a_ops->prepare_write(NULL,page,offs,
PAGE_CACHE_SIZE);
if (ret)
goto out_unlock;
page2 = grab_cache_page(mapping,
(info->f_pos>>PAGE_CACHE_SHIFT)+1);
if (!page2)
goto out_unlock2;
ret = mapping->a_ops->prepare_write(NULL,page2,0,
offs+info->recsize-PAGE_CACHE_SIZE);
if (ret)
goto out_unlock3;
put_entry (p, entry);
memcpy(p->spare,entry->spare,
(char *)(page_address(page) + PAGE_CACHE_SIZE) - p->spare);
memcpy(page_address(page2),
((char*)entry)+PAGE_CACHE_SIZE-offs,
offs+info->recsize-PAGE_CACHE_SIZE);
ret = mapping->a_ops->commit_write(NULL,page2,0,
offs+info->recsize-PAGE_CACHE_SIZE);
if (ret)
goto out_unlock3;
ret = mapping->a_ops->commit_write(NULL,page,offs,
PAGE_CACHE_SIZE);
unlock_page(page2);
page_cache_release(page2);
if (ret)
goto out_unlock;
} else {
ret = mapping->a_ops->prepare_write(NULL,page,offs,
offs + info->recsize);
if (ret)
goto out_unlock;
put_entry (p, entry);
memcpy(p->spare,entry->spare,((char*)p+info->recsize)-p->spare);
ret = mapping->a_ops->commit_write(NULL,page,offs,
offs + info->recsize);
if (ret)
goto out_unlock;
}
unlock_page(page);
page_cache_release(page);
dir->i_ctime = dir->i_mtime = CURRENT_TIME_SEC;
mark_inode_dirty(dir);
out_dput:
dput(emd_dentry);
out:
Printk (("umsdos_writeentry /mn/: returning %d...\n", ret));
return ret;
out_unlock3:
unlock_page(page2);
page_cache_release(page2);
out_unlock2:
ClearPageUptodate(page);
kunmap(page);
out_unlock:
unlock_page(page);
page_cache_release(page);
printk ("UMSDOS: problem with EMD file: can't write\n");
goto out_dput;
}
/*
* General search, locate a name in the EMD file or an empty slot to
* store it. if info->entry.name_len == 0, search the first empty
* slot (of the proper size).
*
* Return 0 if found, -ENOENT if not found, another error code if
* other problem.
*
* So this routine is used to either find an existing entry or to
* create a new one, while making sure it is a new one. After you
* get -ENOENT, you make sure the entry is stuffed correctly and
* call umsdos_writeentry().
*
* To delete an entry, you find it, zero out the entry (memset)
* and call umsdos_writeentry().
*
* All this to say that umsdos_writeentry must be called after this
* function since it relies on the f_pos field of info.
*
* Note: the caller must hold a lock on the parent directory.
*/
/* #Specification: EMD file structure
* The EMD file uses a fairly simple layout. It is made of records
* (UMSDOS_REC_SIZE == 64). When a name can't be written in a single
* record, multiple contiguous records are allocated.
*/
static int umsdos_find (struct dentry *demd, struct umsdos_info *info)
{
struct umsdos_dirent *entry = &info->entry;
int recsize = info->recsize;
struct inode *emd_dir;
int ret = -ENOENT;
struct {
off_t posok; /* Position available to store the entry */
off_t one; /* One empty position -> maybe <- large enough */
} empty;
int found = 0;
int empty_size = 0;
struct address_space *mapping;
filler_t *readpage;
struct page *page = NULL;
int index = -1;
int offs = PAGE_CACHE_SIZE,max_offs = PAGE_CACHE_SIZE;
char *p = NULL;
loff_t pos = 0;
/* make sure there's an EMD file ... */
ret = -ENOENT;
emd_dir = demd->d_inode;
if (!emd_dir)
goto out_dput;
mapping = emd_dir->i_mapping;
readpage = (filler_t*)mapping->a_ops->readpage;
empty.posok = emd_dir->i_size;
while (1) {
struct umsdos_dirent *rentry;
int entry_size;
if (offs >= max_offs) {
if (page) {
kunmap(page);
page_cache_release(page);
page = NULL;
}
if (pos >= emd_dir->i_size) {
info->f_pos = empty.posok;
break;
}
if (++index == (emd_dir->i_size>>PAGE_CACHE_SHIFT))
max_offs = emd_dir->i_size & ~PAGE_CACHE_MASK;
offs -= PAGE_CACHE_SIZE;
page = read_cache_page(mapping,index,readpage,NULL);
if (IS_ERR(page))
goto sync_fail;
wait_on_page_locked(page);
if (!PageUptodate(page))
goto async_fail;
p = kmap(page);
}
rentry = (struct umsdos_dirent *)(p+offs);
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 = pos;
ret = 0;
break;
}
offs += UMSDOS_REC_SIZE;
pos += UMSDOS_REC_SIZE;
if (found)
continue;
if (!empty_size)
empty.one = pos-UMSDOS_REC_SIZE;
empty_size += UMSDOS_REC_SIZE;
if (empty_size == recsize) {
/* Here is a large enough section. */
empty.posok = empty.one;
found = 1;
}
continue;
}
entry_size = umsdos_evalrecsize(rentry->name_len);
if (entry_size > PAGE_CACHE_SIZE)
goto async_fail;
empty_size = 0;
if (entry->name_len != rentry->name_len)
goto skip_it;
if (entry_size + offs > PAGE_CACHE_SIZE) {
/* Sucker spans the page boundary */
int len = (p+PAGE_CACHE_SIZE)-rentry->name;
struct page *next_page;
char *q;
next_page = read_cache_page(mapping,index+1,readpage,NULL);
if (IS_ERR(next_page)) {
page_cache_release(page);
page = next_page;
goto sync_fail;
}
wait_on_page_locked(next_page);
if (!PageUptodate(next_page)) {
page_cache_release(page);
page = next_page;
goto async_fail;
}
q = kmap(next_page);
if (memcmp(entry->name, rentry->name, len) ||
memcmp(entry->name+len, q, entry->name_len-len)) {
kunmap(next_page);
page_cache_release(next_page);
goto skip_it;
}
kunmap(next_page);
page_cache_release(next_page);
} else if (memcmp (entry->name, rentry->name, entry->name_len))
goto skip_it;
info->f_pos = pos;
get_entry(entry, rentry);
ret = 0;
break;
skip_it:
offs+=entry_size;
pos+=entry_size;
}
if (page) {
kunmap(page);
page_cache_release(page);
}
umsdos_manglename (info);
out_dput:
dput(demd);
return ret;
async_fail:
page_cache_release(page);
page = ERR_PTR(-EIO);
sync_fail:
return PTR_ERR(page);
}
/*
* Add a new entry in the EMD file.
* Return 0 if OK or a negative error code.
* Return -EEXIST if the entry already exists.
*
* Complete the information missing in info.
*
* N.B. What if the EMD file doesn't exist?
*/
int umsdos_newentry (struct dentry *parent, struct umsdos_info *info)
{
int err, ret = -EEXIST;
struct dentry *demd = umsdos_get_emd_dentry(parent);
ret = PTR_ERR(demd);
if (IS_ERR(demd))
goto out;
err = umsdos_find (demd, info);
if (err && err == -ENOENT) {
ret = umsdos_writeentry (parent, info, 0);
Printk (("umsdos_writeentry EMD ret = %d\n", ret));
}
out:
return ret;
}
/*
* Create a new hidden link.
* Return 0 if OK, an error code if not.
*/
/* #Specification: hard link / hidden name
* When a hard link is created, the original file is renamed
* to a hidden name. The name is "..LINKNNN" where NNN is a
* number define from the entry offset in the EMD file.
*/
int umsdos_newhidden (struct dentry *parent, struct umsdos_info *info)
{
int ret;
struct dentry *demd = umsdos_get_emd_dentry(parent);
ret = PTR_ERR(demd);
if (IS_ERR(demd))
goto out;
umsdos_parse ("..LINK", 6, info);
info->entry.name_len = 0;
ret = umsdos_find (demd, info);
if (ret == -ENOENT || ret == 0) {
info->entry.name_len = sprintf (info->entry.name,
"..LINK%ld", info->f_pos);
ret = 0;
}
out:
return ret;
}
/*
* Remove an entry from the EMD file.
* Return 0 if OK, a negative error code otherwise.
*
* Complete the information missing in info.
*/
int umsdos_delentry (struct dentry *parent, struct umsdos_info *info, int isdir)
{
int ret;
struct dentry *demd = umsdos_get_emd_dentry(parent);
ret = PTR_ERR(demd);
if (IS_ERR(demd))
goto out;
ret = umsdos_find (demd, info);
if (ret)
goto out;
if (info->entry.name_len == 0)
goto out;
if ((isdir != 0) != (S_ISDIR (info->entry.mode) != 0)) {
if (S_ISDIR (info->entry.mode)) {
ret = -EISDIR;
} else {
ret = -ENOTDIR;
}
goto out;
}
ret = umsdos_writeentry (parent, info, 1);
out:
return ret;
}
/*
* Verify that an EMD directory is empty.
* Return:
* 0 if not empty,
* 1 if empty (except for EMD file),
* 2 if empty or no EMD file.
*/
int umsdos_isempty (struct dentry *dentry)
{
struct dentry *demd;
int ret = 2;
loff_t pos = 0;
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;
ret = 1;
while (pos < demd->d_inode->i_size) {
struct umsdos_dirent entry;
if (umsdos_emd_dir_readentry (demd, &pos, &entry) != 0) {
ret = 0;
break;
}
if (entry.name_len != 0) {
ret = 0;
break;
}
}
out_dput:
dput(demd);
out:
return ret;
}
/*
* Locate an entry in a EMD directory.
* Return 0 if OK, error code if not, generally -ENOENT.
*
* expect argument:
* 0: anything
* 1: file
* 2: directory
*/
int umsdos_findentry (struct dentry *parent, struct umsdos_info *info,
int expect)
{
int ret;
struct dentry *demd = umsdos_get_emd_dentry(parent);
ret = PTR_ERR(demd);
if (IS_ERR(demd))
goto out;
ret = umsdos_find (demd, 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;
}
/*
* linux/fs/umsdos/inode.c
*
* Written 1993 by Jacques Gelinas
* Inspired from linux/fs/msdos/... by Werner Almesberger
*/
#include <linux/module.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/msdos_fs.h>
#include <linux/kernel.h>
#include <linux/time.h>
#include <linux/errno.h>
#include <asm/uaccess.h>
#include <linux/string.h>
#include <linux/stat.h>
#include <linux/umsdos_fs.h>
#include <linux/list.h>
#include <linux/pagemap.h>
extern struct dentry_operations umsdos_dentry_operations;
struct dentry *saved_root; /* Original root if changed */
struct inode *pseudo_root; /* Useful to simulate the pseudo DOS */
/* directory. See UMSDOS_readdir_x() */
static struct dentry *check_pseudo_root(struct super_block *);
void UMSDOS_put_inode (struct inode *inode)
{
PRINTK ((KERN_DEBUG
"put inode %p (%lu) pos %lu count=%d\n"
,inode, inode->i_ino
,UMSDOS_I(inode)->pos
,atomic_read(&inode->i_count)));
if (inode == pseudo_root) {
Printk ((KERN_ERR "Umsdos: debug: releasing pseudo_root - ino=%lu count=%d\n", inode->i_ino, atomic_read(&inode->i_count)));
}
if (atomic_read(&inode->i_count) == 1)
UMSDOS_I(inode)->i_patched = 0;
}
void UMSDOS_put_super (struct super_block *sb)
{
Printk ((KERN_DEBUG "UMSDOS_put_super: entering\n"));
if (saved_root && pseudo_root && kdev_same(sb->s_dev, ROOT_DEV)) {
shrink_dcache_parent(saved_root);
dput(saved_root);
saved_root = NULL;
pseudo_root = NULL;
}
fat_put_super (sb);
}
/*
* Complete the setup of a directory dentry based on its
* EMD/non-EMD status. If it has an EMD, then plug the
* umsdos function table. If not, use the msdos one.
*/
void umsdos_setup_dir(struct dentry *dir)
{
struct inode *inode = dir->d_inode;
struct umsdos_inode_info *ui = UMSDOS_I(inode);
if (!S_ISDIR(inode->i_mode))
printk(KERN_ERR "umsdos_setup_dir: %s/%s not a dir!\n",
dir->d_parent->d_name.name, dir->d_name.name);
init_waitqueue_head (&ui->dir_info.p);
ui->dir_info.looking = 0;
ui->dir_info.creating = 0;
ui->dir_info.pid = 0;
inode->i_op = &umsdos_rdir_inode_operations;
inode->i_fop = &umsdos_rdir_operations;
if (umsdos_have_emd(dir)) {
Printk((KERN_DEBUG "umsdos_setup_dir: %s/%s using EMD\n",
dir->d_parent->d_name.name, dir->d_name.name));
inode->i_op = &umsdos_dir_inode_operations;
inode->i_fop = &umsdos_dir_operations;
}
}
/*
* Add some info into an inode so it can find its owner quickly
*/
void umsdos_set_dirinfo_new (struct dentry *dentry, off_t f_pos)
{
struct inode *inode = dentry->d_inode;
struct dentry *demd;
UMSDOS_I(inode)->pos = f_pos;
/* now check the EMD file */
demd = umsdos_get_emd_dentry(dentry->d_parent);
if (!IS_ERR(demd)) {
dput(demd);
}
return;
}
static struct inode_operations umsdos_file_inode_operations = {
.truncate = fat_truncate,
.setattr = UMSDOS_notify_change,
};
static struct inode_operations umsdos_symlink_inode_operations = {
.readlink = page_readlink,
.follow_link = page_follow_link,
.setattr = UMSDOS_notify_change,
};
/*
* Connect the proper tables in the inode and add some info.
*/
/* #Specification: inode / umsdos info
* The first time an inode is seen (inode->i_count == 1),
* the inode number of the EMD file which controls this inode
* is tagged to this inode. It allows operations such as
* notify_change to be handled.
*/
void umsdos_patch_dentry_inode(struct dentry *dentry, off_t f_pos)
{
struct inode *inode = dentry->d_inode;
PRINTK (("umsdos_patch_dentry_inode: inode=%lu\n", inode->i_ino));
/*
* Classify the inode based on EMD/non-EMD status.
*/
PRINTK (("umsdos_patch_inode: call umsdos_set_dirinfo_new(%p,%lu)\n",
dentry, f_pos));
umsdos_set_dirinfo_new(dentry, f_pos);
inode->i_op = &umsdos_file_inode_operations;
if (S_ISREG (inode->i_mode)) {
/* address_space operations already set */
} else if (S_ISDIR (inode->i_mode)) {
umsdos_setup_dir(dentry);
} else if (S_ISLNK (inode->i_mode)) {
/* address_space operations already set */
inode->i_op = &umsdos_symlink_inode_operations;
} else
init_special_inode(inode, inode->i_mode,
kdev_t_to_nr(inode->i_rdev));
}
/*
* lock the parent dir before starting ...
* also handles hardlink converting
*/
int UMSDOS_notify_change (struct dentry *dentry, struct iattr *attr)
{
struct inode *dir, *inode;
struct umsdos_info info;
struct dentry *temp, *old_dentry = NULL;
int ret;
lock_kernel();
ret = umsdos_parse (dentry->d_name.name, dentry->d_name.len,
&info);
if (ret)
goto out;
ret = umsdos_findentry (dentry->d_parent, &info, 0);
if (ret) {
printk("UMSDOS_notify_change: %s/%s not in EMD, ret=%d\n",
dentry->d_parent->d_name.name, dentry->d_name.name, ret);
goto out;
}
if (info.entry.flags & UMSDOS_HLINK) {
/*
* In order to get the correct (real) inode, we just drop
* the original dentry.
*/
d_drop(dentry);
Printk(("UMSDOS_notify_change: hard link %s/%s, fake=%s\n",
dentry->d_parent->d_name.name, dentry->d_name.name, info.fake.fname));
/* Do a real lookup to get the short name dentry */
temp = umsdos_covered(dentry->d_parent, info.fake.fname,
info.fake.len);
ret = PTR_ERR(temp);
if (IS_ERR(temp))
goto out;
/* now resolve the link ... */
temp = umsdos_solve_hlink(temp);
ret = PTR_ERR(temp);
if (IS_ERR(temp))
goto out;
old_dentry = dentry;
dentry = temp; /* so umsdos_notify_change_locked will operate on that */
}
dir = dentry->d_parent->d_inode;
inode = dentry->d_inode;
ret = inode_change_ok (inode, attr);
if (ret)
goto out;
ret = umsdos_notify_change_locked(dentry, attr);
if (ret == 0)
ret = inode_setattr (inode, attr);
out:
if (old_dentry)
dput (dentry); /* if we had to use fake dentry for hardlinks, dput() it now */
unlock_kernel();
return ret;
}
/*
* Must be called with the parent lock held.
*/
int umsdos_notify_change_locked(struct dentry *dentry, struct iattr *attr)
{
struct inode *inode = dentry->d_inode;
struct dentry *demd;
struct address_space *mapping;
struct page *page;
int ret = 0;
struct umsdos_dirent *entry;
int offs;
Printk(("UMSDOS_notify_change: entering for %s/%s (%d)\n",
dentry->d_parent->d_name.name, dentry->d_name.name, UMSDOS_I(inode)->i_patched));
if (inode->i_nlink == 0)
goto out;
if (inode->i_ino == UMSDOS_ROOT_INO)
goto out;
/* get the EMD file dentry */
demd = umsdos_get_emd_dentry(dentry->d_parent);
ret = PTR_ERR(demd);
if (IS_ERR(demd))
goto out;
ret = 0;
/* don't do anything if directory is not promoted to umsdos yet */
if (!demd->d_inode) {
Printk((KERN_DEBUG
"UMSDOS_notify_change: no EMD file %s/%s\n",
demd->d_parent->d_name.name, demd->d_name.name));
goto out_dput;
}
/* don't do anything if this is the EMD itself */
if (inode == demd->d_inode)
goto out_dput;
/* This inode is not a EMD file nor an inode used internally
* by MSDOS, so we can update its status.
* See emd.c
*/
/* Read only the start of the entry since we don't touch the name */
mapping = demd->d_inode->i_mapping;
offs = UMSDOS_I(inode)->pos & ~PAGE_CACHE_MASK;
ret = -ENOMEM;
page=grab_cache_page(mapping,UMSDOS_I(inode)->pos>>PAGE_CACHE_SHIFT);
if (!page)
goto out_dput;
ret=mapping->a_ops->prepare_write(NULL,page,offs,offs+UMSDOS_REC_SIZE);
if (ret)
goto out_unlock;
entry = (struct umsdos_dirent *) (page_address(page) + offs);
if (attr->ia_valid & ATTR_UID)
entry->uid = cpu_to_le16(attr->ia_uid);
if (attr->ia_valid & ATTR_GID)
entry->gid = cpu_to_le16(attr->ia_gid);
if (attr->ia_valid & ATTR_MODE)
entry->mode = cpu_to_le16(attr->ia_mode);
if (attr->ia_valid & ATTR_ATIME)
entry->atime = cpu_to_le32(attr->ia_atime);
if (attr->ia_valid & ATTR_MTIME)
entry->mtime = cpu_to_le32(attr->ia_mtime);
if (attr->ia_valid & ATTR_CTIME)
entry->ctime = cpu_to_le32(attr->ia_ctime);
entry->nlink = cpu_to_le16(inode->i_nlink);
ret=mapping->a_ops->commit_write(NULL,page,offs,offs+UMSDOS_REC_SIZE);
if (ret)
printk(KERN_WARNING
"umsdos_notify_change: %s/%s EMD write error, ret=%d\n",
dentry->d_parent->d_name.name, dentry->d_name.name,ret);
/* #Specification: notify_change / msdos fs
* notify_change operation are done only on the
* EMD file. The msdos fs is not even called.
*/
out_unlock:
unlock_page(page);
page_cache_release(page);
out_dput:
dput(demd);
out:
return ret;
}
/*
* Update the disk with the inode content
*/
int UMSDOS_write_inode (struct inode *inode, int wait)
{
struct iattr newattrs;
int ret;
ret = fat_write_inode (inode, wait);
newattrs.ia_mtime = inode->i_mtime;
newattrs.ia_atime = inode->i_atime;
newattrs.ia_ctime = inode->i_ctime;
newattrs.ia_valid = ATTR_MTIME | ATTR_ATIME | ATTR_CTIME;
/*
* UMSDOS_notify_change is convenient to call here
* to update the EMD entry associated with this inode.
* But it has the side effect to re"dirt" the inode.
*/
/*
* UMSDOS_notify_change (inode, &newattrs);
* inode->i_state &= ~I_DIRTY; / * FIXME: this doesn't work. We need to remove ourselves from list on dirty inodes. /mn/ */
return ret;
}
static struct super_operations umsdos_sops =
{
.write_inode = UMSDOS_write_inode,
.put_inode = UMSDOS_put_inode,
.delete_inode = fat_delete_inode,
.put_super = UMSDOS_put_super,
.statfs = UMSDOS_statfs,
.clear_inode = fat_clear_inode,
};
int UMSDOS_statfs(struct super_block *sb,struct statfs *buf)
{
int ret;
ret = fat_statfs (sb, buf);
if (!ret)
buf->f_namelen = UMSDOS_MAXNAME;
return ret;
}
/*
* Read the super block of an Extended MS-DOS FS.
*/
struct super_block *UMSDOS_read_super (struct super_block *sb, void *data,
int silent)
{
struct super_block *res;
struct dentry *new_root;
/*
* Call msdos-fs to mount the disk.
* Note: this returns res == sb or NULL
*/
MSDOS_SB(sb)->options.isvfat = 0;
res = fat_read_super(sb, data, silent, &umsdos_rdir_inode_operations);
if (IS_ERR(res))
return NULL;
if (res == NULL) {
if (!silent)
printk(KERN_INFO "VFS: Can't find a valid "
"UMSDOS filesystem on dev %s.\n", sb->s_id);
return NULL;
}
printk (KERN_INFO "UMSDOS 0.86k "
"(compatibility level %d.%d, fast msdos)\n",
UMSDOS_VERSION, UMSDOS_RELEASE);
sb->s_op = &umsdos_sops;
MSDOS_SB(sb)->options.dotsOK = 0; /* disable hidden==dotfile */
/* install our dentry operations ... */
sb->s_root->d_op = &umsdos_dentry_operations;
umsdos_patch_dentry_inode(sb->s_root, 0);
/* Check whether to change to the /linux root */
new_root = check_pseudo_root(sb);
if (new_root) {
/* sanity check */
if (new_root->d_op != &umsdos_dentry_operations)
printk("umsdos_read_super: pseudo-root wrong ops!\n");
pseudo_root = new_root->d_inode;
saved_root = sb->s_root;
printk(KERN_INFO "UMSDOS: changed to alternate root\n");
dget (sb->s_root); sb->s_root = dget(new_root);
}
return sb;
}
/*
* Check for an alternate root if we're the root device.
*/
extern kdev_t ROOT_DEV;
static struct dentry *check_pseudo_root(struct super_block *sb)
{
struct dentry *root, *sbin, *init;
/*
* Check whether we're mounted as the root device.
* must check like this, because we can be used with initrd
*/
if (!kdev_same(sb->s_dev, ROOT_DEV))
goto out_noroot;
/*
* lookup_dentry needs a (so far non-existent) root.
*/
printk(KERN_INFO "check_pseudo_root: mounted as root\n");
root = lookup_one_len(UMSDOS_PSDROOT_NAME, sb->s_root,UMSDOS_PSDROOT_LEN);
if (IS_ERR(root))
goto out_noroot;
if (!root->d_inode || !S_ISDIR(root->d_inode->i_mode))
goto out_dput;
printk(KERN_INFO "check_pseudo_root: found %s/%s\n",
root->d_parent->d_name.name, root->d_name.name);
/* look for /sbin/init */
sbin = lookup_one_len("sbin", root, 4);
if (IS_ERR(sbin))
goto out_dput;
if (!sbin->d_inode || !S_ISDIR(sbin->d_inode->i_mode))
goto out_dput_sbin;
init = lookup_one_len("init", sbin, 4);
if (IS_ERR(init))
goto out_dput_sbin;
if (!init->d_inode)
goto out_dput_init;
printk(KERN_INFO "check_pseudo_root: found %s/%s, enabling pseudo-root\n", init->d_parent->d_name.name, init->d_name.name);
dput(sbin);
dput(init);
return root;
/* Alternate root not found ... */
out_dput_init:
dput(init);
out_dput_sbin:
dput(sbin);
out_dput:
dput(root);
out_noroot:
return NULL;
}
static DECLARE_FSTYPE_DEV(umsdos_fs_type, "umsdos", UMSDOS_read_super);
static int __init init_umsdos_fs (void)
{
return register_filesystem (&umsdos_fs_type);
}
static void __exit exit_umsdos_fs (void)
{
unregister_filesystem (&umsdos_fs_type);
}
module_init(init_umsdos_fs)
module_exit(exit_umsdos_fs)
MODULE_LICENSE("GPL");
/*
* linux/fs/umsdos/ioctl.c
*
* Written 1993 by Jacques Gelinas
*
* Extended MS-DOS ioctl directory handling functions
*
* Changes:
* 11/07/2003 Daniele Bellucci <bellucda@tiscali.it>
* - audit copy_to_user/put_user in umsdos_ioctl_fill.
*/
#include <asm/uaccess.h>
#include <linux/errno.h>
#include <linux/mm.h>
#include <linux/kernel.h>
#include <linux/time.h>
#include <linux/fs.h>
#include <linux/msdos_fs.h>
#include <linux/umsdos_fs.h>
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,
loff_t offset,
ino_t ino,
unsigned type)
{
int ret = -EINVAL;
struct UMSDOS_DIR_ONCE *d = (struct UMSDOS_DIR_ONCE *) buf;
if (d->count == 0) {
if (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))
return -EFAULT;
d->count = 1;
ret = 0;
}
return ret;
}
/*
* Perform special function on a directory
*/
/* #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 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 / access
* 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;
loff_t 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;
ret = 0;
if (!demd->d_inode)
goto read_dput;
while (pos < demd->d_inode->i_size) {
off_t f_pos = pos;
struct umsdos_dirent entry;
struct umsdos_info info;
ret = umsdos_emd_dir_readentry (demd, &pos, &entry);
if (ret == -ENAMETOOLONG) {
printk (KERN_INFO "Fixing EMD entry with invalid size -- zeroing out\n");
memset (&info, 0, sizeof (info));
info.f_pos = f_pos;
info.recsize = UMSDOS_REC_SIZE;
ret = umsdos_writeentry (dentry, &info, 1);
continue;
}
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 = 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.
*/
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));
umsdos_setup_dir (dentry);
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);
d_drop(new_dentry);
d_drop(old_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);
if (!ret)
d_delete(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);
if (!ret)
d_delete(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_fmask =
dir->i_sb->u.msdos_sb.options.fs_dmask =
data.umsdos_dirent.mode;
ret = 0;
}
out:
Printk (("ioctl %d, returning %d\n", cmd, ret));
return ret;
}
/*
* linux/fs/umsdos/mangle.c
*
* Written 1993 by Jacques Gelinas
*
* Control the mangling of file name to fit msdos name space.
* Many optimisations by GLU == dglaude@is1.vub.ac.be (Glaude David)
*/
#include <linux/errno.h>
#include <linux/string.h>
#include <linux/kernel.h>
#include <linux/umsdos_fs.h>
/* (This file is used outside of the kernel) */
#ifndef __KERNEL__
#define KERN_WARNING
#endif
/*
* Complete the mangling of the MSDOS fake name
* based on the position of the entry in the EMD file.
*
* Simply complete the job of umsdos_parse; fill the extension.
*
* Beware that info->f_pos must be set.
*/
void umsdos_manglename (struct umsdos_info *info)
{
if (info->msdos_reject) {
/* #Specification: file name / non MSDOS conforming / mangling
* Each non MSDOS conforming file has a special extension
* build from the entry position in the EMD file.
*
* This number is then transform in a base 32 number, where
* each digit is expressed like hexadecimal number, using
* digit and letter, except it uses 22 letters from 'a' to 'v'.
* The number 32 comes from 2**5. It is faster to split a binary
* number using a base which is a power of two. And I was 32
* when I started this project. Pick your answer :-) .
*
* If the result is '0', it is replace with '_', simply
* to make it odd.
*
* This is true for the first two character of the extension.
* The last one is taken from a list of odd character, which
* are:
*
* { } ( ) ! ` ^ & @
*
* With this scheme, we can produce 9216 ( 9* 32 * 32)
* different extensions which should not clash with any useful
* extension already popular or meaningful. Since most directory
* have much less than 32 * 32 files in it, the first character
* of the extension of any mangled name will be {.
*
* Here are the reason to do this (this kind of mangling).
*
* -The mangling is deterministic. Just by the extension, we
* are able to locate the entry in the EMD file.
*
* -By keeping to beginning of the file name almost unchanged,
* we are helping the MSDOS user.
*
* -The mangling produces names not too ugly, so an msdos user
* may live with it (remember it, type it, etc...).
*
* -The mangling produces names ugly enough so no one will
* ever think of using such a name in real life. This is not
* fool proof. I don't think there is a total solution to this.
*/
int entry_num;
char *pt = info->fake.fname + info->fake.len;
/* lookup for encoding the last character of the extension
* It contains valid character after the ugly one to make sure
* even if someone overflows the 32 * 32 * 9 limit, it still
* does something
*/
#define SPECIAL_MANGLING '{','}','(',')','!','`','^','&','@'
static char lookup3[] =
{
SPECIAL_MANGLING,
/* This is the start of lookup12 */
'_', '1', '2', '3', '4', '5', '6', '7', '8', '9',
'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o',
'p', 'q', 'r', 's', 't', 'u', 'v'
};
#define lookup12 (lookup3+9)
entry_num = info->f_pos / UMSDOS_REC_SIZE;
if (entry_num > (9* 32 * 32)){
printk (KERN_WARNING "UMSDOS: more than 9216 files in a directory.\n"
"This may break the mangling strategy.\n"
"Not a killer problem. See doc.\n");
}
*pt++ = '.';
*pt++ = lookup3 [(entry_num >> 10) & 31];
*pt++ = lookup12[(entry_num >> 5) & 31];
*pt++ = lookup12[entry_num & 31];
*pt = '\0'; /* help doing printk */
info->fake.len += 4;
info->msdos_reject = 0; /* Avoid mangling twice */
}
}
/*
* Evaluate the record size needed to store of name of len character.
* The value returned is a multiple of UMSDOS_REC_SIZE.
*/
int umsdos_evalrecsize (int len)
{
struct umsdos_dirent dirent;
int nbrec = 1 + ((len - 1 + (dirent.name - (char *) &dirent))
/ UMSDOS_REC_SIZE);
return nbrec * UMSDOS_REC_SIZE;
/*
* GLU This should be inlined or something to speed it up to the max.
* GLU nbrec is absolutely not needed to return the value.
*/
}
#ifdef TEST
int umsdos_evalrecsize_old (int len)
{
struct umsdos_dirent dirent;
int size = len + (dirent.name - (char *) &dirent);
int nbrec = size / UMSDOS_REC_SIZE;
int extra = size % UMSDOS_REC_SIZE;
if (extra > 0)
nbrec++;
return nbrec * UMSDOS_REC_SIZE;
}
#endif
/*
* Fill the struct info with the full and msdos name of a file
* Return 0 if all is OK, a negative error code otherwise.
*/
int umsdos_parse (
const char *fname,
int len,
struct umsdos_info *info)
{
int ret = -ENAMETOOLONG;
/* #Specification: file name / too long
* If a file name exceed UMSDOS maxima, the file name is silently
* truncated. This makes it conformant with the other file system
* of Linux (minix and ext2 at least).
*/
if (len > UMSDOS_MAXNAME)
len = UMSDOS_MAXNAME;
{
const char *firstpt = NULL; /* First place we saw a "." in fname */
/* #Specification: file name / non MSDOS conforming / base length 0
* file names beginning with a period '.' are invalid for MS-DOS.
* It needs absolutely a base name. So the file name is mangled
*/
int ivldchar = fname[0] == '.'; /* At least one invalid character */
int msdos_len = len;
int base_len;
/*
* cardinal_per_size tells if there exists at least one
* DOS pseudo device on length n. See the test below.
*/
static const char cardinal_per_size[9] =
{
0, 0, 0, 1, 1, 0, 1, 0, 1
};
/*
* lkp translate all character to acceptable character (for DOS).
* When lkp[n] == n, it means also it is an acceptable one.
* So it serves both as a flag and as a translator.
*/
static char lkp[256];
static char is_init;
if (!is_init) {
/*
* Initialisation of the array is easier and less error
* prone like this.
*/
int i;
static const char *spc = "\"*+,/:;<=>?[\\]|~";
is_init = 1;
for (i = 0; i <= 32; i++)
lkp[i] = '#';
for (i = 33; i < 'A'; i++)
lkp[i] = (char) i;
for (i = 'A'; i <= 'Z'; i++)
lkp[i] = (char) (i + ('a' - 'A'));
for (i = 'Z' + 1; i < 127; i++)
lkp[i] = (char) i;
for (i = 128; i < 256; i++)
lkp[i] = '#';
lkp['.'] = '_';
while (*spc != '\0')
lkp[(unsigned char) (*spc++)] = '#';
}
/* GLU
* File names longer than 8+'.'+3 are invalid for MS-DOS,
* so the file name is to be mangled--no further test is needed.
* This speeds up handling of long names.
* The position of the last point is no more necessary anyway.
*/
if (len <= (8 + 1 + 3)) {
const char *pt = fname;
const char *endpt = fname + len;
while (pt < endpt) {
if (*pt == '.') {
if (firstpt != NULL) {
/* 2 . in a file name. Reject */
ivldchar = 1;
break;
} else {
int extlen = (int) (endpt - pt);
firstpt = pt;
if (firstpt - fname > 8) {
/* base name longer than 8: reject */
ivldchar = 1;
break;
} else if (extlen > 4) {
/* Extension longer than 4 (including .): reject */
ivldchar = 1;
break;
} else if (extlen == 1) {
/* #Specification: file name / non MSDOS conforming / last char == .
* If the last character of a file name is
* a period, mangling is applied. MS-DOS does
* not support those file names.
*/
ivldchar = 1;
break;
} else if (extlen == 4) {
/* #Specification: file name / non MSDOS conforming / mangling clash
* To avoid clash with the umsdos mangling, any file
* with a special character as the first character
* of the extension will be mangled. This solves the
* following problem:
*
* #
* touch FILE
* # FILE is invalid for DOS, so mangling is applied
* # file.{_1 is created in the DOS directory
* touch file.{_1
* # To UMSDOS file point to a single DOS entry.
* # So file.{_1 has to be mangled.
* #
*/
static char special[] =
{
SPECIAL_MANGLING, '\0'
};
if (strchr (special, firstpt[1]) != NULL) {
ivldchar = 1;
break;
}
}
}
} else if (lkp[(unsigned char) (*pt)] != *pt) {
ivldchar = 1;
break;
}
pt++;
}
} else {
ivldchar = 1;
}
if (ivldchar
|| (firstpt == NULL && len > 8)
|| (len == UMSDOS_EMD_NAMELEN
&& memcmp (fname, UMSDOS_EMD_FILE, UMSDOS_EMD_NAMELEN) == 0)) {
/* #Specification: file name / --linux-.---
* The name of the EMD file --linux-.--- is map to a mangled
* name. So UMSDOS does not restrict its use.
*/
/* #Specification: file name / non MSDOS conforming / mangling
* Non MSDOS conforming file names must use some alias to fit
* in the MSDOS name space.
*
* The strategy is simple. The name is simply truncated to
* 8 char. points are replace with underscore and a
* number is given as an extension. This number correspond
* to the entry number in the EMD file. The EMD file
* only need to carry the real name.
*
* Upper case is also converted to lower case.
* Control character are converted to #.
* Spaces are converted to #.
* The following characters are also converted to #.
* #
* " * + , / : ; < = > ? [ \ ] | ~
* #
*
* Sometimes the problem is not in MS-DOS itself but in
* command.com.
*/
int i;
char *pt = info->fake.fname;
base_len = msdos_len = (msdos_len > 8) ? 8 : msdos_len;
/*
* There is no '.' any more so we know for a fact that
* the base length is the length.
*/
memcpy (info->fake.fname, fname, msdos_len);
for (i = 0; i < msdos_len; i++, pt++)
*pt = lkp[(unsigned char) (*pt)];
*pt = '\0'; /* GLU We force null termination. */
info->msdos_reject = 1;
/*
* The numeric extension is added only when we know
* the position in the EMD file, in umsdos_newentry(),
* umsdos_delentry(), and umsdos_findentry().
* See umsdos_manglename().
*/
} else {
/* Conforming MSDOS file name */
strncpy (info->fake.fname, fname, len);
info->msdos_reject = 0;
base_len = firstpt != NULL ? (int) (firstpt - fname) : len;
}
if (cardinal_per_size[base_len]) {
/* #Specification: file name / MSDOS devices / mangling
* To avoid unreachable file from MS-DOS, any MS-DOS conforming
* file with a basename equal to one of the MS-DOS pseudo
* devices will be mangled.
*
* If a file such as "prn" was created, it would be unreachable
* under MS-DOS because "prn" is assumed to be the printer, even
* if the file does have an extension.
*
* Since the extension is unimportant to MS-DOS, we must patch
* the basename also. We simply insert a minus '-'. To avoid
* conflict with valid file with a minus in front (such as
* "-prn"), we add an mangled extension like any other
* mangled file name.
*
* Here is the list of DOS pseudo devices:
*
* #
* "prn","con","aux","nul",
* "lpt1","lpt2","lpt3","lpt4",
* "com1","com2","com3","com4",
* "clock$"
* #
*
* and some standard ones for common DOS programs
*
* "emmxxxx0","xmsxxxx0","setverxx"
*
* (Thanks to Chris Hall <cah17@phoenix.cambridge.ac.uk>
* for pointing these out to me).
*
* Is there one missing?
*/
/* This table must be ordered by length */
static const char *tbdev[] =
{
"prn", "con", "aux", "nul",
"lpt1", "lpt2", "lpt3", "lpt4",
"com1", "com2", "com3", "com4",
"clock$",
"emmxxxx0", "xmsxxxx0", "setverxx"
};
/* Tell where to find in tbdev[], the first name of */
/* a certain length */
static const char start_ind_dev[9] =
{
0, 0, 0, 4, 12, 12, 13, 13, 16
};
char basen[9];
int i;
for (i = start_ind_dev[base_len - 1]; i < start_ind_dev[base_len]; i++) {
if (memcmp (info->fake.fname, tbdev[i], base_len) == 0) {
memcpy (basen, info->fake.fname, base_len);
basen[base_len] = '\0'; /* GLU We force null termination. */
/*
* GLU We do that only if necessary; we try to do the
* GLU simple thing in the usual circumstance.
*/
info->fake.fname[0] = '-';
strcpy (info->fake.fname + 1, basen); /* GLU We already guaranteed a null would be at the end. */
msdos_len = (base_len == 8) ? 8 : base_len + 1;
info->msdos_reject = 1;
break;
}
}
}
info->fake.fname[msdos_len] = '\0'; /* Help doing printk */
/* GLU This zero should (always?) be there already. */
info->fake.len = msdos_len;
/* Why not use info->fake.len everywhere? Is it longer?
*/
memcpy (info->entry.name, fname, len);
info->entry.name[len] = '\0'; /* for printk */
info->entry.name_len = len;
ret = 0;
}
/*
* Evaluate how many records are needed to store this entry.
*/
info->recsize = umsdos_evalrecsize (len);
return ret;
}
#ifdef TEST
struct MANG_TEST {
char *fname; /* Name to validate */
int msdos_reject; /* Expected msdos_reject flag */
char *msname; /* Expected msdos name */
};
struct MANG_TEST tb[] =
{
"hello", 0, "hello",
"hello.1", 0, "hello.1",
"hello.1_", 0, "hello.1_",
"prm", 0, "prm",
#ifdef PROPOSITION
"HELLO", 1, "hello",
"Hello.1", 1, "hello.1",
"Hello.c", 1, "hello.c",
#else
/*
* I find the three examples below very unfortunate. I propose to
* convert them to lower case in a quick preliminary pass, then test
* whether there are other troublesome characters. I have not made
* this change, because it is not easy, but I wanted to mention the
* principle. Obviously something like that would increase the chance
* of collisions, for example between "HELLO" and "Hello", but these
* can be treated elsewhere along with the other collisions.
*/
"HELLO", 1, "hello",
"Hello.1", 1, "hello_1",
"Hello.c", 1, "hello_c",
#endif
"hello.{_1", 1, "hello_{_",
"hello\t", 1, "hello#",
"hello.1.1", 1, "hello_1_",
"hel,lo", 1, "hel#lo",
"Salut.Tu.vas.bien?", 1, "salut_tu",
".profile", 1, "_profile",
".xv", 1, "_xv",
"toto.", 1, "toto_",
"clock$.x", 1, "-clock$",
"emmxxxx0", 1, "-emmxxxx",
"emmxxxx0.abcd", 1, "-emmxxxx",
"aux", 1, "-aux",
"prn", 1, "-prn",
"prn.abc", 1, "-prn",
"PRN", 1, "-prn",
/*
* GLU WARNING: the results of these are different with my version
* GLU of mangling compared to the original one.
* GLU CAUSE: the manner of calculating the baselen variable.
* GLU For you they are always 3.
* GLU For me they are respectively 7, 8, and 8.
*/
"PRN.abc", 1, "prn_abc",
"Prn.abcd", 1, "prn_abcd",
"prn.abcd", 1, "prn_abcd",
"Prn.abcdefghij", 1, "prn_abcd"
};
int main (int argc, char *argv[])
{
int i, rold, rnew;
printf ("Testing the umsdos_parse.\n");
for (i = 0; i < sizeof (tb) / sizeof (tb[0]); i++) {
struct MANG_TEST *pttb = tb + i;
struct umsdos_info info;
int ok = umsdos_parse (pttb->fname, strlen (pttb->fname), &info);
if (strcmp (info.fake.fname, pttb->msname) != 0) {
printf ("**** %s -> ", pttb->fname);
printf ("%s <> %s\n", info.fake.fname, pttb->msname);
} else if (info.msdos_reject != pttb->msdos_reject) {
printf ("**** %s -> %s ", pttb->fname, pttb->msname);
printf ("%d <> %d\n", info.msdos_reject, pttb->msdos_reject);
} else {
printf (" %s -> %s %d\n", pttb->fname, pttb->msname
,pttb->msdos_reject);
}
}
printf ("Testing the new umsdos_evalrecsize.");
for (i = 0; i < UMSDOS_MAXNAME; i++) {
rnew = umsdos_evalrecsize (i);
rold = umsdos_evalrecsize_old (i);
if (!(i % UMSDOS_REC_SIZE)) {
printf ("\n%d:\t", i);
}
if (rnew != rold) {
printf ("**** %d newres: %d != %d \n", i, rnew, rold);
} else {
printf (".");
}
}
printf ("\nEnd of Testing.\n");
return 0;
}
#endif
/*
* linux/fs/umsdos/namei.c
*
* Written 1993 by Jacques Gelinas
* Inspired from linux/fs/msdos/... by Werner Almesberger
*
* Maintain and access the --linux alternate directory file.
*/
/*
* You are in the maze of twisted functions - half of them shouldn't
* be here...
*/
#include <linux/errno.h>
#include <linux/kernel.h>
#include <linux/time.h>
#include <linux/types.h>
#include <linux/fcntl.h>
#include <linux/stat.h>
#include <linux/string.h>
#include <linux/msdos_fs.h>
#include <linux/umsdos_fs.h>
#include <linux/slab.h>
#define UMSDOS_DIR_LOCK
#ifdef UMSDOS_DIR_LOCK
static inline void u_sleep_on (struct inode *dir)
{
sleep_on (&UMSDOS_I(dir)->dir_info.p);
}
static inline void u_wake_up (struct inode *dir)
{
wake_up (&UMSDOS_I(dir)->dir_info.p);
}
/*
* Wait for creation exclusivity.
* Return 0 if the dir was already available.
* Return 1 if a wait was necessary.
* When 1 is return, it means a wait was done. It does not
* mean the directory is available.
*/
static int umsdos_waitcreate (struct inode *dir)
{
int ret = 0;
if (UMSDOS_I(dir)->dir_info.creating
&& UMSDOS_I(dir)->dir_info.pid != current->pid) {
PRINTK (("creating && dir_info.pid=%lu, current->pid=%u\n", UMSDOS_I(dir)->dir_info.pid, current->pid));
u_sleep_on (dir);
ret = 1;
}
return ret;
}
/*
* Wait for any lookup process to finish
*/
static void umsdos_waitlookup (struct inode *dir)
{
while (UMSDOS_I(dir)->dir_info.looking) {
u_sleep_on (dir);
}
}
/*
* Lock all other process out of this directory.
*/
/* #Specification: file creation / not atomic
* File creation is a two step process. First we create (allocate)
* an entry in the EMD file and then (using the entry offset) we
* build a unique name for MSDOS. We create this name in the msdos
* space.
*
* We have to use semaphore (sleep_on/wake_up) to prevent lookup
* into a directory when we create a file or directory and to
* prevent creation while a lookup is going on. Since many lookup
* may happen at the same time, the semaphore is a counter.
*
* Only one creation is allowed at the same time. This protection
* may not be necessary. The problem arise mainly when a lookup
* or a readdir is done while a file is partially created. The
* lookup process see that as a "normal" problem and silently
* erase the file from the EMD file. Normal because a file
* may be erased during a MSDOS session, but not removed from
* the EMD file.
*
* The locking is done on a directory per directory basis. Each
* directory inode has its wait_queue.
*
* For some operation like hard link, things even get worse. Many
* creation must occur at once (atomic). To simplify the design
* a process is allowed to recursively lock the directory for
* creation. The pid of the locking process is kept along with
* a counter so a second level of locking is granted or not.
*/
void umsdos_lockcreate (struct inode *dir)
{
/*
* Wait for any creation process to finish except
* if we (the process) own the lock
*/
while (umsdos_waitcreate (dir) != 0);
UMSDOS_I(dir)->dir_info.creating++;
UMSDOS_I(dir)->dir_info.pid = current->pid;
umsdos_waitlookup (dir);
}
/*
* Lock all other process out of those two directories.
*/
static void umsdos_lockcreate2 (struct inode *dir1, struct inode *dir2)
{
/*
* We must check that both directory are available before
* locking anyone of them. This is to avoid some deadlock.
* Thanks to dglaude@is1.vub.ac.be (GLAUDE DAVID) for pointing
* this to me.
*/
while (1) {
if (umsdos_waitcreate (dir1) == 0
&& umsdos_waitcreate (dir2) == 0) {
/* We own both now */
UMSDOS_I(dir1)->dir_info.creating++;
UMSDOS_I(dir1)->dir_info.pid = current->pid;
UMSDOS_I(dir2)->dir_info.creating++;
UMSDOS_I(dir2)->dir_info.pid = current->pid;
break;
}
}
umsdos_waitlookup (dir1);
umsdos_waitlookup (dir2);
}
/*
* Wait until creation is finish in this directory.
*/
void umsdos_startlookup (struct inode *dir)
{
while (umsdos_waitcreate (dir) != 0);
UMSDOS_I(dir)->dir_info.looking++;
}
/*
* Unlock the directory.
*/
void umsdos_unlockcreate (struct inode *dir)
{
UMSDOS_I(dir)->dir_info.creating--;
if (UMSDOS_I(dir)->dir_info.creating < 0) {
printk ("UMSDOS: UMSDOS_I(dir)->dir_info.creating < 0: %d"
,UMSDOS_I(dir)->dir_info.creating);
}
u_wake_up (dir);
}
/*
* Tell directory lookup is over.
*/
void umsdos_endlookup (struct inode *dir)
{
UMSDOS_I(dir)->dir_info.looking--;
if (UMSDOS_I(dir)->dir_info.looking < 0) {
printk ("UMSDOS: UMSDOS_I(dir)->dir_info.looking < 0: %d"
,UMSDOS_I(dir)->dir_info.looking);
}
u_wake_up (dir);
}
#else
static void umsdos_lockcreate (struct inode *dir)
{
}
static void umsdos_lockcreate2 (struct inode *dir1, struct inode *dir2)
{
}
void umsdos_startlookup (struct inode *dir)
{
}
static void umsdos_unlockcreate (struct inode *dir)
{
}
void umsdos_endlookup (struct inode *dir)
{
}
#endif
static int umsdos_nevercreat (struct inode *dir, struct dentry *dentry,
int errcod)
{
int ret = 0;
if (umsdos_is_pseudodos (dir, dentry)) {
/* #Specification: pseudo root / any file creation /DOS
* The pseudo sub-directory /DOS can't be created!
* EEXIST is returned.
*
* The pseudo sub-directory /DOS can't be removed!
* EPERM is returned.
*/
ret = errcod;
}
return ret;
}
/*
* Add a new file (ordinary or special) into the alternate directory.
* The file is added to the real MSDOS directory. If successful, it
* is then added to the EMD file.
*
* Return the status of the operation. 0 mean success.
*
* #Specification: create / file exists in DOS
* Here is a situation: we are trying to create a file with
* UMSDOS. The file is unknown to UMSDOS but already
* exists in the DOS directory.
*
* Here is what we are NOT doing:
*
* We could silently assume that everything is fine
* and allows the creation to succeed.
*
* It is possible not all files in the partition
* are meant to be visible from linux. By trying to create
* those file in some directory, one user may get access
* to those file without proper permissions. Looks like
* a security hole to me. Off course sharing a file system
* with DOS is some kind of security hole :-)
*
* So ?
*
* We return EEXIST in this case.
* The same is true for directory creation.
*/
static int umsdos_create_any (struct inode *dir, struct dentry *dentry,
int mode, dev_t rdev, char flags)
{
struct dentry *fake;
struct inode *inode;
int ret;
struct umsdos_info info;
ret = umsdos_nevercreat (dir, dentry, -EEXIST);
if (ret)
goto out;
ret = umsdos_parse (dentry->d_name.name, dentry->d_name.len, &info);
if (ret)
goto out;
info.entry.mode = mode;
info.entry.rdev = rdev;
info.entry.flags = flags;
info.entry.uid = current->fsuid;
info.entry.gid = (dir->i_mode & S_ISGID) ? dir->i_gid : current->fsgid;
info.entry.ctime = info.entry.atime = info.entry.mtime = get_seconds();
info.entry.nlink = 1;
ret = umsdos_newentry (dentry->d_parent, &info);
if (ret)
goto out;
/* do a real lookup to get the short name dentry */
fake = umsdos_covered(dentry->d_parent, info.fake.fname, info.fake.len);
ret = PTR_ERR(fake);
if (IS_ERR(fake))
goto out_remove;
/* should not exist yet ... */
ret = -EEXIST;
if (fake->d_inode)
goto out_remove_dput;
ret = msdos_create (dir, fake, S_IFREG | 0777, NULL);
if (ret)
goto out_remove_dput;
inode = fake->d_inode;
atomic_inc(&inode->i_count);
d_instantiate (dentry, inode);
dput(fake);
if (atomic_read(&inode->i_count) > 1) {
printk(KERN_WARNING
"umsdos_create_any: %s/%s, ino=%ld, icount=%d??\n",
dentry->d_parent->d_name.name, dentry->d_name.name,
inode->i_ino, atomic_read(&inode->i_count));
}
umsdos_lookup_patch_new(dentry, &info);
out:
return ret;
/* Creation failed ... remove the EMD entry */
out_remove_dput:
dput(fake);
out_remove:
if (ret == -EEXIST)
printk(KERN_WARNING "UMSDOS: out of sync, deleting %s/%s\n",
dentry->d_parent->d_name.name, info.fake.fname);
umsdos_delentry (dentry->d_parent, &info, S_ISDIR (info.entry.mode));
goto out;
}
/*
* Add a new file into the alternate directory.
* The file is added to the real MSDOS directory. If successful, it
* is then added to the EMD file.
*
* Return the status of the operation. 0 mean success.
*/
int UMSDOS_create (struct inode *dir, struct dentry *dentry, int mode, struct nameidata *nd)
{
return umsdos_create_any (dir, dentry, mode, 0, 0);
}
/*
* Initialise the new_entry from the old for a rename operation.
* (Only useful for umsdos_rename_f() below).
*/
static void umsdos_ren_init (struct umsdos_info *new_info,
struct umsdos_info *old_info)
{
new_info->entry.mode = old_info->entry.mode;
new_info->entry.rdev = old_info->entry.rdev;
new_info->entry.uid = old_info->entry.uid;
new_info->entry.gid = old_info->entry.gid;
new_info->entry.ctime = old_info->entry.ctime;
new_info->entry.atime = old_info->entry.atime;
new_info->entry.mtime = old_info->entry.mtime;
new_info->entry.flags = old_info->entry.flags;
new_info->entry.nlink = old_info->entry.nlink;
}
/*
* Rename a file (move) in the file system.
*/
static int umsdos_rename_f (struct inode *old_dir, struct dentry *old_dentry,
struct inode *new_dir, struct dentry *new_dentry,
int flags)
{
struct inode *old_inode = old_dentry->d_inode;
struct dentry *old, *new, *old_emd;
int err, ret;
struct umsdos_info old_info;
struct umsdos_info new_info;
ret = -EPERM;
err = umsdos_parse (old_dentry->d_name.name,
old_dentry->d_name.len, &old_info);
if (err)
goto out;
err = umsdos_parse (new_dentry->d_name.name,
new_dentry->d_name.len, &new_info);
if (err)
goto out;
/* Get the EMD dentry for the old parent */
old_emd = umsdos_get_emd_dentry(old_dentry->d_parent);
ret = PTR_ERR(old_emd);
if (IS_ERR(old_emd))
goto out;
umsdos_lockcreate2 (old_dir, new_dir);
ret = umsdos_findentry(old_emd->d_parent, &old_info, 0);
if (ret)
goto out_unlock;
err = umsdos_findentry(new_dentry->d_parent, &new_info, 0);
if (err == 0) {
/* check whether it _really_ exists ... */
ret = -EEXIST;
if (new_dentry->d_inode)
goto out_unlock;
/* bogus lookup? complain and fix up the EMD ... */
printk(KERN_WARNING
"umsdos_rename_f: entry %s/%s exists, inode NULL??\n",
new_dentry->d_parent->d_name.name, new_info.entry.name);
err = umsdos_delentry(new_dentry->d_parent, &new_info,
S_ISDIR(new_info.entry.mode));
}
umsdos_ren_init (&new_info, &old_info);
if (flags)
new_info.entry.flags = flags;
ret = umsdos_newentry (new_dentry->d_parent, &new_info);
if (ret)
goto out_unlock;
/* If we're moving a hardlink, drop it first */
if (old_info.entry.flags & UMSDOS_HLINK) {
d_drop(old_dentry);
}
old = umsdos_covered(old_dentry->d_parent, old_info.fake.fname,
old_info.fake.len);
ret = PTR_ERR(old);
if (IS_ERR(old))
goto out_unlock;
/* make sure it's the same inode! */
ret = -ENOENT;
/*
* note: for hardlinks they will be different!
* old_inode will contain inode of .LINKxxx file containing data, and
* old->d_inode will contain inode of file containing path to .LINKxxx file
*/
if (!(old_info.entry.flags & UMSDOS_HLINK)) {
if (old->d_inode != old_inode)
goto out_dput;
}
new = umsdos_covered(new_dentry->d_parent, new_info.fake.fname,
new_info.fake.len);
ret = PTR_ERR(new);
if (IS_ERR(new))
goto out_dput;
/* Do the msdos-level rename */
ret = msdos_rename (old_dir, old, new_dir, new);
dput(new);
/* If the rename failed, remove the new EMD entry */
if (ret != 0) {
umsdos_delentry (new_dentry->d_parent, &new_info,
S_ISDIR (new_info.entry.mode));
goto out_dput;
}
/*
* Rename successful ... remove the old name from the EMD.
* Note that we use the EMD parent here, as the old dentry
* may have moved to a new parent ...
*/
err = umsdos_delentry (old_emd->d_parent, &old_info,
S_ISDIR (old_info.entry.mode));
if (err) {
/* Failed? Complain a bit, but don't fail the operation */
printk(KERN_WARNING
"umsdos_rename_f: delentry %s/%s failed, error=%d\n",
old_emd->d_parent->d_name.name, old_info.entry.name,
err);
}
/*
* Update f_pos so notify_change will succeed
* if the file was already in use.
*/
umsdos_set_dirinfo_new(old_dentry, new_info.f_pos);
/* dput() the dentry if we haven't already */
out_dput:
dput(old);
out_unlock:
dput(old_emd);
umsdos_unlockcreate (old_dir);
umsdos_unlockcreate (new_dir);
out:
Printk ((" _ret=%d\n", ret));
return ret;
}
/*
* Setup a Symbolic link or a (pseudo) hard link
* Return a negative error code or 0 if OK.
*/
/* #Specification: symbolic links / strategy
* A symbolic link is simply a file which holds a path. It is
* implemented as a normal MSDOS file (not very space efficient :-()
*
* I see two different ways to do this: One is to place the link data
* in unused entries of the EMD file; the other is to have a separate
* file dedicated to hold all symbolic links data.
*
* Let's go for simplicity...
*/
/*
* AV. Should be called with dir->i_sem down.
*/
static int umsdos_symlink_x (struct inode *dir, struct dentry *dentry,
const char *symname, int mode, char flags)
{
int ret, len;
ret = umsdos_create_any (dir, dentry, mode, 0, flags);
if (ret) {
printk(KERN_WARNING
"umsdos_symlink: create failed, ret=%d\n", ret);
goto out;
}
len = strlen (symname) + 1;
ret = page_symlink(dentry->d_inode, symname, len);
if (ret < 0)
goto out_unlink;
out:
return ret;
out_unlink:
printk(KERN_WARNING "umsdos_symlink: write failed, unlinking\n");
UMSDOS_unlink (dir, dentry);
d_drop(dentry);
goto out;
}
/*
* Setup a Symbolic link.
* Return a negative error code or 0 if OK.
*/
int UMSDOS_symlink ( struct inode *dir, struct dentry *dentry,
const char *symname)
{
return umsdos_symlink_x (dir, dentry, symname, S_IFLNK | 0777, 0);
}
/*
* Add a link to an inode in a directory
*/
int UMSDOS_link (struct dentry *olddentry, struct inode *dir,
struct dentry *dentry)
{
struct inode *oldinode = olddentry->d_inode;
struct inode *olddir = olddentry->d_parent->d_inode;
struct dentry *temp;
char *path;
unsigned long buffer;
int ret;
struct umsdos_info old_info;
struct umsdos_info hid_info;
#ifdef UMSDOS_DEBUG_VERBOSE
printk("umsdos_link: new %s/%s -> %s/%s\n",
dentry->d_parent->d_name.name, dentry->d_name.name,
olddentry->d_parent->d_name.name, olddentry->d_name.name);
#endif
ret = -EPERM;
if (S_ISDIR (oldinode->i_mode))
goto out;
ret = umsdos_nevercreat (dir, dentry, -EPERM);
if (ret)
goto out;
ret = -ENOMEM;
buffer = get_zeroed_page(GFP_KERNEL);
if (!buffer)
goto out;
/*
* Lock the link parent if it's not the same directory.
*/
ret = -EDEADLOCK;
if (olddir != dir) {
if (atomic_read(&olddir->i_sem.count) < 1)
goto out_free;
down(&olddir->i_sem);
}
/*
* Parse the name and get the visible directory entry.
*/
ret = umsdos_parse (olddentry->d_name.name, olddentry->d_name.len,
&old_info);
if (ret)
goto out_unlock;
ret = umsdos_findentry (olddentry->d_parent, &old_info, 1);
if (ret) {
printk("UMSDOS_link: %s/%s not in EMD, ret=%d\n",
olddentry->d_parent->d_name.name, olddentry->d_name.name, ret);
goto out_unlock;
}
/*
* If the visible dentry is a pseudo-hardlink, the original
* file must be already hidden.
*/
if (!(old_info.entry.flags & UMSDOS_HLINK)) {
int err;
/* create a hidden link name */
ret = umsdos_newhidden (olddentry->d_parent, &hid_info);
if (ret) {
printk("umsdos_link: can't make hidden %s/%s, ret=%d\n",
olddentry->d_parent->d_name.name, hid_info.entry.name, ret);
goto out_unlock;
}
/*
* Make a dentry and rename the original file ...
*/
temp = umsdos_lookup_dentry(olddentry->d_parent,
hid_info.entry.name,
hid_info.entry.name_len, 0);
ret = PTR_ERR(temp);
if (IS_ERR(temp)) {
printk("umsdos_link: lookup %s/%s failed, ret=%d\n",
dentry->d_parent->d_name.name, hid_info.entry.name, ret);
goto cleanup;
}
/* rename the link to the hidden location ... */
ret = umsdos_rename_f(olddir, olddentry, olddir, temp,
UMSDOS_HIDDEN);
d_move(olddentry, temp);
dput(temp);
if (ret) {
printk("umsdos_link: rename to %s/%s failed, ret=%d\n",
temp->d_parent->d_name.name, temp->d_name.name, ret);
goto cleanup;
}
/* mark the inode as a hardlink */
UMSDOS_I(oldinode)->i_is_hlink = 1;
/*
* Capture the path to the hidden link.
*/
path = umsdos_d_path(olddentry, (char *) buffer, PAGE_SIZE);
Printk(("umsdos_link: hidden link path=%s\n", path));
/*
* Recreate a dentry for the original name and symlink it,
* then symlink the new dentry. Don't give up if one fails,
* or we'll lose the file completely!
*
* Note: this counts as the "original" reference, so we
* don't increment i_nlink for this one.
*/
temp = umsdos_lookup_dentry(olddentry->d_parent,
old_info.entry.name,
old_info.entry.name_len, 0);
ret = PTR_ERR(temp);
if (!IS_ERR(temp)) {
ret = umsdos_symlink_x (olddir, temp, path,
S_IFREG | 0777, UMSDOS_HLINK);
dput(temp);
}
/* This symlink increments i_nlink (see below.) */
err = umsdos_symlink_x (dir, dentry, path,
S_IFREG | 0777, UMSDOS_HLINK);
/* fold the two errors */
if (!ret)
ret = err;
goto out_unlock;
/* creation failed ... remove the link entry */
cleanup:
printk("umsdos_link: link failed, ret=%d, removing %s/%s\n",
ret, olddentry->d_parent->d_name.name, hid_info.entry.name);
err = umsdos_delentry(olddentry->d_parent, &hid_info, 0);
goto out_unlock;
}
Printk(("UMSDOS_link: %s/%s already hidden\n",
olddentry->d_parent->d_name.name, olddentry->d_name.name));
/*
* The original file is already hidden, and we need to get
* the dentry for its real name, not the visible name.
* N.B. make sure it's the hidden inode ...
*/
if (!UMSDOS_I(oldinode)->i_is_hlink)
printk("UMSDOS_link: %s/%s hidden, ino=%ld not hlink??\n",
olddentry->d_parent->d_name.name,
olddentry->d_name.name, oldinode->i_ino);
/*
* In order to get the correct (real) inode, we just drop
* the original dentry.
*/
d_drop(olddentry);
Printk(("UMSDOS_link: hard link %s/%s, fake=%s\n",
olddentry->d_parent->d_name.name, olddentry->d_name.name, old_info.fake.fname));
/* Do a real lookup to get the short name dentry */
temp = umsdos_covered(olddentry->d_parent, old_info.fake.fname,
old_info.fake.len);
ret = PTR_ERR(temp);
if (IS_ERR(temp))
goto out_unlock;
/* now resolve the link ... */
temp = umsdos_solve_hlink(temp);
ret = PTR_ERR(temp);
if (IS_ERR(temp))
goto out_unlock;
path = umsdos_d_path(temp, (char *) buffer, PAGE_SIZE);
dput(temp);
Printk(("umsdos_link: %s/%s already hidden, path=%s\n",
olddentry->d_parent->d_name.name, olddentry->d_name.name, path));
/* finally we can symlink it ... */
ret = umsdos_symlink_x (dir, dentry, path, S_IFREG | 0777,UMSDOS_HLINK);
out_unlock:
/* remain locked for the call to notify_change ... */
if (ret == 0) {
struct iattr newattrs;
/* Do a real lookup to get the short name dentry */
temp = umsdos_covered(olddentry->d_parent,
old_info.fake.fname,
old_info.fake.len);
ret = PTR_ERR(temp);
if (IS_ERR(temp))
goto out_unlock2;
/* now resolve the link ... */
temp = umsdos_solve_hlink(temp);
ret = PTR_ERR(temp);
if (IS_ERR(temp))
goto out_unlock2;
#ifdef UMSDOS_PARANOIA
if (!UMSDOS_I(oldinode)->i_is_hlink)
printk("UMSDOS_link: %s/%s, ino=%ld, not marked as hlink!\n",
olddentry->d_parent->d_name.name, olddentry->d_name.name, oldinode->i_ino);
#endif
temp->d_inode->i_nlink++;
Printk(("UMSDOS_link: linked %s/%s, ino=%ld, nlink=%d\n",
olddentry->d_parent->d_name.name, olddentry->d_name.name,
oldinode->i_ino, oldinode->i_nlink));
newattrs.ia_valid = 0;
ret = umsdos_notify_change_locked(temp, &newattrs);
if (ret == 0)
mark_inode_dirty(temp->d_inode);
dput(temp);
out_unlock2:
if (ret == 0)
mark_inode_dirty(olddentry->d_inode);
}
if (olddir != dir)
up(&olddir->i_sem);
out_free:
free_page(buffer);
out:
Printk (("umsdos_link %d\n", ret));
return ret;
}
/*
* Add a sub-directory in a directory
*/
/* #Specification: mkdir / Directory already exist in DOS
* We do the same thing as for file creation.
* For all user it is an error.
*/
/* #Specification: mkdir / umsdos directory / create EMD
* When we created a new sub-directory in a UMSDOS
* directory (one with full UMSDOS semantics), we
* create immediately an EMD file in the new
* sub-directory so it inherits UMSDOS semantics.
*/
int UMSDOS_mkdir (struct inode *dir, struct dentry *dentry, int mode)
{
struct dentry *temp;
struct inode *inode;
int ret, err;
struct umsdos_info info;
ret = umsdos_nevercreat (dir, dentry, -EEXIST);
if (ret)
goto out;
ret = umsdos_parse (dentry->d_name.name, dentry->d_name.len, &info);
if (ret)
goto out;
info.entry.mode = mode | S_IFDIR;
info.entry.rdev = 0;
info.entry.uid = current->fsuid;
info.entry.gid = (dir->i_mode & S_ISGID) ? dir->i_gid : current->fsgid;
info.entry.ctime = info.entry.atime = info.entry.mtime = get_seconds();
info.entry.flags = 0;
info.entry.nlink = 1;
ret = umsdos_newentry (dentry->d_parent, &info);
if (ret)
goto out;
/* lookup the short name dentry */
temp = umsdos_covered(dentry->d_parent, info.fake.fname, info.fake.len);
ret = PTR_ERR(temp);
if (IS_ERR(temp))
goto out_remove;
/* Make sure the short name doesn't exist */
ret = -EEXIST;
if (temp->d_inode) {
printk("umsdos_mkdir: short name %s/%s exists\n",
dentry->d_parent->d_name.name, info.fake.fname);
goto out_remove_dput;
}
ret = msdos_mkdir (dir, temp, mode);
if (ret)
goto out_remove_dput;
/*
* Lock the inode to protect the EMD creation ...
*/
inode = temp->d_inode;
down(&inode->i_sem);
atomic_inc(&inode->i_count);
d_instantiate(dentry, inode);
/* N.B. this should have an option to create the EMD ... */
umsdos_lookup_patch_new(dentry, &info);
/*
* Create the EMD file, and set up the dir so it is
* promoted to EMD with the EMD file invisible.
*
* N.B. error return if EMD fails?
*/
err = umsdos_make_emd(dentry);
umsdos_setup_dir(dentry);
up(&inode->i_sem);
dput(temp);
out:
Printk(("umsdos_mkdir: %s/%s, ret=%d\n",
dentry->d_parent->d_name.name, dentry->d_name.name, ret));
return ret;
/* an error occurred ... remove EMD entry. */
out_remove_dput:
dput(temp);
out_remove:
umsdos_delentry (dentry->d_parent, &info, 1);
goto out;
}
/*
* Add a new device special file into a directory.
*
* #Specification: Special files / strategy
* Device special file, pipes, etc ... are created like normal
* file in the msdos file system. Of course they remain empty.
*
* One strategy was to create those files only in the EMD file
* since they were not important for MSDOS. The problem with
* that, is that there were not getting inode number allocated.
* The MSDOS filesystems is playing a nice game to fake inode
* number, so why not use it.
*
* The absence of inode number compatible with those allocated
* for ordinary files was causing major trouble with hard link
* in particular and other parts of the kernel I guess.
*/
int UMSDOS_mknod (struct inode *dir, struct dentry *dentry,
int mode, dev_t rdev)
{
return umsdos_create_any (dir, dentry, mode, rdev, 0);
}
/*
* Remove a sub-directory.
*/
int UMSDOS_rmdir (struct inode *dir, struct dentry *dentry)
{
struct dentry *temp;
int ret, err, empty;
struct umsdos_info info;
ret = umsdos_nevercreat (dir, dentry, -EPERM);
if (ret)
goto out;
ret = -EBUSY;
if (!d_unhashed(dentry))
goto out;
/* check whether the EMD is empty */
ret = -ENOTEMPTY;
empty = umsdos_isempty (dentry);
/* Have to remove the EMD file? */
if (empty == 1) {
struct dentry *demd;
demd = umsdos_get_emd_dentry(dentry);
if (!IS_ERR(demd)) {
err = -ENOENT;
if (demd->d_inode)
err = msdos_unlink (dentry->d_inode, demd);
Printk (("UMSDOS_rmdir: unlinking empty EMD err=%d", err));
#ifdef UMSDOS_PARANOIA
if (err)
printk("umsdos_rmdir: EMD %s/%s unlink failed, err=%d\n",
demd->d_parent->d_name.name, demd->d_name.name, err);
#endif
if (!err) {
d_delete(demd);
ret = 0;
}
dput(demd);
}
} else if (empty == 2)
ret = 0;
if (ret)
goto out;
umsdos_parse (dentry->d_name.name, dentry->d_name.len, &info);
/* Call findentry to complete the mangling */
umsdos_findentry (dentry->d_parent, &info, 2);
temp = umsdos_covered(dentry->d_parent, info.fake.fname, info.fake.len);
ret = PTR_ERR(temp);
if (IS_ERR(temp))
goto out;
/*
* Attempt to remove the msdos name.
*/
ret = msdos_rmdir (dir, temp);
if (ret && ret != -ENOENT)
goto out_dput;
d_delete(temp);
/* OK so far ... remove the name from the EMD */
ret = umsdos_delentry (dentry->d_parent, &info, 1);
#ifdef UMSDOS_PARANOIA
if (ret)
printk("umsdos_rmdir: delentry %s failed, ret=%d\n", info.entry.name, ret);
#endif
/* dput() temp if we didn't do it above */
out_dput:
dput(temp);
out:
Printk (("umsdos_rmdir %d\n", ret));
return ret;
}
/*
* Remove a file from the directory.
*
* #Specification: hard link / deleting a link
* When we delete a file and this file is a link,
* we must subtract 1 from the nlink field of the
* hidden link.
*
* If the count goes to 0, we delete this hidden
* link too.
*/
int UMSDOS_unlink (struct inode *dir, struct dentry *dentry)
{
struct dentry *temp, *link = NULL;
struct inode *inode;
int ret;
struct umsdos_info info;
Printk(("UMSDOS_unlink: entering %s/%s\n",
dentry->d_parent->d_name.name, dentry->d_name.name));
ret = umsdos_nevercreat (dir, dentry, -EPERM);
if (ret)
goto out;
ret = umsdos_parse (dentry->d_name.name, dentry->d_name.len, &info);
if (ret)
goto out;
umsdos_lockcreate (dir);
ret = umsdos_findentry (dentry->d_parent, &info, 1);
if (ret) {
printk("UMSDOS_unlink: %s/%s not in EMD, ret=%d\n",
dentry->d_parent->d_name.name, dentry->d_name.name, ret);
goto out_unlock;
}
Printk (("UMSDOS_unlink %.*s ", info.fake.len, info.fake.fname));
/*
* Note! If this is a hardlink and the names are aliased,
* the short-name lookup will return the hardlink dentry.
* In order to get the correct (real) inode, we just drop
* the original dentry.
*/
if (info.entry.flags & UMSDOS_HLINK) {
d_drop(dentry);
}
/* Do a real lookup to get the short name dentry */
temp = umsdos_covered(dentry->d_parent, info.fake.fname, info.fake.len);
ret = PTR_ERR(temp);
if (IS_ERR(temp))
goto out_unlock;
/*
* Resolve hardlinks now, but defer processing until later.
*/
if (info.entry.flags & UMSDOS_HLINK) {
link = umsdos_solve_hlink(dget(temp));
}
/* Delete the EMD entry */
ret = umsdos_delentry (dentry->d_parent, &info, 0);
if (ret && ret != -ENOENT) {
printk(KERN_WARNING "UMSDOS_unlink: delentry %s, error=%d\n",
info.entry.name, ret);
goto out_dput;
}
ret = msdos_unlink(dir, temp);
if (!ret)
d_delete(temp);
#ifdef UMSDOS_PARANOIA
if (ret)
printk("umsdos_unlink: %s/%s unlink failed, ret=%d\n",
temp->d_parent->d_name.name, temp->d_name.name, ret);
#endif
/* dput() temp if we didn't do it above */
out_dput:
dput(temp);
out_unlock:
umsdos_unlockcreate (dir);
/*
* Now check for deferred handling of a hardlink.
*/
if (!link)
goto out;
if (IS_ERR(link)) {
printk("umsdos_unlink: failed to resolve %s/%s\n",
dentry->d_parent->d_name.name, dentry->d_name.name);
if (!ret)
ret = PTR_ERR(link);
goto out;
}
Printk(("umsdos_unlink: link %s/%s deferred, pending ret=%d\n",
link->d_parent->d_name.name, link->d_name.name, ret));
/* already have an error? */
if (ret)
goto out_cleanup;
/* make sure the link exists ... */
inode = link->d_inode;
if (!inode) {
printk(KERN_WARNING "umsdos_unlink: hard link not found\n");
goto out_cleanup;
}
/*
* If this was the last linked reference, delete it now.
*
* N.B. Deadlock problem? We should be holding the lock
* for the hardlink's parent, but another process might
* be holding that lock waiting for us to finish ...
*/
if (inode->i_nlink <= 1) {
ret = UMSDOS_unlink (link->d_parent->d_inode, link);
if (ret) {
printk(KERN_WARNING
"umsdos_unlink: link removal failed, ret=%d\n",
ret);
} else
d_delete(link);
} else {
struct iattr newattrs;
inode->i_nlink--;
newattrs.ia_valid = 0;
ret = umsdos_notify_change_locked(link, &newattrs);
if (!ret)
mark_inode_dirty(link->d_inode);
}
out_cleanup:
d_drop(link);
dput(link);
out:
Printk (("umsdos_unlink %d\n", ret));
return ret;
}
/*
* Rename (move) a file.
*/
int UMSDOS_rename (struct inode *old_dir, struct dentry *old_dentry,
struct inode *new_dir, struct dentry *new_dentry)
{
int ret;
ret = umsdos_nevercreat (new_dir, new_dentry, -EEXIST);
if (ret)
return ret;
/*
* If the target already exists, delete it first.
*/
if (new_dentry->d_inode) {
dget(new_dentry);
if (S_ISDIR(old_dentry->d_inode->i_mode))
ret = UMSDOS_rmdir (new_dir, new_dentry);
else
ret = UMSDOS_unlink (new_dir, new_dentry);
if (!ret)
d_drop(new_dentry);
dput(new_dentry);
if (ret)
return ret;
}
ret = umsdos_rename_f(old_dir, old_dentry, new_dir, new_dentry, 0);
return ret;
}
/*
* linux/fs/umsdos/rdir.c
*
* Written 1994 by Jacques Gelinas
*
* Extended MS-DOS directory pure MS-DOS handling functions
* (For directory without EMD file).
*/
#include <linux/time.h>
#include <linux/fs.h>
#include <linux/msdos_fs.h>
#include <linux/errno.h>
#include <linux/stat.h>
#include <linux/limits.h>
#include <linux/umsdos_fs.h>
#include <linux/slab.h>
#include <linux/smp_lock.h>
#include <asm/uaccess.h>
extern struct dentry *saved_root;
extern struct inode *pseudo_root;
extern struct dentry_operations umsdos_dentry_operations;
struct RDIR_FILLDIR {
void *dirbuf;
filldir_t filldir;
int real_root;
};
static int rdir_filldir ( void *buf,
const char *name,
int name_len,
loff_t offset,
ino_t ino,
unsigned int d_type)
{
int ret = 0;
struct RDIR_FILLDIR *d = (struct RDIR_FILLDIR *) buf;
if (d->real_root) {
PRINTK ((KERN_DEBUG "rdir_filldir /mn/: real root!\n"));
/* real root of a pseudo_rooted partition */
if (name_len != UMSDOS_PSDROOT_LEN
|| memcmp (name, UMSDOS_PSDROOT_NAME, UMSDOS_PSDROOT_LEN) != 0) {
/* So it is not the /linux directory */
if (name_len == 2 && name[0] == '.' && name[1] == '.') {
/* Make sure the .. entry points back to the pseudo_root */
ino = pseudo_root->i_ino;
}
ret = d->filldir (d->dirbuf, name, name_len, offset, ino, DT_UNKNOWN);
}
} else {
/* Any DOS directory */
ret = d->filldir (d->dirbuf, name, name_len, offset, ino, DT_UNKNOWN);
}
return ret;
}
static int UMSDOS_rreaddir (struct file *filp, void *dirbuf, filldir_t filldir)
{
struct inode *dir = filp->f_dentry->d_inode;
struct RDIR_FILLDIR bufk;
int ret;
lock_kernel();
bufk.filldir = filldir;
bufk.dirbuf = dirbuf;
bufk.real_root = pseudo_root && (dir == saved_root->d_inode);
ret = fat_readdir (filp, &bufk, rdir_filldir);
unlock_kernel();
return ret;
}
/*
* Lookup into a non promoted directory.
* If the result is a directory, make sure we find out if it is
* a promoted one or not (calling umsdos_setup_dir_inode(inode)).
*/
/* #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, NULL);
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));
/* only patch if needed (because we get called even for lookup
(not only rlookup) stuff sometimes, like in umsdos_covered() */
if (UMSDOS_I(dentry->d_inode)->i_patched == 0)
umsdos_patch_dentry_inode(dentry, 0);
}
out:
/* always install our dentry ops ... */
dentry->d_op = &umsdos_dentry_operations;
return ret;
}
struct dentry *UMSDOS_rlookup ( struct inode *dir, struct dentry *dentry, struct nameidata *nd)
{
return umsdos_rlookup_x (dir, dentry, 0);
}
/* #Specification: dual mode / rmdir in a DOS directory
* In a DOS (not EMD in it) directory, we use a reverse strategy
* compared with a UMSDOS directory. We assume that a subdirectory
* of a DOS directory is also a DOS directory. This is not always
* true (umssync may be used anywhere), but makes sense.
*
* So we call msdos_rmdir() directly. If it failed with a -ENOTEMPTY
* then we check if it is a Umsdos directory. We check if it is
* really empty (only . .. and --linux-.--- in it). If it is true
* we remove the EMD and do a msdos_rmdir() again.
*
* In a Umsdos directory, we assume all subdirectories are also
* Umsdos directories, so we check the EMD file first.
*/
/* #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;
}
/* #Specification: dual mode / introduction
* One goal of UMSDOS is to allow a practical and simple coexistence
* between MS-DOS and Linux in a single partition. Using the EMD file
* in each directory, UMSDOS adds Unix semantics and capabilities to
* a normal DOS filesystem. To help and simplify coexistence, here is
* the logic related to the EMD file.
*
* If it is missing, then the directory is managed by the MS-DOS driver.
* The names are limited to DOS limits (8.3). No links, no device special
* and pipe and so on.
*
* If it is there, it is the directory. If it is there but empty, then
* the directory looks empty. The utility umssync allows synchronisation
* of the real DOS directory and the EMD.
*
* Whenever umssync is applied to a directory without EMD, one is
* created on the fly. The directory is promoted to full Unix semantics.
* Of course, the ls command will show exactly the same content as before
* the umssync session.
*
* It is believed that the user/admin will promote directories to Unix
* semantics as needed.
*
* The strategy to implement this is to use two function table (struct
* inode_operations). One for true UMSDOS directory and one for directory
* with missing EMD.
*
* Functions related to the DOS semantic (but aware of UMSDOS) generally
* have a "r" prefix (r for real) such as UMSDOS_rlookup, to differentiate
* from the one with full UMSDOS semantics.
*/
struct file_operations umsdos_rdir_operations =
{
.read = generic_read_dir,
.readdir = UMSDOS_rreaddir,
.ioctl = UMSDOS_ioctl_dir,
};
struct inode_operations umsdos_rdir_inode_operations =
{
.create = msdos_create,
.lookup = UMSDOS_rlookup,
.unlink = msdos_unlink,
.mkdir = msdos_mkdir,
.rmdir = UMSDOS_rrmdir,
.rename = msdos_rename,
.setattr = UMSDOS_notify_change,
};
/* #Specification: umsdos / readdir
* umsdos_readdir() should fill a struct dirent with
* an inode number. The cheap way to get it is to
* do a lookup in the MSDOS directory for each
* entry processed by the readdir() function.
* This is not very efficient, but very simple. The
* other way around is to maintain a copy of the inode
* number in the EMD file. This is a problem because
* this has to be maintained in sync using tricks.
* Remember that MSDOS (the OS) does not update the
* modification time (mtime) of a directory. There is
* no easy way to tell that a directory was modified
* during a DOS session and synchronise the EMD file.
*/
/* #Specification: readdir / . and ..
* The msdos filesystem manages the . and .. entry properly
* so the EMD file won't hold any info about it.
*
* In readdir, we assume that for the root directory
* the read position will be 0 for ".", 1 for "..". For
* a non root directory, the read position will be 0 for "."
* and 32 for "..".
*/
/*
* This is a trick used by the msdos file system (fs/msdos/dir.c)
* to manage . and .. for the root directory of a file system.
* Since there is no such entry in the root, fs/msdos/dir.c
* use the following:
*
* if f_pos == 0, return ".".
* if f_pos == 1, return "..".
*
* So let msdos handle it
*
* Since umsdos entries are much larger, we share the same f_pos.
* if f_pos is 0 or 1 or 32, we are clearly looking at . and
* ..
*
* As soon as we get f_pos == 2 or f_pos == 64, then back to
* 0, but this time we are reading the EMD file.
*
* Well, not so true. The problem, is that UMSDOS_REC_SIZE is
* also 64, so as soon as we read the first record in the
* EMD, we are back at offset 64. So we set the offset
* to UMSDOS_SPECIAL_DIRFPOS(3) as soon as we have read the
* .. entry from msdos.
*
* Now (linux 1.3), umsdos_readdir can read more than one
* entry even if we limit (umsdos_dir_once) to only one:
* It skips over hidden file. So we switch to
* UMSDOS_SPECIAL_DIRFPOS as soon as we have read successfully
* the .. entry.
*/
/* #Specification: umsdos / lookup / inode info
* After successfully reading an inode from the MSDOS
* filesystem, we use the EMD file to complete it.
* We update the following field.
*
* uid, gid, atime, ctime, mtime, mode.
*
* We rely on MSDOS for mtime. If the file
* was modified during an MSDOS session, at least
* mtime will be meaningful. We do this only for regular
* file.
*
* We don't rely on MS-DOS for mtime for directories
* because the MS-DOS date on a directory is its
* creation time (strange MSDOS behavior) which
* corresponds to none of the three Unix time stamps.
*/
/* #Specification: umsdos / conversion mode
* The msdos filesystem can do some inline conversion
* of the data of a file. It can translate silently
* from the MS-DOS text file format to the Unix one
* (CRLF -> LF) while reading, and the reverse
* while writing. This is activated using the mount
* option conv=....
*
* This is not useful for Linux files in a promoted
* directory. It can even be harmful. For this
* reason, the binary (no conversion) mode is
* always activated.
*/
/* #Specification: umsdos / conversion mode / todo
* A flag could be added to file and directories
* forcing an automatic conversion mode (as
* done with the msdos filesystem).
*
* This flag could be setup on a directory basis
* (instead of file) and all files in it would
* logically inherit it. If the conversion mode
* is active (conv=) then the i_binary flag would
* be left untouched in those directories.
*
* It was proposed that the sticky bit be used to set
* this. A problem with that is that new files would
* be written incorrectly. The other problem is that
* the sticky bit has a meaning for directories. So
* another bit should be used (there is some space
* in the EMD file for it) and a special utility
* would be used to assign the flag to a directory).
* I don't think it is useful to assign this flag
* on a single file.
*/
* #Specification: weakness / rename
* There is a case where UMSDOS rename has a different behavior
* than a normal Unix file system. Renaming an open file across
* directory boundary does not work. Renaming an open file within
* a directory does work, however.
*
* The problem may is in Linux VFS driver for msdos.
* I believe this is not a bug but a design feature, because
* an inode number represents some sort of directory address
* in the MSDOS directory structure, so moving the file into
* another directory does not preserve the inode number.
*/
/* #Specification: rename / new name exist
* If the destination name already exists, it will
* silently be removed. EXT2 does it this way
* and this is the spec of SunOS. So does UMSDOS.
*
* If the destination is an empty directory it will
* also be removed.
*/
/* #Specification: rename / new name exist / possible flaw
* The code to handle the deletion of the target (file
* and directory) use to be in umsdos_rename_f, surrounded
* by proper directory locking. This was ensuring that only
* one process could achieve a rename (modification) operation
* in the source and destination directory. This was also
* ensuring the operation was "atomic".
*
* This has been changed because this was creating a
* stack overflow (the stack is only 4 kB) in the kernel. To avoid
* the code doing the deletion of the target (if exist) has
* been moved to a upper layer. umsdos_rename_f is tried
* once and if it fails with EEXIST, the target is removed
* and umsdos_rename_f is done again.
*
* This makes the code cleaner and may solve a
* deadlock problem one tester was experiencing.
*
* The point is to mention that possibly, the semantic of
* "rename" may be wrong. Anyone dare to check that :-)
* Be aware that IF it is wrong, to produce the problem you
* will need two process trying to rename a file to the
* same target at the same time. Again, I am not sure it
* is a problem at all.
*/
/* #Specification: hard link / strategy
* Hard links are difficult to implement on top of an MS-DOS FAT file
* system. Unlike Unix file systems, there are no inodes. A directory
* entry holds the functionality of the inode and the entry.
*
* We will used the same strategy as a normal Unix file system
* (with inodes) except we will do it symbolically (using paths).
*
* Because anything can happen during a DOS session (defragment,
* directory sorting, etc.), we can't rely on an MS-DOS pseudo
* inode number to record the link. For this reason, the link
* will be done using hidden symbolic links. The following
* scenario illustrates how it works.
*
* Given a file /foo/file
*
* #
* ln /foo/file /tmp/file2
*
* become internally
*
* mv /foo/file /foo/-LINK1
* ln -s /foo/-LINK1 /foo/file
* ln -s /foo/-LINK1 /tmp/file2
* #
*
* Using this strategy, we can operate on /foo/file or /foo/file2.
* We can remove one and keep the other, like a normal Unix hard link.
* We can rename /foo/file or /tmp/file2 independently.
*
* The entry -LINK1 will be hidden. It will hold a link count.
* When all link are erased, the hidden file is erased too.
*/
/* #Specification: weakness / hard link
* The strategy for hard link introduces a side effect that
* may or may not be acceptable. Here is the sequence
*
* #
* mkdir subdir1
* touch subdir1/file
* mkdir subdir2
* ln subdir1/file subdir2/file
* rm subdir1/file
* rmdir subdir1
* rmdir: subdir1: Directory not empty
* #
*
* This happen because there is an invisible file (--link) in
* subdir1 which is referenced by subdir2/file.
*
* Any idea ?
*/
/* #Specification: weakness / hard link / rename directory
* Another weakness of hard link come from the fact that
* it is based on hidden symbolic links. Here is an example.
*
* #
* mkdir /subdir1
* touch /subdir1/file
* mkdir /subdir2
* ln /subdir1/file subdir2/file
* mv /subdir1 subdir3
* ls -l /subdir2/file
* #
*
* Since /subdir2/file is a hidden symbolic link
* to /subdir1/..hlinkNNN, accessing it will fail since
* /subdir1 does not exist anymore (has been renamed).
*/
/* #Specification: hard link / directory
* A hard link can't be made on a directory. EPERM is returned
* in this case.
*/
/* #Specification: hard link / first hard link
* The first time a hard link is done on a file, this
* file must be renamed and hidden. Then an internal
* symbolic link must be done on the hidden file.
*
* The second link is done after on this hidden file.
*
* It is expected that the Linux MSDOS file system
* keeps the same pseudo inode when a rename operation
* is done on a file in the same directory.
*/
/* #Specification: function name / convention
* A simple convention for function names has been used in
* the UMSDOS filesystem. First, all functions use the prefix
* umsdos_ to avoid name clashes with other parts of the kernel.
*
* Standard VFS entry points use the prefix UMSDOS (upper case)
* so it's easier to tell them apart.
* N.B. (FIXME) PTW, the order and contents of this struct changed.
*/
/* #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.
*/
/* #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.
*/
#ifndef LINUX_UMSDOS_FS_H
#define LINUX_UMSDOS_FS_H
/*#define UMS_DEBUG 1 // define for check_* functions */
/*#define UMSDOS_DEBUG 1*/
#define UMSDOS_PARANOIA 1
#define UMSDOS_VERSION 0
#define UMSDOS_RELEASE 4
#define UMSDOS_ROOT_INO 1
/* This is the file acting as a directory extension */
#define UMSDOS_EMD_FILE "--linux-.---"
#define UMSDOS_EMD_NAMELEN 12
#define UMSDOS_PSDROOT_NAME "linux"
#define UMSDOS_PSDROOT_LEN 5
#ifndef _LINUX_TYPES_H
#include <linux/types.h>
#endif
#ifndef _LINUX_LIMITS_H
#include <linux/limits.h>
#endif
#ifndef _LINUX_DIRENT_H
#include <linux/dirent.h>
#endif
#ifndef _LINUX_IOCTL_H
#include <linux/ioctl.h>
#endif
#ifdef __KERNEL__
/* #Specification: convention / PRINTK Printk and printk
* Here is the convention for the use of printk inside fs/umsdos
*
* printk carry important message (error or status).
* Printk is for debugging (it is a macro defined at the beginning of
* most source.
* PRINTK is a nulled Printk macro.
*
* This convention makes the source easier to read, and Printk easier
* to shut off.
*/
# define PRINTK(x)
# ifdef UMSDOS_DEBUG
# define Printk(x) printk x
# else
# define Printk(x)
# endif
#endif /* __KERNEL__ */
struct umsdos_fake_info {
char fname[13];
int len;
};
#define UMSDOS_MAXNAME 220
/* This structure is 256 bytes large, depending on the name, only part */
/* of it is written to disk */
/* nice though it would be, I can't change this and preserve backward compatibility */
struct umsdos_dirent {
unsigned char name_len; /* if == 0, then this entry is not used */
unsigned char flags; /* UMSDOS_xxxx */
unsigned short nlink; /* How many hard links point to this entry */
__kernel_uid_t uid; /* Owner user id */
__kernel_gid_t gid; /* Group id */
time_t atime; /* Access time */
time_t mtime; /* Last modification time */
time_t ctime; /* Creation time */
unsigned short rdev; /* major and minor of a device special file */
umode_t mode; /* Standard UNIX permissions bits + type of */
char spare[12]; /* unused bytes for future extensions */
/* file, see linux/stat.h */
char name[UMSDOS_MAXNAME]; /* Not '\0' terminated */
/* but '\0' padded, so it will allow */
/* for adding news fields in this record */
/* by reducing the size of name[] */
};
#define UMSDOS_HIDDEN 1 /* Never show this entry in directory search */
#define UMSDOS_HLINK 2 /* It is a (pseudo) hard link */
/* #Specification: EMD file / record size
* Entry are 64 bytes wide in the EMD file. It allows for a 30 characters
* name. If a name is longer, contiguous entries are allocated. So a
* umsdos_dirent may span multiple records.
*/
#define UMSDOS_REC_SIZE 64
/* Translation between MSDOS name and UMSDOS name */
struct umsdos_info {
int msdos_reject; /* Tell if the file name is invalid for MSDOS */
/* See umsdos_parse */
struct umsdos_fake_info fake;
struct umsdos_dirent entry;
off_t f_pos; /* offset of the entry in the EMD file
* or offset where the entry may be store
* if it is a new entry
*/
int recsize; /* Record size needed to store entry */
};
/* Definitions for ioctl (number randomly chosen)
* The next ioctl commands operate only on the DOS directory
* The file umsdos_progs/umsdosio.c contain a string table
* based on the order of those definition. Keep it in sync
*/
#define UMSDOS_READDIR_DOS _IO(0x04,210) /* Do a readdir of the DOS directory */
#define UMSDOS_UNLINK_DOS _IO(0x04,211) /* Erase in the DOS directory only */
#define UMSDOS_RMDIR_DOS _IO(0x04,212) /* rmdir in the DOS directory only */
#define UMSDOS_STAT_DOS _IO(0x04,213) /* Get info about a file */
/* The next ioctl commands operate only on the EMD file */
#define UMSDOS_CREAT_EMD _IO(0x04,214) /* Create a file */
#define UMSDOS_UNLINK_EMD _IO(0x04,215) /* unlink (rmdir) a file */
#define UMSDOS_READDIR_EMD _IO(0x04,216) /* read the EMD file only. */
#define UMSDOS_GETVERSION _IO(0x04,217) /* Get the release number of UMSDOS */
#define UMSDOS_INIT_EMD _IO(0x04,218) /* Create the EMD file if not there */
#define UMSDOS_DOS_SETUP _IO(0x04,219) /* Set the defaults of the MS-DOS driver. */
#define UMSDOS_RENAME_DOS _IO(0x04,220) /* rename a file/directory in the DOS
* directory only */
struct umsdos_ioctl {
struct dirent dos_dirent;
struct umsdos_dirent umsdos_dirent;
/* The following structure is used to exchange some data with
* utilities (umsdos_progs/util/umsdosio.c). The first releases
* were using struct stat from "sys/stat.h". This was causing
* some problem for cross compilation of the kernel.
* Since I am not really using the structure stat, but only
* some fields of it, I have decided to replicate the structure
* here for compatibility with the binaries out there.
* FIXME PTW 1998, this has probably changed
*/
struct {
unsigned long st_dev;
ino_t st_ino; /* used */
umode_t st_mode; /* used */
nlink_t st_nlink;
__kernel_uid_t st_uid;
__kernel_gid_t st_gid;
unsigned long st_rdev;
off_t st_size; /* used */
unsigned long st_blksize;
unsigned long st_blocks;
time_t st_atime; /* used */
unsigned long __unused1;
time_t st_mtime; /* used */
unsigned long __unused2;
time_t st_ctime; /* used */
unsigned long __unused3;
uid_t st_uid32;
gid_t st_gid32;
} stat;
char version, release;
};
/* Different macros to access struct umsdos_dirent */
#define EDM_ENTRY_ISUSED(e) ((e)->name_len!=0)
#ifdef __KERNEL__
#ifndef LINUX_FS_H
#include <linux/fs.h>
#endif
extern struct inode_operations umsdos_dir_inode_operations;
extern struct inode_operations umsdos_rdir_inode_operations;
extern struct file_operations umsdos_dir_operations;
extern struct file_operations umsdos_rdir_operations;
#include <linux/umsdos_fs.p>
#endif /* __KERNEL__ */
#endif
/* check.c 23/01/95 03.38.30 */
void check_page_tables (void);
/* dir.c 22/06/95 00.22.12 */
int dummy_dir_read ( struct file *filp,
char *buf,
size_t size,
loff_t *count);
char * umsdos_d_path(struct dentry *, char *, int);
void umsdos_lookup_patch_new(struct dentry *, struct umsdos_info *);
int umsdos_is_pseudodos (struct inode *dir, struct dentry *dentry);
struct dentry *umsdos_lookup_x ( struct inode *dir, struct dentry *dentry, int nopseudo);
struct dentry *UMSDOS_lookup(struct inode *, struct dentry *, struct nameidata *);
struct dentry *umsdos_lookup_dentry(struct dentry *, char *, int, int);
struct dentry *umsdos_covered(struct dentry *, char *, int);
struct dentry *umsdos_solve_hlink (struct dentry *hlink);
/* emd.c 22/06/95 00.22.04 */
struct dentry *umsdos_get_emd_dentry(struct dentry *);
int umsdos_have_emd(struct dentry *);
int umsdos_make_emd(struct dentry *);
int umsdos_emd_dir_readentry (struct dentry *, loff_t *, struct umsdos_dirent *);
int umsdos_newentry (struct dentry *, struct umsdos_info *);
int umsdos_newhidden (struct dentry *, struct umsdos_info *);
int umsdos_delentry (struct dentry *, struct umsdos_info *, int);
int umsdos_findentry (struct dentry *, struct umsdos_info *, int);
int umsdos_isempty (struct dentry *);
int umsdos_writeentry (struct dentry *, struct umsdos_info *, int);
/* file.c 25/01/95 02.25.38 */
/* inode.c 12/06/95 09.49.40 */
void fill_new_filp (struct file *filp, struct dentry *dentry);
void UMSDOS_read_inode (struct inode *);
void UMSDOS_write_inode (struct inode *, int);
int UMSDOS_notify_change (struct dentry *, struct iattr *attr);
int umsdos_notify_change_locked(struct dentry *, struct iattr *attr);
void UMSDOS_put_inode (struct inode *);
int UMSDOS_statfs (struct super_block *, struct statfs *);
struct super_block *UMSDOS_read_super (struct super_block *, void *, int);
void UMSDOS_put_super (struct super_block *);
void umsdos_setup_dir(struct dentry *);
void umsdos_set_dirinfo_new(struct dentry *, off_t);
void umsdos_patch_dentry_inode (struct dentry *, off_t);
int umsdos_get_dirowner (struct inode *inode, struct inode **result);
/* ioctl.c 22/06/95 00.22.08 */
int UMSDOS_ioctl_dir (struct inode *dir,
struct file *filp,
unsigned int cmd,
unsigned long data);
/* mangle.c 25/01/95 02.25.38 */
void umsdos_manglename (struct umsdos_info *info);
int umsdos_evalrecsize (int len);
int umsdos_parse (const char *name,int len, struct umsdos_info *info);
/* namei.c 25/01/95 02.25.38 */
void umsdos_lockcreate (struct inode *dir);
void umsdos_startlookup (struct inode *dir);
void umsdos_unlockcreate (struct inode *dir);
void umsdos_endlookup (struct inode *dir);
int umsdos_readlink_x ( struct dentry *dentry,
char *buffer,
int bufsiz);
int UMSDOS_symlink (struct inode *dir,
struct dentry *dentry,
const char *symname);
int UMSDOS_link (struct dentry *olddentry,
struct inode *dir,
struct dentry *dentry);
int UMSDOS_create (struct inode *dir,
struct dentry *dentry,
int mode);
int UMSDOS_mkdir (struct inode *dir,
struct dentry *dentry,
int mode);
int UMSDOS_mknod (struct inode *dir,
struct dentry *dentry,
int mode,
dev_t rdev);
int UMSDOS_rmdir (struct inode *dir,struct dentry *dentry);
int UMSDOS_unlink (struct inode *dir, struct dentry *dentry);
int UMSDOS_rename (struct inode *old_dir,
struct dentry *old_dentry,
struct inode *new_dir,
struct dentry *new_dentry);
/* rdir.c 22/03/95 03.31.42 */
struct dentry *umsdos_rlookup_x (struct inode *dir, struct dentry *dentry, int nopseudo);
struct dentry *UMSDOS_rlookup (struct inode *dir, struct dentry *dentry, struct nameidata *nd);
static inline struct umsdos_inode_info *UMSDOS_I(struct inode *inode)
{
return &inode->u.umsdos_i;
}
#ifndef UMSDOS_FS_I_H
#define UMSDOS_FS_I_H
#ifndef _LINUX_TYPES_H
#include <linux/types.h>
#endif
#include <linux/msdos_fs_i.h>
#include <linux/pipe_fs_i.h>
/* #Specification: strategy / in memory inode
* Here is the information specific to the inode of the UMSDOS file
* system. This information is added to the end of the standard struct
* inode. Each file system has its own extension to struct inode,
* so do the umsdos file system.
*
* The strategy is to have the umsdos_inode_info as a superset of
* the msdos_inode_info, since most of the time the job is done
* by the msdos fs code.
*
* So we duplicate the msdos_inode_info, and add our own info at the
* end.
*
* The offset in this EMD file of the entry: pos
*
* For directory, we have dir_locking_info to help synchronise
* file creation and file lookup. See also msdos_fs_i.h for more
* information about msdos_inode_info.
*
* Special file and fifo do have an inode which correspond to an
* empty MSDOS file.
*
* symlink are processed mostly like regular file. The content is the
* link.
*
* The UMSDOS specific extension is placed after the union.
*/
struct dir_locking_info {
wait_queue_head_t p;
short int looking; /* How many process doing a lookup */
short int creating; /* Is there any creation going on here
* Only one at a time, although one
* may recursively lock, so it is a counter
*/
long pid; /* pid of the process owning the creation
* lock */
};
struct umsdos_inode_info {
struct msdos_inode_info msdos_info;
struct dir_locking_info dir_info;
int i_patched; /* Inode has been patched */
int i_is_hlink; /* Resolved hardlink inode? */
off_t pos; /* Entry offset in the emd_owner file */
};
#endif
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment