OpenCores
URL https://opencores.org/ocsvn/or1k_old/or1k_old/trunk

Subversion Repositories or1k_old

Compare Revisions

  • This comparison shows the changes necessary to convert path
    /or1k_old/trunk/rc203soc/sw/uClinux/fs/umsdos
    from Rev 1765 to Rev 1782
    Reverse comparison

Rev 1765 → Rev 1782

/dir.c
0,0 → 1,830
/*
* 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/sched.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/malloc.h>
 
#include <asm/segment.h>
 
#define PRINTK(x)
#define Printk(x) printk x
 
#define UMSDOS_SPECIAL_DIRFPOS 3
extern struct inode *pseudo_root;
/*
So grep * doesn't complain in the presence of directories.
*/
int UMSDOS_dir_read(struct inode *inode,struct file *filp,char *buf,
int count)
{
return -EISDIR;
}
 
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.
*/
static int umsdos_dir_once(
void * buf,
const char * name,
int name_len,
off_t offset,
ino_t ino)
{
int ret = -EINVAL;
struct UMSDOS_DIR_ONCE *d = (struct UMSDOS_DIR_ONCE *)buf;
if (d->count == 0){
#if 0
char zname[100];
memcpy (zname,name,name_len);
zname[name_len] = '\0';
Printk (("dir_once :%s: offset %Ld\n",zname,offset));
#endif
ret = d->filldir (d->dirbuf,name,name_len,offset,ino);
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 amount of byte written by filldir).
 
This function is used by the normal readdir VFS entry point and by
some function who try to find out info on a file from a pure MSDOS
inode. See umsdos_locate_ancestor() below.
*/
static int umsdos_readdir_x(
struct inode *dir, /* Point to a description of the super block */
struct file *filp, /* Point to a directory which is read */
void *dirbuf, /* Will hold count directory entry */
/* but filled by the filldir function */
int internal_read, /* Called for internal purpose */
struct umsdos_dirent *u_entry, /* Optional umsdos entry */
int follow_hlink,
filldir_t filldir)
{
int ret = 0;
umsdos_startlookup(dir);
if (filp->f_pos == UMSDOS_SPECIAL_DIRFPOS
&& dir == pseudo_root
&& !internal_read){
/*
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.
*/
if (filldir (dirbuf,"DOS",3,UMSDOS_SPECIAL_DIRFPOS
,dir->i_sb->s_mounted->i_ino) == 0){
filp->f_pos++;
}
}else if (filp->f_pos < 2
|| (dir != dir->i_sb->s_mounted && filp->f_pos == 32)){
/* #Specification: readdir / . and ..
The msdos filesystem manage 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.
*/
int last_f_pos = filp->f_pos;
struct UMSDOS_DIR_ONCE bufk;
bufk.dirbuf = dirbuf;
bufk.filldir = filldir;
bufk.count = 0;
ret = fat_readdir(dir,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;
}else{
struct inode *emd_dir = umsdos_emd_dir_lookup(dir,0);
if (emd_dir != NULL){
off_t start_fpos = filp->f_pos;
if (filp->f_pos <= UMSDOS_SPECIAL_DIRFPOS+1) filp->f_pos = 0;
PRINTK (("f_pos %lu i_size %ld\n",filp->f_pos,emd_dir->i_size));
ret = 0;
while (filp->f_pos < emd_dir->i_size){
struct umsdos_dirent entry;
off_t cur_f_pos = filp->f_pos;
if (umsdos_emd_dir_readentry (emd_dir,filp,&entry)!=0){
ret = -EIO;
break;
}else if (entry.name_len != 0){
/* #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.
 
Suggestion welcome.
 
So the easy way is used!
*/
struct umsdos_info info;
struct inode *inode;
int lret;
umsdos_parse (entry.name,entry.name_len,&info);
info.f_pos = cur_f_pos;
umsdos_manglename (&info);
lret = umsdos_real_lookup (dir,info.fake.fname
,info.fake.len,&inode);
PRINTK (("Cherche inode de %s lret %d flags %d\n"
,info.fake.fname,lret,entry.flags));
if (lret == 0
&& (entry.flags & UMSDOS_HLINK)
&& follow_hlink){
struct inode *rinode;
lret = umsdos_hlink2inode (inode,&rinode);
inode = rinode;
}
if (lret == 0){
/* #Specification: pseudo root / reading real root
The pseudo root (/linux) is logically
erased from the real root. This mean that
ls /DOS, won't show "linux". This avoids
infinite recursion /DOS/linux/DOS/linux while
walking the file system.
*/
if (inode != pseudo_root
&& (internal_read
|| !(entry.flags & UMSDOS_HIDDEN))){
if (filldir (dirbuf
,entry.name,entry.name_len
,cur_f_pos, inode->i_ino) < 0){
filp->f_pos = cur_f_pos;
}
PRINTK (("Trouve ino %ld ",inode->i_ino));
if (u_entry != NULL) *u_entry = entry;
iput (inode);
break;
}
iput (inode);
}else{
/* #Specification: umsdos / readdir / not in MSDOS
During a readdir operation, if the file is not
in the MSDOS directory anymore, the entry is
removed from the EMD file silently.
*/
ret = umsdos_writeentry (dir,emd_dir,&info,1);
if (ret != 0){
break;
}
}
}
}
/*
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.
*/
if (filp->f_pos == 0) filp->f_pos = start_fpos;
iput(emd_dir);
}
}
umsdos_endlookup(dir);
PRINTK (("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 inode *dir, /* Point to a description of the super block */
struct file *filp, /* Point to a directory which is read */
void *dirbuf, /* Will hold directory entries */
filldir_t filldir)
{
int ret = 0;
int count = 0;
struct UMSDOS_DIR_ONCE bufk;
bufk.dirbuf = dirbuf;
bufk.filldir = filldir;
bufk.stop = 0;
PRINTK (("UMSDOS_readdir in\n"));
while (ret == 0 && bufk.stop == 0){
struct umsdos_dirent entry;
bufk.count = 0;
ret = umsdos_readdir_x (dir,filp,&bufk,0,&entry,1,umsdos_dir_once);
if (bufk.count == 0) break;
count += bufk.count;
}
PRINTK (("UMSDOS_readdir out %d count %d pos %Ld\n",ret,count
,filp->f_pos));
return count?:ret;
}
/*
Complete the inode content with info from the EMD file
*/
void umsdos_lookup_patch (
struct inode *dir,
struct inode *inode,
struct umsdos_dirent *entry,
off_t emd_pos)
{
/*
This function modify the state of a dir inode. It decides
if the dir is a umsdos dir or a dos dir. This is done
deeper in umsdos_patch_inode() called at the end of this function.
 
umsdos_patch_inode() may block because it is doing disk access.
At the same time, another process may get here to initialise
the same dir inode. There is 3 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 lock the inode, so we try
to lock it and right after check if 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 mem to 4 meg 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.
*/
if (inode->i_sb == dir->i_sb && !umsdos_isinit(inode)){
if (S_ISDIR(inode->i_mode)) umsdos_lockcreate(inode);
if (!umsdos_isinit(inode)){
/* #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 MSDOS for mtime for directory because
the MSDOS directory date is creation time (strange
MSDOS behavior) which fit nowhere in the three UNIX
time stamp.
*/
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 / conversion mode
The msdos fs can do some inline conversion
of the data of a file. It can translate
silently from MsDOS text file format to 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 file in 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 fs).
This flag could be setup on a directory basis
(instead of file) and all file in it would
logically inherited. 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 was used
to set this. The problem is that new file 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 utilities
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.
*/
 
MSDOS_I(inode)->i_binary = 1;
/* #Specification: umsdos / i_nlink
The nlink field of an inode is maintain by the MSDOS file system
for directory and by UMSDOS for other file. 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 link
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 ("UMSDOS: lookup_patch entry->nlink < 1 ???\n");
}
}
umsdos_patch_inode(inode,dir,emd_pos);
}
if (S_ISDIR(inode->i_mode)) umsdos_unlockcreate(inode);
if (inode->u.umsdos_i.i_emd_owner==0) printk ("emd_owner still 0 ???\n");
}
}
struct UMSDOS_DIRENT_K{
off_t f_pos; /* will hold the offset of the entry in EMD */
ino_t ino;
};
 
/*
Just to record the offset of one entry.
*/
static int umsdos_filldir_k(
void * buf,
const char * name,
int name_len,
off_t offset,
ino_t ino)
{
struct UMSDOS_DIRENT_K *d = (struct UMSDOS_DIRENT_K *)buf;
d->f_pos = offset;
d->ino = ino;
return 0;
}
 
struct UMSDOS_DIR_SEARCH{
struct umsdos_dirent *entry;
int found;
ino_t search_ino;
};
 
static int umsdos_dir_search (
void * buf,
const char * name,
int name_len,
off_t offset,
ino_t ino)
{
int ret = 0;
struct UMSDOS_DIR_SEARCH *d = (struct UMSDOS_DIR_SEARCH *)buf;
if (d->search_ino == ino){
d->found = 1;
memcpy (d->entry->name,name,name_len);
d->entry->name[name_len] = '\0';
d->entry->name_len = name_len;
ret = 1; /* So fat_readdir will terminate */
}
return ret;
}
 
 
/*
Locate entry of an inode in a directory.
Return 0 or a negative error code.
 
Normally, this function must succeed. It means a strange corruption
in the file system if not.
*/
int umsdos_inode2entry (
struct inode *dir,
struct inode *inode,
struct umsdos_dirent *entry) /* Will hold the entry */
{
int ret = -ENOENT;
if (inode == pseudo_root){
/*
Quick way to find the name.
Also umsdos_readdir_x won't show /linux anyway
*/
memcpy (entry->name,UMSDOS_PSDROOT_NAME,UMSDOS_PSDROOT_LEN+1);
entry->name_len = UMSDOS_PSDROOT_LEN;
ret = 0;
}else{
struct inode *emddir = umsdos_emd_dir_lookup(dir,0);
iput (emddir);
if (emddir == NULL){
/* This is a DOS directory */
struct UMSDOS_DIR_SEARCH bufk;
struct file filp;
filp.f_reada = 1;
filp.f_pos = 0;
bufk.entry = entry;
bufk.search_ino = inode->i_ino;
fat_readdir (dir,&filp,&bufk,umsdos_dir_search);
if (bufk.found){
ret = 0;
inode->u.umsdos_i.i_dir_owner = dir->i_ino;
inode->u.umsdos_i.i_emd_owner = 0;
umsdos_setup_dir_inode(inode);
}
}else{
/* skip . and .. see umsdos_readdir_x() */
struct file filp;
filp.f_reada = 1;
filp.f_pos = UMSDOS_SPECIAL_DIRFPOS;
while (1){
struct UMSDOS_DIRENT_K bufk;
if (umsdos_readdir_x(dir,&filp,&bufk
,1,entry,0,umsdos_filldir_k) < 0){
printk ("UMSDOS: can't locate inode %ld in EMD file???\n"
,inode->i_ino);
break;
}else if (bufk.ino == inode->i_ino){
ret = 0;
umsdos_lookup_patch (dir,inode,entry,bufk.f_pos);
break;
}
}
}
}
return ret;
}
/*
Locate the parent of a directory and the info on that directory
Return 0 or a negative error code.
*/
static int umsdos_locate_ancestor (
struct inode *dir,
struct inode **result,
struct umsdos_dirent *entry)
{
int ret;
umsdos_patch_inode (dir,NULL,0);
ret = umsdos_real_lookup (dir,"..",2,result);
PRINTK (("result %d %p ",ret,*result));
if (ret == 0){
struct inode *adir = *result;
ret = umsdos_inode2entry (adir,dir,entry);
}
PRINTK (("\n"));
return ret;
}
/*
Build the path name of an inode (relative to the file system.
This function is need to set (pseudo) hard link.
 
It uses the same strategy as the standard getcwd().
*/
int umsdos_locate_path (
struct inode *inode,
char *path)
{
int ret = 0;
struct inode *dir = inode;
char *bpath = (char*)kmalloc(PATH_MAX,GFP_KERNEL);
if (bpath == NULL){
ret = -ENOMEM;
}else{
struct umsdos_dirent entry;
char *ptbpath = bpath+PATH_MAX-1;
*ptbpath = '\0';
PRINTK (("locate_path mode %x ",inode->i_mode));
if (!S_ISDIR(inode->i_mode)){
ret = umsdos_get_dirowner (inode,&dir);
PRINTK (("locate_path ret %d ",ret));
if (ret == 0){
ret = umsdos_inode2entry (dir,inode,&entry);
if (ret == 0){
ptbpath -= entry.name_len;
memcpy (ptbpath,entry.name,entry.name_len);
PRINTK (("ptbpath :%s: ",ptbpath));
}
}
}else{
dir->i_count++;
}
if (ret == 0){
while (dir != dir->i_sb->s_mounted){
struct inode *adir;
ret = umsdos_locate_ancestor (dir,&adir,&entry);
iput (dir);
dir = NULL;
PRINTK (("ancestor %d ",ret));
if (ret == 0){
*--ptbpath = '/';
ptbpath -= entry.name_len;
memcpy (ptbpath,entry.name,entry.name_len);
dir = adir;
PRINTK (("ptbpath :%s: ",ptbpath));
}else{
break;
}
}
}
strcpy (path,ptbpath);
kfree (bpath);
}
PRINTK (("\n"));
iput (dir);
return ret;
}
 
/*
Return != 0 if an entry is the pseudo DOS entry in the pseudo root.
*/
int umsdos_is_pseudodos (
struct inode *dir,
const char *name,
int len)
{
/* #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
&& len == 3
&& name[0] == 'D' && name[1] == 'O' && name[2] == 'S';
}
/*
Check if a file exist in the current directory.
Return 0 if ok, negative error code if not (ex: -ENOENT).
*/
static int umsdos_lookup_x (
struct inode *dir,
const char *name,
int len,
struct inode **result, /* Will hold inode of the file, if successful */
int nopseudo) /* Don't care about pseudo root mode */
{
int ret = -ENOENT;
*result = NULL;
umsdos_startlookup(dir);
if (len == 1 && name[0] == '.'){
*result = dir;
dir->i_count++;
ret = 0;
}else if (len == 2 && name[0] == '.' && name[1] == '.'){
if (pseudo_root != NULL && dir == pseudo_root->i_sb->s_mounted){
/* #Specification: pseudo root / .. in real root
Whenever a lookup is those in the real root for
the directory .., and pseudo root is active, the
pseudo root is returned.
*/
ret = 0;
*result = pseudo_root;
pseudo_root->i_count++;
}else{
/* #Specification: locating .. / strategy
We use the msdos filesystem to locate the parent directory.
But it is more complicated than that.
We have to step back even further to
get the parent of the parent, so we can get the EMD
of the parent of the parent. Using the EMD file, we can
locate all the info on the parent, such a permissions
and owner.
*/
ret = umsdos_real_lookup (dir,"..",2,result);
PRINTK (("ancestor ret %d dir %p *result %p ",ret,dir,*result));
if (ret == 0
&& *result != dir->i_sb->s_mounted
&& *result != pseudo_root){
struct inode *aadir;
struct umsdos_dirent entry;
ret = umsdos_locate_ancestor (*result,&aadir,&entry);
iput (aadir);
}
}
}else if (umsdos_is_pseudodos(dir,name,len)){
/* #Specification: pseudo root / lookup(DOS)
A lookup of DOS in the pseudo root will always succeed
and return the inode of the real root.
*/
*result = dir->i_sb->s_mounted;
(*result)->i_count++;
ret = 0;
}else{
struct umsdos_info info;
ret = umsdos_parse (name,len,&info);
if (ret == 0) ret = umsdos_findentry (dir,&info,0);
PRINTK (("lookup %s pos %lu ret %d len %d ",info.fake.fname,info.f_pos,ret
,info.fake.len));
if (ret == 0){
/* #Specification: umsdos / lookup
A lookup for a file is done in two step. 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 inode *inode;
ret = umsdos_real_lookup (dir,info.fake.fname,info.fake.len,result);
inode = *result;
if (inode == NULL){
printk ("UMSDOS: Erase entry %s, out of sync with MsDOS\n"
,info.fake.fname);
umsdos_delentry (dir,&info,S_ISDIR(info.entry.mode));
}else{
umsdos_lookup_patch (dir,inode,&info.entry,info.f_pos);
PRINTK (("lookup ino %ld flags %d\n",inode->i_ino
,info.entry.flags));
if (info.entry.flags & UMSDOS_HLINK){
ret = umsdos_hlink2inode (inode,result);
}
if (*result == 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 link
which are recorded independently of the pseudo-root
mode.
*/
iput (pseudo_root);
*result = NULL;
ret = -ENOENT;
}
}
}
}
umsdos_endlookup(dir);
iput (dir);
return ret;
}
/*
Check if a file exist in the current directory.
Return 0 if ok, negative error code if not (ex: -ENOENT).
*/
int UMSDOS_lookup (
struct inode *dir,
const char *name,
int len,
struct inode **result) /* Will hold inode of the file, if successful */
{
return umsdos_lookup_x(dir,name,len,result,0);
}
/*
Locate the inode pointed by a (pseudo) hard link
Return 0 if ok, a negative error code if not.
*/
int umsdos_hlink2inode (struct inode *hlink, struct inode **result)
{
int ret = -EIO;
char *path = (char*)kmalloc(PATH_MAX,GFP_KERNEL);
*result = NULL;
if (path == NULL){
ret = -ENOMEM;
iput (hlink);
}else{
struct file filp;
filp.f_reada = 1;
filp.f_pos = 0;
PRINTK (("hlink2inode "));
if (umsdos_file_read_kmem (hlink,&filp,path,hlink->i_size)
==hlink->i_size){
struct inode *dir;
char *pt = path;
dir = hlink->i_sb->s_mounted;
path[hlink->i_size] = '\0';
iput (hlink);
dir->i_count++;
while (1){
char *start = pt;
int len;
while (*pt != '\0' && *pt != '/') pt++;
len = (int)(pt - start);
if (*pt == '/') *pt++ = '\0';
if (dir->u.umsdos_i.i_emd_dir == 0){
/* This is a DOS directory */
ret = umsdos_rlookup_x(dir,start,len,result,1);
}else{
ret = umsdos_lookup_x(dir,start,len,result,1);
}
PRINTK (("h2n lookup :%s: -> %d ",start,ret));
if (ret == 0 && *pt != '\0'){
dir = *result;
}else{
break;
}
}
}else{
iput (hlink);
}
PRINTK (("hlink2inode ret = %d %p -> %p\n",ret,hlink,*result));
kfree (path);
}
return ret;
}
 
static struct file_operations umsdos_dir_operations = {
NULL, /* lseek - default */
UMSDOS_dir_read, /* read */
NULL, /* write - bad */
UMSDOS_readdir, /* readdir */
NULL, /* select - default */
UMSDOS_ioctl_dir, /* ioctl - default */
NULL, /* mmap */
NULL, /* no special open code */
NULL, /* no special release code */
NULL /* fsync */
};
 
struct inode_operations umsdos_dir_inode_operations = {
&umsdos_dir_operations, /* default directory file-ops */
UMSDOS_create, /* create */
UMSDOS_lookup, /* lookup */
UMSDOS_link, /* link */
UMSDOS_unlink, /* unlink */
UMSDOS_symlink, /* symlink */
UMSDOS_mkdir, /* mkdir */
UMSDOS_rmdir, /* rmdir */
UMSDOS_mknod, /* mknod */
UMSDOS_rename, /* rename */
NULL, /* readlink */
NULL, /* follow_link */
NULL, /* readpage */
NULL, /* writepage */
NULL, /* bmap */
NULL, /* truncate */
NULL /* permission */
};
 
 
 
 
 
 
 
 
 
 
/inode.c
0,0 → 1,523
/*
* linux/fs/umsdos/inode.c
*
* Written 1993 by Jacques Gelinas
* Inspired from linux/fs/msdos/... by Werner Almesberger
*
*/
 
#include <linux/module.h>
 
#include <linux/fs.h>
#include <linux/msdos_fs.h>
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/errno.h>
#include <asm/segment.h>
#include <linux/string.h>
#include <linux/stat.h>
#include <linux/umsdos_fs.h>
 
struct inode *pseudo_root=NULL; /* Useful to simulate the pseudo DOS */
/* directory. See UMSDOS_readdir_x() */
 
/* #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)
#define Printk(x) printk x
 
 
void UMSDOS_put_inode(struct inode *inode)
{
PRINTK (("put inode %x owner %x pos %d dir %x\n",inode
,inode->u.umsdos_i.i_emd_owner,inode->u.umsdos_i.pos
,inode->u.umsdos_i.i_emd_dir));
if (inode != NULL && inode == pseudo_root){
printk ("Umsdos: Oops releasing pseudo_root. Notify jacques@solucorp.qc.ca\n");
}
fat_put_inode(inode);
}
 
 
void UMSDOS_put_super(struct super_block *sb)
{
msdos_put_super(sb);
MOD_DEC_USE_COUNT;
}
 
 
void UMSDOS_statfs(struct super_block *sb,struct statfs *buf, int bufsiz)
{
fat_statfs(sb,buf,bufsiz);
}
 
 
/*
Call msdos_lookup, but set back the original msdos function table.
Return 0 if ok, or a negative error code if not.
*/
int umsdos_real_lookup (
struct inode *dir,
const char *name,
int len,
struct inode **result) /* Will hold inode of the file, if successful */
{
int ret;
dir->i_count++;
ret = msdos_lookup (dir,name,len,result);
return ret;
}
/*
Complete the setup of an directory inode.
First, it completes the function pointers, then
it locates the EMD file. If the EMD is there, then plug the
umsdos function table. If not, use the msdos one.
*/
void umsdos_setup_dir_inode (struct inode *inode)
{
inode->u.umsdos_i.i_emd_dir = 0;
{
struct inode *emd_dir = umsdos_emd_dir_lookup (inode,0);
extern struct inode_operations umsdos_rdir_inode_operations;
inode->i_op = emd_dir != NULL
? &umsdos_dir_inode_operations
: &umsdos_rdir_inode_operations;
iput (emd_dir);
}
}
/*
Add some info into an inode so it can find its owner quickly
*/
void umsdos_set_dirinfo(
struct inode *inode,
struct inode *dir,
off_t f_pos)
{
struct inode *emd_owner = umsdos_emd_dir_lookup(dir,1);
inode->u.umsdos_i.i_dir_owner = dir->i_ino;
inode->u.umsdos_i.i_emd_owner = emd_owner->i_ino;
iput (emd_owner);
inode->u.umsdos_i.pos = f_pos;
}
/*
Tells if an Umsdos inode has been "patched" once.
Return != 0 if so.
*/
int umsdos_isinit (struct inode *inode)
{
#if 1
return inode->u.umsdos_i.i_emd_owner != 0;
#elif 0
return inode->i_atime != 0;
#else
return inode->i_count > 1;
#endif
}
/*
Connect the proper tables in the inode and add some info.
*/
void umsdos_patch_inode (
struct inode *inode,
struct inode *dir, /* May be NULL */
off_t f_pos)
{
/*
This function is called very early to setup the inode, somewhat
too early (called by UMSDOS_read_inode). At this point, we can't
do to much, such as lookup up EMD files and so on. This causes
confusion in the kernel. This is why some initialisation
will be done when dir != NULL only.
 
UMSDOS do run piggy back on top of msdos fs. It looks like something
is missing in the VFS to accommodate stacked fs. Still unclear what
(quite honestly).
 
Well, maybe one! A new entry "may_unmount" which would allow
the stacked fs to allocate some inode permanently and release
them at the end. Doing that now introduce a problem. unmount
always fail because some inodes are in use.
*/
if (!umsdos_isinit(inode)){
inode->u.umsdos_i.i_emd_dir = 0;
if (S_ISREG(inode->i_mode)){
if (inode->i_op->bmap != NULL){
inode->i_op = &umsdos_file_inode_operations;
}else{
inode->i_op = &umsdos_file_inode_operations_no_bmap;
}
}else if (S_ISDIR(inode->i_mode)){
if (dir != NULL){
umsdos_setup_dir_inode(inode);
}
}else if (S_ISLNK(inode->i_mode)){
inode->i_op = &umsdos_symlink_inode_operations;
}else if (S_ISCHR(inode->i_mode)){
inode->i_op = &chrdev_inode_operations;
}else if (S_ISBLK(inode->i_mode)){
inode->i_op = &blkdev_inode_operations;
}else if (S_ISFIFO(inode->i_mode)){
init_fifo(inode);
}
if (dir != NULL){
/* #Specification: inode / umsdos info
The first time an inode is seen (inode->i_count == 1),
the inode number of the EMD file which control this inode
is tagged to this inode. It allows operation such
as notify_change to be handled.
*/
/*
This is done last because it also control the
status of umsdos_isinit()
*/
umsdos_set_dirinfo (inode,dir,f_pos);
}
}else if (dir != NULL){
/*
Test to see if the info is maintained.
This should be removed when the file system will be proven.
*/
struct inode *emd_owner = umsdos_emd_dir_lookup(dir,1);
iput (emd_owner);
if (emd_owner->i_ino != inode->u.umsdos_i.i_emd_owner){
printk ("UMSDOS: *** EMD_OWNER ??? *** ino = %ld %ld <> %ld "
,inode->i_ino,emd_owner->i_ino,inode->u.umsdos_i.i_emd_owner);
}
}
}
/*
Get the inode of the directory which owns this inode.
Return 0 if ok, -EIO if error.
*/
int umsdos_get_dirowner(
struct inode *inode,
struct inode **result) /* Hold NULL if any error */
/* else, the inode of the directory */
{
int ret = -EIO;
unsigned long ino = inode->u.umsdos_i.i_dir_owner;
*result = NULL;
if (ino == 0){
printk ("UMSDOS: umsdos_get_dirowner ino == 0\n");
}else{
struct inode *dir = *result = iget(inode->i_sb,ino);
if (dir != NULL){
umsdos_patch_inode (dir,NULL,0);
ret = 0;
}
}
return ret;
}
/*
Load an inode from disk.
*/
void UMSDOS_read_inode(struct inode *inode)
{
PRINTK (("read inode %x ino = %d ",inode,inode->i_ino));
msdos_read_inode(inode);
PRINTK (("ino = %d %ld\n",inode->i_ino,inode->i_count));
if (S_ISDIR(inode->i_mode)
&& (inode->u.umsdos_i.u.dir_info.creating != 0
|| inode->u.umsdos_i.u.dir_info.looking != 0
|| waitqueue_active(&inode->u.umsdos_i.u.dir_info.p))){
Printk (("read inode %d %d %p\n"
,inode->u.umsdos_i.u.dir_info.creating
,inode->u.umsdos_i.u.dir_info.looking
,inode->u.umsdos_i.u.dir_info.p));
}
/* #Specification: Inode / post initialisation
To completely initialise an inode, we need access to the owner
directory, so we can locate more info in the EMD file. This is
not available the first time the inode is access, we use
a value in the inode to tell if it has been finally initialised.
 
At first, we have tried testing i_count but it was causing
problem. It is possible that two or more process use the
newly accessed inode. While the first one block during
the initialisation (probably while reading the EMD file), the
others believe all is well because i_count > 1. They go banana
with a broken inode. See umsdos_lookup_patch and umsdos_patch_inode.
*/
umsdos_patch_inode(inode,NULL,0);
}
 
/*
Update the disk with the inode content
*/
void UMSDOS_write_inode(struct inode *inode)
{
struct iattr newattrs;
 
PRINTK (("UMSDOS_write_inode emd %d\n",inode->u.umsdos_i.i_emd_owner));
fat_write_inode(inode);
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_dirt = 0;
}
 
int UMSDOS_notify_change(struct inode *inode, struct iattr *attr)
{
int ret = 0;
 
if ((ret = inode_change_ok(inode, attr)) != 0)
return ret;
 
if (inode->i_nlink > 0){
/* #Specification: notify_change / i_nlink > 0
notify change is only done for inode with nlink > 0. An inode
with nlink == 0 is no longer associated with any entry in
the EMD file, so there is nothing to update.
*/
unsigned long i_emd_owner = inode->u.umsdos_i.i_emd_owner;
if (inode == inode->i_sb->s_mounted){
/* #Specification: root inode / attributes
I don't know yet how this should work. Normally
the attributes (permissions bits, owner, times) of
a directory are stored in the EMD file of its parent.
 
One thing we could do is store the attributes of the root
inode in its own EMD file. A simple entry named "." could
be used for this special case. It would be read once
when the file system is mounted and update in
UMSDOS_notify_change() (right here).
 
I am not sure of the behavior of the root inode for
a real UNIX file system. For now, this is a nop.
*/
}else if (i_emd_owner != 0xffffffff && i_emd_owner != 0){
/* This inode is not a EMD file nor an inode used internally
by MSDOS, so we can update its status.
See emd.c
*/
struct inode *emd_owner = iget (inode->i_sb,i_emd_owner);
PRINTK (("notify change %p ",inode));
if (emd_owner == NULL){
printk ("UMSDOS: emd_owner = NULL ???");
ret = -EPERM;
}else{
struct file filp;
struct umsdos_dirent entry;
filp.f_pos = inode->u.umsdos_i.pos;
filp.f_reada = 0;
PRINTK (("pos = %d ",filp.f_pos));
/* Read only the start of the entry since we don't touch */
/* the name */
ret = umsdos_emd_dir_read (emd_owner,&filp,(char*)&entry
,UMSDOS_REC_SIZE);
if (ret == 0){
if (attr->ia_valid & ATTR_UID)
entry.uid = attr->ia_uid;
if (attr->ia_valid & ATTR_GID)
entry.gid = attr->ia_gid;
if (attr->ia_valid & ATTR_MODE)
entry.mode = attr->ia_mode;
if (attr->ia_valid & ATTR_ATIME)
entry.atime = attr->ia_atime;
if (attr->ia_valid & ATTR_MTIME)
entry.mtime = attr->ia_mtime;
if (attr->ia_valid & ATTR_CTIME)
entry.ctime = attr->ia_ctime;
 
entry.nlink = inode->i_nlink;
filp.f_pos = inode->u.umsdos_i.pos;
ret = umsdos_emd_dir_write (emd_owner,&filp,(char*)&entry
,UMSDOS_REC_SIZE);
 
PRINTK (("notify pos %d ret %d nlink %d "
,inode->u.umsdos_i.pos
,ret,entry.nlink));
/* #Specification: notify_change / msdos fs
notify_change operation are done only on the
EMD file. The msdos fs is not even called.
*/
}
iput (emd_owner);
}
PRINTK (("\n"));
}
}
if (ret == 0)
inode_setattr(inode, attr);
return ret;
}
 
/* #Specification: function name / convention
A simple convention for function name has been used in
the UMSDOS file system. First all function use the prefix
umsdos_ to avoid name clash with other part of the kernel.
 
And standard VFS entry point use the prefix UMSDOS (upper case)
so it's easier to tell them apart.
*/
 
static struct super_operations umsdos_sops = {
UMSDOS_read_inode,
UMSDOS_notify_change,
UMSDOS_write_inode,
UMSDOS_put_inode,
UMSDOS_put_super,
NULL, /* added in 0.96c */
UMSDOS_statfs,
NULL
};
 
/*
Read the super block of an Extended MS-DOS FS.
*/
struct super_block *UMSDOS_read_super(
struct super_block *s,
void *data,
int silent)
{
/* #Specification: mount / options
Umsdos run on top of msdos. Currently, it supports no
mount option, but happily pass all option received to
the msdos driver. I am not sure if all msdos mount option
make sense with Umsdos. Here are at least those who
are useful.
uid=
gid=
 
These options affect the operation of umsdos in directories
which do not have an EMD file. They behave like normal
msdos directory, with all limitation of msdos.
*/
struct super_block *sb;
MOD_INC_USE_COUNT;
sb = msdos_read_super(s,data,silent);
printk ("UMSDOS Beta 0.6 (compatibility level %d.%d, fast msdos)\n"
,UMSDOS_VERSION,UMSDOS_RELEASE);
if (sb != NULL){
MSDOS_SB(sb)->options.dotsOK = 0; /* disable hidden==dotfile */
sb->s_op = &umsdos_sops;
PRINTK (("umsdos_read_super %p\n",sb->s_mounted));
umsdos_setup_dir_inode (sb->s_mounted);
PRINTK (("End umsdos_read_super\n"));
if (s == super_blocks){
/* #Specification: pseudo root / mount
When a umsdos fs is mounted, a special handling is done
if it is the root partition. We check for the presence
of the file /linux/etc/init or /linux/etc/rc or
/linux/sbin/init. If one is there, we do a chroot("/linux").
 
We check both because (see init/main.c) the kernel
try to exec init at different place and if it fails
it tries /bin/sh /etc/rc. To be consistent with
init/main.c, many more test would have to be done
to locate init. Any complain ?
 
The chroot is done manually in init/main.c but the
info (the inode) is located at mount time and store
in a global variable (pseudo_root) which is used at
different place in the umsdos driver. There is no
need to store this variable elsewhere because it
will always be one, not one per mount.
 
This feature allows the installation
of a linux system within a DOS system in a subdirectory.
A user may install its linux stuff in c:\linux
avoiding any clash with existing DOS file and subdirectory.
When linux boots, it hides this fact, showing a normal
root directory with /etc /bin /tmp ...
 
The word "linux" is hardcoded in /usr/include/linux/umsdos_fs.h
in the macro UMSDOS_PSDROOT_NAME.
*/
 
struct inode *pseudo;
Printk (("Mounting root\n"));
if (umsdos_real_lookup (sb->s_mounted,UMSDOS_PSDROOT_NAME
,UMSDOS_PSDROOT_LEN,&pseudo)==0
&& S_ISDIR(pseudo->i_mode)){
struct inode *etc = NULL;
struct inode *sbin = NULL;
int pseudo_ok = 0;
Printk (("/%s is there\n",UMSDOS_PSDROOT_NAME));
if (umsdos_real_lookup (pseudo,"etc",3,&etc)==0
&& S_ISDIR(etc->i_mode)){
struct inode *init = NULL;
struct inode *rc = NULL;
Printk (("/%s/etc is there\n",UMSDOS_PSDROOT_NAME));
if ((umsdos_real_lookup (etc,"init",4,&init)==0
&& S_ISREG(init->i_mode))
|| (umsdos_real_lookup (etc,"rc",2,&rc)==0
&& S_ISREG(rc->i_mode))){
pseudo_ok = 1;
}
iput (init);
iput (rc);
}
if (!pseudo_ok
&& umsdos_real_lookup (pseudo,"sbin",4,&sbin)==0
&& S_ISDIR(sbin->i_mode)){
struct inode *init = NULL;
Printk (("/%s/sbin is there\n",UMSDOS_PSDROOT_NAME));
if (umsdos_real_lookup (sbin,"init",4,&init)==0
&& S_ISREG(init->i_mode)){
pseudo_ok = 1;
}
iput (init);
}
if (pseudo_ok){
umsdos_setup_dir_inode (pseudo);
Printk (("Activating pseudo root /%s\n",UMSDOS_PSDROOT_NAME));
pseudo_root = pseudo;
pseudo->i_count++;
pseudo = NULL;
}
iput (sbin);
iput (etc);
}
iput (pseudo);
}
} else {
MOD_DEC_USE_COUNT;
}
return sb;
}
 
 
 
static struct file_system_type umsdos_fs_type = {
UMSDOS_read_super, "umsdos", 1, NULL
};
 
int init_umsdos_fs(void)
{
return register_filesystem(&umsdos_fs_type);
}
 
#ifdef MODULE
int init_module(void)
{
int status;
 
if ((status = init_umsdos_fs()) == 0)
register_symtab(0);
return status;
}
 
void cleanup_module(void)
{
unregister_filesystem(&umsdos_fs_type);
}
 
#endif
 
/file.c
0,0 → 1,131
/*
* linux/fs/umsdos/file.c
*
* Written 1993 by Jacques Gelinas
* inspired from linux/fs/msdos/file.c Werner Almesberger
*
* Extended MS-DOS regular file handling primitives
*/
 
#include <linux/sched.h>
#include <linux/fs.h>
#include <linux/msdos_fs.h>
#include <linux/errno.h>
#include <linux/fcntl.h>
#include <linux/stat.h>
#include <linux/msdos_fs.h>
#include <linux/umsdos_fs.h>
 
#include <asm/segment.h>
#include <asm/system.h>
 
#define PRINTK(x)
#define Printk(x) printk x
/*
Read a file into user space memory
*/
static int UMSDOS_file_read(
struct inode *inode,
struct file *filp,
char *buf,
int count)
{
/* We have to set the access time because msdos don't care */
int ret = fat_file_read(inode,filp,buf,count);
if (!IS_RDONLY(inode)){
inode->i_atime = CURRENT_TIME;
inode->i_dirt = 1;
}
return ret;
}
/*
Write a file from user space memory
*/
static int UMSDOS_file_write(
struct inode *inode,
struct file *filp,
const char *buf,
int count)
{
return fat_file_write(inode,filp,buf,count);
}
/*
Truncate a file to 0 length.
*/
static void UMSDOS_truncate(struct inode *inode)
{
PRINTK (("UMSDOS_truncate\n"));
fat_truncate (inode);
inode->i_ctime = inode->i_mtime = CURRENT_TIME;
inode->i_dirt = 1;
}
 
/* Function for normal file system (512 bytes hardware sector size) */
struct file_operations umsdos_file_operations = {
NULL, /* lseek - default */
UMSDOS_file_read, /* read */
UMSDOS_file_write, /* write */
NULL, /* readdir - bad */
NULL, /* select - default */
NULL, /* ioctl - default */
generic_file_mmap, /* mmap */
NULL, /* no special open is needed */
NULL, /* release */
file_fsync /* fsync */
};
 
struct inode_operations umsdos_file_inode_operations = {
&umsdos_file_operations, /* default file operations */
NULL, /* create */
NULL, /* lookup */
NULL, /* link */
NULL, /* unlink */
NULL, /* symlink */
NULL, /* mkdir */
NULL, /* rmdir */
NULL, /* mknod */
NULL, /* rename */
NULL, /* readlink */
NULL, /* follow_link */
generic_readpage, /* readpage */
NULL, /* writepage */
fat_bmap, /* bmap */
UMSDOS_truncate,/* truncate */
NULL, /* permission */
fat_smap /* smap */
};
/* For other with larger and unaligned file system */
struct file_operations umsdos_file_operations_no_bmap = {
NULL, /* lseek - default */
UMSDOS_file_read, /* read */
UMSDOS_file_write, /* write */
NULL, /* readdir - bad */
NULL, /* select - default */
NULL, /* ioctl - default */
fat_mmap, /* mmap */
NULL, /* no special open is needed */
NULL, /* release */
file_fsync /* fsync */
};
 
struct inode_operations umsdos_file_inode_operations_no_bmap = {
&umsdos_file_operations_no_bmap, /* default file operations */
NULL, /* create */
NULL, /* lookup */
NULL, /* link */
NULL, /* unlink */
NULL, /* symlink */
NULL, /* mkdir */
NULL, /* rmdir */
NULL, /* mknod */
NULL, /* rename */
NULL, /* readlink */
NULL, /* follow_link */
NULL, /* readpage */
NULL, /* writepage */
NULL, /* bmap */
UMSDOS_truncate,/* truncate */
NULL, /* permission */
NULL, /* smap */
};
 
/rdir.c
0,0 → 1,271
/*
* 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/sched.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/malloc.h>
 
#include <asm/segment.h>
 
#define PRINTK(x)
#define Printk(x) printk x
 
 
extern struct inode *pseudo_root;
 
struct RDIR_FILLDIR {
void *dirbuf;
filldir_t filldir;
int real_root;
};
 
static int rdir_filldir(
void * buf,
const char * name,
int name_len,
off_t offset,
ino_t ino)
{
int ret = 0;
struct RDIR_FILLDIR *d = (struct RDIR_FILLDIR*) buf;
if (d->real_root){
/* 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);
}
}else{
/* Any DOS directory */
ret = d->filldir (d->dirbuf,name,name_len,offset,ino);
}
return ret;
}
 
 
static int UMSDOS_rreaddir (
struct inode *dir,
struct file *filp,
void *dirbuf,
filldir_t filldir)
{
struct RDIR_FILLDIR bufk;
bufk.filldir = filldir;
bufk.dirbuf = dirbuf;
bufk.real_root = pseudo_root != NULL
&& dir == dir->i_sb->s_mounted
&& dir == pseudo_root->i_sb->s_mounted;
return fat_readdir(dir,filp,&bufk,rdir_filldir);
}
 
/*
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)).
*/
int umsdos_rlookup_x(
struct inode *dir,
const char *name,
int len,
struct inode **result, /* Will hold inode of the file, if successful */
int nopseudo) /* Don't care about pseudo root mode */
/* so locating "linux" will work */
{
int ret;
if (pseudo_root != NULL
&& len == 2
&& name[0] == '.'
&& name[1] == '.'
&& dir == dir->i_sb->s_mounted
&& dir == pseudo_root->i_sb->s_mounted){
*result = pseudo_root;
pseudo_root->i_count++;
ret = 0;
/* #Specification: pseudo root / DOS/..
In the real root directory (c:\), the directory ..
is the pseudo root (c:\linux).
*/
}else{
ret = umsdos_real_lookup (dir,name,len,result);
if (ret == 0){
struct inode *inode = *result;
if (inode == pseudo_root && !nopseudo){
/* #Specification: pseudo root / DOS/linux
Even in the real root directory (c:\), the directory
/linux won't show
*/
ret = -ENOENT;
iput (pseudo_root);
*result = NULL;
}else if (S_ISDIR(inode->i_mode)){
/* We must place the proper function table */
/* depending if this is a MsDOS directory or an UMSDOS directory */
umsdos_setup_dir_inode(inode);
}
}
}
iput (dir);
return ret;
}
int UMSDOS_rlookup(
struct inode *dir,
const char *name,
int len,
struct inode **result) /* Will hold inode of the file, if successful */
{
return umsdos_rlookup_x(dir,name,len,result,0);
}
 
static int UMSDOS_rrmdir (
struct inode *dir,
const char *name,
int len)
{
/* #Specification: dual mode / rmdir in a DOS directory
In a DOS (not EMD in it) directory, we use a reverse strategy
compared with an 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 make 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 subdirectory are also
Umsdos directory, so we check the EMD file first.
*/
int ret;
if (umsdos_is_pseudodos(dir,name,len)){
/* #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
MsDOS) is always possible.
 
EPERM is returned.
*/
ret = -EPERM;
}else{
umsdos_lockcreate (dir);
dir->i_count++;
ret = msdos_rmdir (dir,name,len);
if (ret == -ENOTEMPTY){
struct inode *sdir;
dir->i_count++;
ret = UMSDOS_rlookup (dir,name,len,&sdir);
PRINTK (("rrmdir lookup %d ",ret));
if (ret == 0){
int empty;
if ((empty = umsdos_isempty (sdir)) != 0){
PRINTK (("isempty %d i_count %ld ",empty,sdir->i_count));
if (empty == 2){
/*
Not a Umsdos directory, so the previous msdos_rmdir
was not lying :-)
*/
ret = -ENOTEMPTY;
}else if (empty == 1){
/* We have to removed the EMD file */
ret = msdos_unlink(sdir,UMSDOS_EMD_FILE
,UMSDOS_EMD_NAMELEN);
sdir = NULL;
if (ret == 0){
dir->i_count++;
ret = msdos_rmdir (dir,name,len);
}
}
}else{
ret = -ENOTEMPTY;
}
iput (sdir);
}
}
umsdos_unlockcreate (dir);
}
iput (dir);
return ret;
}
 
/* #Specification: dual mode / introduction
One goal of UMSDOS is to allow a practical and simple coexistence
between MsDOS and Linux in a single partition. Using the EMD file
in each directory, UMSDOS add Unix semantics and capabilities to
normal DOS file system. 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 MsDOS 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 semantic.
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
semantic 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 semantic.
*/
static struct file_operations umsdos_rdir_operations = {
NULL, /* lseek - default */
UMSDOS_dir_read, /* read */
NULL, /* write - bad */
UMSDOS_rreaddir, /* readdir */
NULL, /* select - default */
UMSDOS_ioctl_dir, /* ioctl - default */
NULL, /* mmap */
NULL, /* no special open code */
NULL, /* no special release code */
NULL /* fsync */
};
 
struct inode_operations umsdos_rdir_inode_operations = {
&umsdos_rdir_operations, /* default directory file-ops */
msdos_create, /* create */
UMSDOS_rlookup, /* lookup */
NULL, /* link */
msdos_unlink, /* unlink */
NULL, /* symlink */
msdos_mkdir, /* mkdir */
UMSDOS_rrmdir, /* rmdir */
NULL, /* mknod */
msdos_rename, /* rename */
NULL, /* readlink */
NULL, /* follow_link */
NULL, /* readpage */
NULL, /* writepage */
NULL, /* bmap */
NULL, /* truncate */
NULL /* permission */
};
 
 
/mangle.c
0,0 → 1,484
/*
* linux/fs/umsdos/mangle.c
*
* Written 1993 by Jacques Gelinas
*
* Control the mangling of file name to fit msdos name space.
* Many optimisation 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>
 
/*
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 mangle 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.
*/
union {
int entry_num;
struct {
unsigned num1:5,num2:5,num3:5;
}num;
} u;
char *pt = info->fake.fname + info->fake.len;
/* lookup for encoding the last character of the extension */
/* It contain valid character after the ugly one to make sure */
/* even if someone overflow the 32 * 32 * 9 limit, it still do */
/* 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)
u.entry_num = info->f_pos / UMSDOS_REC_SIZE;
if (u.entry_num > (9* 32 * 32)){
printk ("UMSDOS: More than 9216 file in a directory.\n"
"This may break the mangling strategy.\n"
"Not a killer problem. See doc.\n");
}
*pt++ = '.';
*pt++ = lookup3 [u.num.num3];
*pt++ = lookup12[u.num.num2];
*pt++ = lookup12[u.num.num1];
*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 name beginning with a period '.' are invalid for MsDOS.
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 exist at least one
DOS pseudo devices 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 serve both as a flag and as a translator.
*/
static char lkp[256];
static char is_init=0;
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 name which are longer than 8+'.'+3 are invalid for MsDOS.
So the file name is to be mangled no more test needed.
This Speed Up for long and very long name.
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. MsDOS do
not support those file name.
*/
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 solve 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 name 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 convert to lower case.
Control character are converted to #.
Space are converted to #.
The following character are also converted to #.
#
" * + , / : ; < = > ? [ \ ] | ~
#
 
Sometime, the problem is not in MsDOS 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 C'est sur on a un 0 a la fin */
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 MsDOS, any MsDOS conforming
file with a basename equal to one of the MsDOS pseudo
devices will be mangled.
 
If a file such as "prn" was created, it would be unreachable
under MsDOS because prn is assumed to be the printer, even
if the file does have an extension.
 
Since the extension is unimportant to MsDOS, 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 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 C'est sur on a un 0 a la fin */
/*
GLU On ne fait cela que si necessaire, on essaye d'etre le
GLU simple dans le cas general (le plus frequent).
*/
info->fake.fname[0] = '-';
strcpy (info->fake.fname+1,basen); /* GLU C'est sur on a un 0 a la fin */
msdos_len = (base_len==8) ? 8 : base_len + 1;
info->msdos_reject = 1;
break;
}
}
}
info->fake.fname[msdos_len] = '\0'; /* Help doing printk */
/* GLU Ce zero devrais deja y etre ! (invariant ?) */
info->fake.len = msdos_len;
/* Pourquoi ne pas utiliser info->fake.len partout ??? plus long ?*/
memcpy (info->entry.name,fname,len);
info->entry.name_len = len;
ret = 0;
}
/*
Evaluate how many record 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",
#elseif
/*
Je trouve les trois exemples ci-dessous tres "malheureux".
Je propose de mettre en minuscule dans un passe preliminaire,
et de tester apres si il y a d'autres caracters "mechants".
Bon, je ne l'ai pas fait, parceque ce n'est pas si facilement
modifiable que ca. Mais c'est pour le principe.
Evidemment cela augmente les chances de "Collision",
par exemple: entre "HELLO" et "Hello", mais ces problemes
peuvent etre traiter ailleur avec les autres 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 ATTENTION : Le resultat de ceux-ci sont differents avec ma version
GLU du mangle par rapport au mangle originale.
GLU CAUSE: La maniere de calculer la variable baselen.
GLU Pour toi c'est toujours 3
GLU Pour moi c'est respectivement 7, 8 et 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
/emd.c
0,0 → 1,495
/*
* 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/sched.h>
#include <linux/errno.h>
#include <linux/string.h>
#include <linux/msdos_fs.h>
#include <linux/umsdos_fs.h>
 
#include <asm/segment.h>
 
#define PRINTK(x)
#define Printk(x) printk x
 
/*
Read a file into kernel space memory
*/
int umsdos_file_read_kmem(
struct inode *inode,
struct file *filp,
char *buf,
int count)
{
int ret;
int old_fs = get_fs();
set_fs (KERNEL_DS);
ret = fat_file_read(inode,filp,buf,count);
set_fs (old_fs);
return ret;
}
/*
Write to a file from kernel space
*/
int umsdos_file_write_kmem(
struct inode *inode,
struct file *filp,
const char *buf,
int count)
{
int ret;
int old_fs = get_fs();
set_fs (KERNEL_DS);
ret = fat_file_write(inode,filp,buf,count);
set_fs (old_fs);
return ret;
}
 
 
/*
Write a block of bytes into one EMD file.
The block of data is NOT in user space.
 
Return 0 if ok, a negative error code if not.
*/
int umsdos_emd_dir_write (
struct inode *emd_dir,
struct file *filp,
char *buf, /* buffer in kernel memory, not in user space */
int count)
{
int written;
filp->f_flags = 0;
written = umsdos_file_write_kmem (emd_dir,filp,buf,count);
return written != count ? -EIO : 0;
}
/*
Read a block of bytes from one EMD file.
The block of data is NOT in user space.
Return 0 if ok, -EIO if any error.
*/
int umsdos_emd_dir_read (
struct inode *emd_dir,
struct file *filp,
char *buf, /* buffer in kernel memory, not in user space */
int count)
{
int ret = 0;
int sizeread;
filp->f_flags = 0;
sizeread = umsdos_file_read_kmem (emd_dir,filp,buf,count);
if (sizeread != count){
printk ("UMSDOS: problem with EMD file. Can't read pos = %Ld (%d != %d)\n"
,filp->f_pos,sizeread,count);
ret = -EIO;
}
return ret;
 
}
/*
Locate the EMD file in a directory and optionally, creates it.
 
Return NULL if error. If ok, dir->u.umsdos_i.emd_inode
*/
struct inode *umsdos_emd_dir_lookup(struct inode *dir, int creat)
{
struct inode *ret = NULL;
if (dir->u.umsdos_i.i_emd_dir != 0){
ret = iget (dir->i_sb,dir->u.umsdos_i.i_emd_dir);
PRINTK (("deja trouve %d %x [%ld] "
,dir->u.umsdos_i.i_emd_dir,ret,ret->i_count));
}else{
umsdos_real_lookup (dir,UMSDOS_EMD_FILE,UMSDOS_EMD_NAMELEN,&ret);
PRINTK (("emd_dir_lookup "));
if (ret != NULL){
PRINTK (("Find --linux "));
dir->u.umsdos_i.i_emd_dir = ret->i_ino;
}else if (creat){
int code;
PRINTK (("avant create "));
dir->i_count++;
code = msdos_create (dir,UMSDOS_EMD_FILE,UMSDOS_EMD_NAMELEN
,S_IFREG|0777,&ret);
PRINTK (("Creat EMD code %d ret %x ",code,ret));
if (ret != NULL){
dir->u.umsdos_i.i_emd_dir = ret->i_ino;
}else{
printk ("UMSDOS: Can't create EMD file\n");
}
}
}
if (ret != NULL){
/* Disable UMSDOS_notify_change() for EMD file */
ret->u.umsdos_i.i_emd_owner = 0xffffffff;
}
return ret;
}
 
/*
Read an entry from the EMD file.
Support variable length record.
Return -EIO if error, 0 if ok.
*/
int umsdos_emd_dir_readentry (
struct inode *emd_dir,
struct file *filp,
struct umsdos_dirent *entry)
{
int ret = umsdos_emd_dir_read(emd_dir,filp,(char*)entry,UMSDOS_REC_SIZE);
if (ret == 0){
/* Variable size record. Maybe, we have to read some more */
int recsize = umsdos_evalrecsize (entry->name_len);
if (recsize > UMSDOS_REC_SIZE){
ret = umsdos_emd_dir_read(emd_dir,filp
,((char*)entry)+UMSDOS_REC_SIZE,recsize - UMSDOS_REC_SIZE);
}
}
return ret;
}
/*
Write an entry in the EMD file.
Return 0 if ok, -EIO if some error.
*/
int umsdos_writeentry (
struct inode *dir,
struct inode *emd_dir,
struct umsdos_info *info,
int free_entry) /* This entry is deleted, so Write all 0's */
{
int ret = 0;
struct file filp;
struct umsdos_dirent *entry = &info->entry;
struct umsdos_dirent entry0;
if (free_entry){
/* #Specification: EMD file / empty entries
Unused entry in the EMD file are identify
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 file around.
*/
memset (entry->spare,0,sizeof(entry->spare));
}
filp.f_pos = info->f_pos;
filp.f_reada = 0;
ret = umsdos_emd_dir_write(emd_dir,&filp,(char*)entry,info->recsize);
if (ret != 0){
printk ("UMSDOS: problem with EMD file. Can't write\n");
}else{
dir->i_ctime = dir->i_mtime = CURRENT_TIME;
dir->i_dirt = 1;
}
return ret;
}
 
#define CHUNK_SIZE (8*UMSDOS_REC_SIZE)
struct find_buffer{
char buffer[CHUNK_SIZE];
int pos; /* read offset in buffer */
int size; /* Current size of buffer */
struct file filp;
};
 
/*
Fill the read buffer and take care of the byte remaining inside.
Unread bytes are simply move to the beginning.
 
Return -ENOENT if EOF, 0 if ok, a negative error code if any problem.
*/
static int umsdos_fillbuf (
struct inode *inode,
struct find_buffer *buf)
{
int ret = -ENOENT;
int mustmove = buf->size - buf->pos;
int mustread;
int remain;
if (mustmove > 0){
memcpy (buf->buffer,buf->buffer+buf->pos,mustmove);
}
buf->pos = 0;
mustread = CHUNK_SIZE - mustmove;
remain = inode->i_size - buf->filp.f_pos;
if (remain < mustread) mustread = remain;
if (mustread > 0){
ret = umsdos_emd_dir_read (inode,&buf->filp,buf->buffer+mustmove
,mustread);
if (ret == 0) buf->size = mustmove + mustread;
}else if (mustmove){
buf->size = mustmove;
ret = 0;
}
return ret;
}
 
/*
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).
 
Caller must do iput on *pt_emd_dir.
 
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 call after this
function since it rely on the f_pos field of info.
*/
static int umsdos_find (
struct inode *dir,
struct umsdos_info *info, /* Hold name and name_len */
/* Will hold the entry found */
struct inode **pt_emd_dir) /* Will hold the emd_dir inode */
/* or NULL if not found */
{
/* #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 is a single
record, multiple contiguous record are allocated.
*/
int ret = -ENOENT;
struct inode *emd_dir = umsdos_emd_dir_lookup(dir,1);
if (emd_dir != NULL){
struct umsdos_dirent *entry = &info->entry;
int recsize = info->recsize;
struct {
off_t posok; /* Position available to store the entry */
int found; /* A valid empty position has been found */
off_t one; /* One empty position -> maybe <- large enough */
int onesize; /* size of empty region starting at one */
}empty;
/* Read several entries at a time to speed up the search */
struct find_buffer buf;
buf.pos = 0;
buf.size = 0;
buf.filp.f_pos = 0;
buf.filp.f_reada = 1;
empty.found = 0;
empty.posok = emd_dir->i_size;
empty.onesize = 0;
while (1){
struct umsdos_dirent *rentry = (struct umsdos_dirent*)
(buf.buffer + buf.pos);
int file_pos = buf.filp.f_pos - buf.size + buf.pos;
if (buf.pos == buf.size){
ret = umsdos_fillbuf (emd_dir,&buf);
if (ret < 0){
/* Not found, so note where it can be added */
info->f_pos = empty.posok;
break;
}
}else if (rentry->name_len == 0){
/* We are looking for an empty section at least */
/* recsize large */
if (entry->name_len == 0){
info->f_pos = file_pos;
ret = 0;
break;
}else if (!empty.found){
if (empty.onesize == 0){
/* This is the first empty record of a section */
empty.one = file_pos;
}
/* grow the empty section */
empty.onesize += UMSDOS_REC_SIZE;
if (empty.onesize == recsize){
/* here is a large enough section */
empty.posok = empty.one;
empty.found = 1;
}
}
buf.pos += UMSDOS_REC_SIZE;
}else{
int entry_size = umsdos_evalrecsize(rentry->name_len);
if (buf.pos+entry_size > buf.size){
ret = umsdos_fillbuf (emd_dir,&buf);
if (ret < 0){
/* Not found, so note where it can be added */
info->f_pos = empty.posok;
break;
}
}else{
empty.onesize = 0; /* Reset the free slot search */
if (entry->name_len == rentry->name_len
&& memcmp(entry->name,rentry->name,rentry->name_len)
==0){
info->f_pos = file_pos;
*entry = *rentry;
ret = 0;
break;
}else{
buf.pos += entry_size;
}
}
}
}
umsdos_manglename(info);
}
*pt_emd_dir = emd_dir;
return ret;
}
/*
Add a new entry in the emd file
Return 0 if ok or a negative error code.
Return -EEXIST if the entry already exist.
 
Complete the information missing in info.
*/
int umsdos_newentry (
struct inode *dir,
struct umsdos_info *info)
{
struct inode *emd_dir;
int ret = umsdos_find (dir,info,&emd_dir);
if (ret == 0){
ret = -EEXIST;
}else if (ret == -ENOENT){
ret = umsdos_writeentry(dir,emd_dir,info,0);
PRINTK (("umsdos_newentry EDM ret = %d\n",ret));
}
iput (emd_dir);
return ret;
}
/*
Create a new hidden link.
Return 0 if ok, an error code if not.
*/
int umsdos_newhidden (
struct inode *dir,
struct umsdos_info *info)
{
struct inode *emd_dir;
int ret;
umsdos_parse ("..LINK",6,info);
info->entry.name_len = 0;
ret = umsdos_find (dir,info,&emd_dir);
iput (emd_dir);
if (ret == -ENOENT || ret == 0){
/* #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.
*/
info->entry.name_len = sprintf (info->entry.name,"..LINK%ld"
,info->f_pos);
ret = 0;
}
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 inode *dir,
struct umsdos_info *info,
int isdir)
{
struct inode *emd_dir;
int ret = umsdos_find (dir,info,&emd_dir);
if (ret == 0){
if (info->entry.name_len != 0){
if ((isdir != 0) != (S_ISDIR(info->entry.mode) != 0)){
if (S_ISDIR(info->entry.mode)){
ret = -EISDIR;
}else{
ret = -ENOTDIR;
}
}else{
ret = umsdos_writeentry(dir,emd_dir,info,1);
}
}
}
iput(emd_dir);
return ret;
}
 
 
/*
Verify is a EMD directory is empty.
Return 0 if not empty
1 if empty
2 if empty, no EMD file.
*/
int umsdos_isempty (struct inode *dir)
{
int ret = 2;
struct inode *emd_dir = umsdos_emd_dir_lookup(dir,0);
/* If the EMD file does not exist, it is certainly empty :-) */
if (emd_dir != NULL){
struct file filp;
/* Find an empty slot */
filp.f_pos = 0;
filp.f_reada = 1;
filp.f_flags = O_RDONLY;
ret = 1;
while (filp.f_pos < emd_dir->i_size){
struct umsdos_dirent entry;
if (umsdos_emd_dir_readentry(emd_dir,&filp,&entry)!=0){
ret = 0;
break;
}else if (entry.name_len != 0){
ret = 0;
break;
}
}
iput (emd_dir);
}
return ret;
}
 
/*
Locate an entry in a EMD directory.
Return 0 if ok, errcod if not, generally -ENOENT.
*/
int umsdos_findentry (
struct inode *dir,
struct umsdos_info *info,
int expect) /* 0: anything */
/* 1: file */
/* 2: directory */
{
struct inode *emd_dir;
int ret = umsdos_find (dir,info,&emd_dir);
if (ret == 0){
if (expect != 0){
if (S_ISDIR(info->entry.mode)){
if (expect != 2) ret = -EISDIR;
}else if (expect == 2){
ret = -ENOTDIR;
}
}
}
iput (emd_dir);
return ret;
}
 
/symlink.c
0,0 → 1,148
/*
* linux/fs/umsdos/file.c
*
* Written 1992 by Jacques Gelinas
* inspired from linux/fs/msdos/file.c Werner Almesberger
*
* Extended MS-DOS regular file handling primitives
*/
 
#include <linux/sched.h>
#include <linux/fs.h>
#include <linux/msdos_fs.h>
#include <linux/errno.h>
#include <linux/fcntl.h>
#include <linux/stat.h>
#include <linux/umsdos_fs.h>
#include <linux/malloc.h>
 
#include <asm/segment.h>
#include <asm/system.h>
 
#define PRINTK(x)
#define Printk(x) printk x
 
/*
Read the data associate with the symlink.
Return length read in buffer or a negative error code.
*/
static int umsdos_readlink_x (
struct inode *inode,
char *buffer,
int (*msdos_read)(struct inode *, struct file *, char *, int),
int bufsiz)
{
int ret = inode->i_size;
struct file filp;
filp.f_pos = 0;
filp.f_reada = 0;
if (ret > bufsiz) ret = bufsiz;
if ((*msdos_read) (inode, &filp, buffer,ret) != ret){
ret = -EIO;
}
return ret;
}
/*
Follow a symbolic link chain by calling open_namei recursively
until an inode is found.
 
Return 0 if ok, or a negative error code if not.
*/
static int UMSDOS_follow_link(
struct inode * dir,
struct inode * inode,
int flag,
int mode,
struct inode ** res_inode)
{
int ret = -ELOOP;
*res_inode = NULL;
if (current->link_count < 5) {
char *path = (char*)kmalloc(PATH_MAX,GFP_KERNEL);
if (path == NULL){
ret = -ENOMEM;
}else{
if (!dir) {
dir = current->fs[1].root;
dir->i_count++;
}
if (!inode){
PRINTK (("symlink: inode = NULL\n"));
ret = -ENOENT;
}else if (!S_ISLNK(inode->i_mode)){
PRINTK (("symlink: Not ISLNK\n"));
*res_inode = inode;
inode = NULL;
ret = 0;
}else{
ret = umsdos_readlink_x (inode,path
,umsdos_file_read_kmem,PATH_MAX-1);
if (ret > 0){
path[ret] = '\0';
PRINTK (("follow :%s: %d ",path,ret));
iput(inode);
inode = NULL;
current->link_count++;
ret = open_namei(path,flag,mode,res_inode,dir);
current->link_count--;
dir = NULL;
}else{
ret = -EIO;
}
}
kfree (path);
}
}
iput(inode);
iput(dir);
PRINTK (("follow_link ret %d\n",ret));
return ret;
}
 
static int UMSDOS_readlink(struct inode * inode, char * buffer, int buflen)
{
int ret = -EINVAL;
if (S_ISLNK(inode->i_mode)) {
ret = umsdos_readlink_x (inode,buffer,fat_file_read,buflen);
}
PRINTK (("readlink %d %x bufsiz %d\n",ret,inode->i_mode,buflen));
iput(inode);
return ret;
}
 
static struct file_operations umsdos_symlink_operations = {
NULL, /* lseek - default */
NULL, /* read */
NULL, /* write */
NULL, /* readdir - bad */
NULL, /* select - default */
NULL, /* ioctl - default */
NULL, /* mmap */
NULL, /* no special open is needed */
NULL, /* release */
NULL /* fsync */
};
 
struct inode_operations umsdos_symlink_inode_operations = {
&umsdos_symlink_operations, /* default file operations */
NULL, /* create */
NULL, /* lookup */
NULL, /* link */
NULL, /* unlink */
NULL, /* symlink */
NULL, /* mkdir */
NULL, /* rmdir */
NULL, /* mknod */
NULL, /* rename */
UMSDOS_readlink, /* readlink */
UMSDOS_follow_link, /* follow_link */
NULL, /* readpage */
NULL, /* writepage */
NULL, /* bmap */
NULL, /* truncate */
NULL /* permission */
};
 
 
 
/notes
0,0 → 1,17
This file contain idea and things I don't want to forget
 
Possible bug in fs/read_write.c
Function sys_readdir()
 
There is a call the verify_area that does not take in account
the count parameter. I guess it should read
 
error = verify_area(VERIFY_WRITE, dirent, count*sizeof (*dirent));
 
instead of
 
error = verify_area(VERIFY_WRITE, dirent, sizeof (*dirent));
 
Of course, now , count is always 1
 
 
/namei.c
0,0 → 1,1096
/*
* 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.
*/
 
#include <linux/errno.h>
#include <linux/kernel.h>
#include <linux/sched.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/malloc.h>
 
#define PRINTK(x)
#define Printk(x) printk x
 
#if 1
/*
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 (dir->u.umsdos_i.u.dir_info.creating
&& dir->u.umsdos_i.u.dir_info.pid != current->pid){
sleep_on(&dir->u.umsdos_i.u.dir_info.p);
ret = 1;
}
return ret;
}
/*
Wait for any lookup process to finish
*/
static void umsdos_waitlookup (struct inode *dir)
{
while (dir->u.umsdos_i.u.dir_info.looking){
sleep_on(&dir->u.umsdos_i.u.dir_info.p);
}
}
/*
Lock all other process out of this directory.
*/
void umsdos_lockcreate (struct inode *dir)
{
/* #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.
*/
/*
Wait for any creation process to finish except
if we (the process) own the lock
*/
while (umsdos_waitcreate(dir)!=0);
dir->u.umsdos_i.u.dir_info.creating++;
dir->u.umsdos_i.u.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 */
dir1->u.umsdos_i.u.dir_info.creating++;
dir1->u.umsdos_i.u.dir_info.pid = current->pid;
dir2->u.umsdos_i.u.dir_info.creating++;
dir2->u.umsdos_i.u.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);
dir->u.umsdos_i.u.dir_info.looking++;
}
 
/*
Unlock the directory.
*/
void umsdos_unlockcreate (struct inode *dir)
{
dir->u.umsdos_i.u.dir_info.creating--;
if (dir->u.umsdos_i.u.dir_info.creating < 0){
printk ("UMSDOS: dir->u.umsdos_i.u.dir_info.creating < 0: %d"
,dir->u.umsdos_i.u.dir_info.creating);
}
wake_up (&dir->u.umsdos_i.u.dir_info.p);
}
/*
Tell directory lookup is over.
*/
void umsdos_endlookup (struct inode *dir)
{
dir->u.umsdos_i.u.dir_info.looking--;
if (dir->u.umsdos_i.u.dir_info.looking < 0){
printk ("UMSDOS: dir->u.umsdos_i.u.dir_info.looking < 0: %d"
,dir->u.umsdos_i.u.dir_info.looking);
}
wake_up (&dir->u.umsdos_i.u.dir_info.p);
}
#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,
const char *name, /* Name of the file to add */
int len,
int errcod) /* Length of the name */
{
int ret = 0;
if (umsdos_is_pseudodos(dir,name,len)){
/* #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 = -EPERM;
ret = errcod;
}else if (name[0] == '.'
&& (len == 1 || (len == 2 && name[1] == '.'))){
/* #Specification: create / . and ..
If one try to creates . or .., it always fail and return
EEXIST.
 
If one try to delete . or .., it always fail and return
EPERM.
 
This should be test at the VFS layer level to avoid
duplicating this in all file systems. Any comments ?
*/
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 EDM file.
 
Return the status of the operation. 0 mean success.
*/
static int umsdos_create_any (
struct inode *dir,
const char *name, /* Name of the file to add */
int len, /* Length of the name */
int mode, /* Permission bit + file type ??? */
int rdev, /* major, minor or 0 for ordinary file */
/* and symlinks */
char flags,
struct inode **result) /* Will hold the inode of the newly created */
/* file */
{
int ret = umsdos_nevercreat(dir,name,len,-EEXIST);
if (ret == 0){
struct umsdos_info info;
ret = umsdos_parse (name,len,&info);
*result = NULL;
if (ret == 0){
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
= CURRENT_TIME;
info.entry.nlink = 1;
umsdos_lockcreate(dir);
ret = umsdos_newentry (dir,&info);
if (ret == 0){
dir->i_count++;
ret = msdos_create (dir,info.fake.fname,info.fake.len
,S_IFREG|0777,result);
if (ret == 0){
struct inode *inode = *result;
umsdos_lookup_patch (dir,inode,&info.entry,info.f_pos);
PRINTK (("inode %p[%ld] ",inode,inode->i_count));
PRINTK (("Creation OK: [%d] %s %d pos %d\n",dir->i_ino
,info.fake.fname,current->pid,info.f_pos));
}else{
/* #Specification: create / file exist in DOS
Here is a situation. Trying to create a file with
UMSDOS. The file is unknown to UMSDOS but already
exist 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 mean 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.
*/
if (ret == -EEXIST){
printk ("UMSDOS: out of sync, Creation error [%ld], "
"deleting %s %d %d pos %ld\n",dir->i_ino
,info.fake.fname,-ret,current->pid,info.f_pos);
}
umsdos_delentry (dir,&info,0);
}
PRINTK (("umsdos_create %s ret = %d pos %d\n"
,info.fake.fname,ret,info.f_pos));
}
umsdos_unlockcreate(dir);
}
}
iput (dir);
return ret;
}
/*
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,
int flags) /* 0 == copy flags from old_name */
/* != 0, this is the value of flags */
{
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 = flags ? flags : old_info->entry.flags;
new_info->entry.nlink = old_info->entry.nlink;
}
 
#define chkstk() \
if (STACK_MAGIC != *(unsigned long *)current->kernel_stack_page){\
printk(KERN_ALERT "UMSDOS: %s magic %x != %lx ligne %d\n" \
, current->comm,STACK_MAGIC \
,*(unsigned long *)current->kernel_stack_page \
,__LINE__); \
}
/*
Rename a file (move) in the file system.
*/
static int umsdos_rename_f(
struct inode * old_dir,
const char * old_name,
int old_len,
struct inode * new_dir,
const char * new_name,
int new_len,
int flags) /* 0 == copy flags from old_name */
/* != 0, this is the value of flags */
{
int ret = -EPERM;
struct umsdos_info old_info;
int old_ret = umsdos_parse (old_name,old_len,&old_info);
struct umsdos_info new_info;
int new_ret = umsdos_parse (new_name,new_len,&new_info);
chkstk();
PRINTK (("umsdos_rename %d %d ",old_ret,new_ret));
if (old_ret == 0 && new_ret == 0){
umsdos_lockcreate2(old_dir,new_dir);
chkstk();
PRINTK (("old findentry "));
ret = umsdos_findentry(old_dir,&old_info,0);
chkstk();
PRINTK (("ret %d ",ret));
if (ret == 0){
/* check sticky bit on old_dir */
if ( !(old_dir->i_mode & S_ISVTX) || fsuser() ||
current->fsuid == old_info.entry.uid ||
current->fsuid == old_dir->i_uid ) {
/* Does new_name already exist? */
PRINTK(("new findentry "));
ret = umsdos_findentry(new_dir,&new_info,0);
if (ret != 0 || /* if destination file exists, are we allowed to replace it ? */
!(new_dir->i_mode & S_ISVTX) || fsuser() ||
current->fsuid == new_info.entry.uid ||
current->fsuid == new_dir->i_uid ) {
PRINTK (("new newentry "));
umsdos_ren_init(&new_info,&old_info,flags);
ret = umsdos_newentry (new_dir,&new_info);
chkstk();
PRINTK (("ret %d %d ",ret,new_info.fake.len));
if (ret == 0){
PRINTK (("msdos_rename "));
old_dir->i_count++;
new_dir->i_count++; /* Both inode are needed later */
ret = msdos_rename (old_dir
,old_info.fake.fname,old_info.fake.len
,new_dir
,new_info.fake.fname,new_info.fake.len
,0);
chkstk();
PRINTK (("after m_rename ret %d ",ret));
if (ret != 0){
umsdos_delentry (new_dir,&new_info
,S_ISDIR(new_info.entry.mode));
chkstk();
}else{
ret = umsdos_delentry (old_dir,&old_info
,S_ISDIR(old_info.entry.mode));
chkstk();
if (ret == 0){
/*
This UMSDOS_lookup does not look very useful.
It makes sure that the inode of the file will
be correctly setup (umsdos_patch_inode()) in
case it is already in use.
Not very efficient ...
*/
struct inode *inode;
new_dir->i_count++;
PRINTK (("rename lookup len %d %d -- ",new_len,new_info.entry.flags));
ret = UMSDOS_lookup (new_dir,new_name,new_len
,&inode);
chkstk();
if (ret != 0){
printk ("UMSDOS: partial rename for file %s\n"
,new_info.entry.name);
}else{
/*
Update f_pos so notify_change will succeed
if the file was already in use.
*/
umsdos_set_dirinfo (inode,new_dir,new_info.f_pos);
chkstk();
iput (inode);
}
}
}
}
}else{
/* sticky bit set on new_dir */
PRINTK(("sticky set on new "));
ret = -EPERM;
}
}else{
/* sticky bit set on old_dir */
PRINTK(("sticky set on old "));
ret = -EPERM;
}
}
umsdos_unlockcreate(old_dir);
umsdos_unlockcreate(new_dir);
}
iput (old_dir);
iput (new_dir);
PRINTK (("\n"));
return ret;
}
/*
Setup un Symbolic link or a (pseudo) hard link
Return a negative error code or 0 if ok.
*/
static int umsdos_symlink_x(
struct inode * dir,
const char * name,
int len,
const char * symname, /* name will point to this path */
int mode,
char flags)
{
/* #Specification: symbolic links / strategy
A symbolic link is simply a file which hold a path. It is
implemented as a normal MSDOS file (not very space efficient :-()
 
I see 2 different way to do it. One is to place the link data
in unused entry of the EMD file. The other is to have a separate
file dedicated to hold all symbolic links data.
 
Let's go for simplicity...
*/
struct inode *inode;
int ret;
dir->i_count++; /* We keep the inode in case we need it */
/* later */
ret = umsdos_create_any (dir,name,len,mode,0,flags,&inode);
PRINTK (("umsdos_symlink ret %d ",ret));
if (ret == 0){
int len = strlen(symname);
struct file filp;
filp.f_pos = 0;
/* Make the inode acceptable to MSDOS */
ret = umsdos_file_write_kmem (inode,&filp,symname,len);
iput (inode);
if (ret >= 0){
if (ret != len){
ret = -EIO;
printk ("UMSDOS: "
"Can't write symbolic link data\n");
}else{
ret = 0;
}
}
if (ret != 0){
UMSDOS_unlink (dir,name,len);
dir = NULL;
}
}
iput (dir);
PRINTK (("\n"));
return ret;
}
/*
Setup un Symbolic link.
Return a negative error code or 0 if ok.
*/
int UMSDOS_symlink(
struct inode * dir,
const char * name,
int len,
const char * symname) /* name will point to this path */
{
return umsdos_symlink_x (dir,name,len,symname,S_IFLNK|0777,0);
}
/*
Add a link to an inode in a directory
*/
int UMSDOS_link (
struct inode * oldinode,
struct inode * dir,
const char * name,
int len)
{
/* #Specification: hard link / strategy
Well ... hard link are difficult to implement on top of an
MsDOS fat file system. Unlike UNIX file systems, there are no
inode. A directory entry hold the functionality of the inode
and the entry.
 
We will used the same strategy as a normal Unix file system
(with inode) 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 MsDOS pseudo
inode number to record the link. For this reason, the link
will be done using hidden symbolic links. The following
scenario illustrate how it work.
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).
*/
int ret = 0;
if (S_ISDIR(oldinode->i_mode)){
/* #Specification: hard link / directory
A hard link can't be made on a directory. EPERM is returned
in this case.
*/
ret = -EPERM;
}else if ((ret = umsdos_nevercreat(dir,name,len,-EPERM))==0){
struct inode *olddir;
ret = umsdos_get_dirowner(oldinode,&olddir);
PRINTK (("umsdos_link dir_owner = %d -> %p [%ld] "
,oldinode->u.umsdos_i.i_dir_owner,olddir,olddir->i_count));
if (ret == 0){
struct umsdos_dirent entry;
umsdos_lockcreate2(dir,olddir);
ret = umsdos_inode2entry (olddir,oldinode,&entry);
if (ret == 0){
PRINTK (("umsdos_link :%s: ino %d flags %d "
,entry.name
,oldinode->i_ino,entry.flags));
if (!(entry.flags & UMSDOS_HIDDEN)){
/* #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.
*/
struct umsdos_info info;
ret = umsdos_newhidden (olddir,&info);
if (ret == 0){
olddir->i_count+=2;
PRINTK (("olddir[%ld] ",olddir->i_count));
ret = umsdos_rename_f (olddir,entry.name
,entry.name_len
,olddir,info.entry.name,info.entry.name_len
,UMSDOS_HIDDEN);
if (ret == 0){
char *path = (char*)kmalloc(PATH_MAX,GFP_KERNEL);
if (path == NULL){
ret = -ENOMEM;
}else{
PRINTK (("olddir[%ld] ",olddir->i_count));
ret = umsdos_locate_path (oldinode,path);
PRINTK (("olddir[%ld] ",olddir->i_count));
if (ret == 0){
olddir->i_count++;
ret = umsdos_symlink_x (olddir
,entry.name
,entry.name_len,path
,S_IFREG|0777,UMSDOS_HLINK);
if (ret == 0){
dir->i_count++;
ret = umsdos_symlink_x (dir,name,len
,path
,S_IFREG|0777,UMSDOS_HLINK);
}
}
kfree (path);
}
}
}
}else{
char *path = (char*)kmalloc(PATH_MAX,GFP_KERNEL);
if (path == NULL){
ret = -ENOMEM;
}else{
ret = umsdos_locate_path (oldinode,path);
if (ret == 0){
dir->i_count++;
ret = umsdos_symlink_x (dir,name,len,path
,S_IFREG|0777,UMSDOS_HLINK);
}
kfree (path);
}
}
}
umsdos_unlockcreate(olddir);
umsdos_unlockcreate(dir);
}
iput (olddir);
}
if (ret == 0){
struct iattr newattrs;
oldinode->i_nlink++;
newattrs.ia_valid = 0;
ret = UMSDOS_notify_change(oldinode, &newattrs);
}
iput (oldinode);
iput (dir);
PRINTK (("umsdos_link %d\n",ret));
return ret;
}
/*
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 EDM file.
 
Return the status of the operation. 0 mean success.
*/
int UMSDOS_create (
struct inode *dir,
const char *name, /* Name of the file to add */
int len, /* Length of the name */
int mode, /* Permission bit + file type ??? */
struct inode **result) /* Will hold the inode of the newly created */
/* file */
{
return umsdos_create_any (dir,name,len,mode,0,0,result);
}
/*
Add a sub-directory in a directory
*/
int UMSDOS_mkdir(
struct inode * dir,
const char * name,
int len,
int mode)
{
int ret = umsdos_nevercreat(dir,name,len,-EEXIST);
if (ret == 0){
struct umsdos_info info;
ret = umsdos_parse (name,len,&info);
PRINTK (("umsdos_mkdir %d\n",ret));
if (ret == 0){
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
= CURRENT_TIME;
info.entry.flags = 0;
umsdos_lockcreate(dir);
info.entry.nlink = 1;
ret = umsdos_newentry (dir,&info);
PRINTK (("newentry %d ",ret));
if (ret == 0){
dir->i_count++;
ret = msdos_mkdir (dir,info.fake.fname,info.fake.len,mode);
if (ret != 0){
umsdos_delentry (dir,&info,1);
/* #Specification: mkdir / Directory already exist in DOS
We do the same thing as for file creation.
For all user it is an error.
*/
}else{
/* #Specification: mkdir / umsdos directory / create EMD
When we created a new sub-directory in a UMSDOS
directory (one with full UMSDOS semantic), we
create immediately an EMD file in the new
sub-directory so it inherit UMSDOS semantic.
*/
struct inode *subdir;
ret = umsdos_real_lookup (dir,info.fake.fname
,info.fake.len,&subdir);
if (ret == 0){
struct inode *result;
ret = msdos_create (subdir,UMSDOS_EMD_FILE
,UMSDOS_EMD_NAMELEN,S_IFREG|0777,&result);
subdir = NULL;
iput (result);
}
if (ret < 0){
printk ("UMSDOS: Can't create empty --linux-.---\n");
}
iput (subdir);
}
}
umsdos_unlockcreate(dir);
}
}
PRINTK (("umsdos_mkdir %d\n",ret));
iput (dir);
return ret;
}
/*
Add a new device special file into a directory.
*/
int UMSDOS_mknod(
struct inode * dir,
const char * name,
int len,
int mode,
int rdev)
{
/* #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.
*/
struct inode *inode;
int ret = umsdos_create_any (dir,name,len,mode,rdev,0,&inode);
iput (inode);
return ret;
}
 
/*
Remove a sub-directory.
*/
int UMSDOS_rmdir(
struct inode * dir,
const char * name,
int len)
{
/* #Specification: style / iput strategy
In the UMSDOS project, I am trying to apply a single
programming style regarding inode management. Many
entry point are receiving an inode to act on, and must
do an iput() as soon as they are finished with
the inode.
 
For simple case, there is no problem. When you introduce
error checking, you end up with many iput placed around the
code.
 
The coding style I use all around is one where I am trying
to provide independent flow logic (I don't know how to
name this). With this style, code is easier to understand
but you rapidly get iput() all around. Here is an exemple
of what I am trying to avoid.
 
#
if (a){
...
if(b){
...
}
...
if (c){
// Complex state. Was b true ?
...
}
...
}
// Weird state
if (d){
// ...
}
// Was iput finally done ?
return status;
#
 
Here is the style I am using. Still sometime I do the
first when things are very simple (or very complicated :-( )
 
#
if (a){
if (b){
...
}else if (c){
// A single state gets here
}
}else if (d){
...
}
return status;
#
 
Again, while this help clarifying the code, I often get a lot
of iput(), unlike the first style, where I can place few
"strategic" iput(). "strategic" also mean, more difficult
to place.
 
So here is the style I will be using from now on in this project.
There is always an iput() at the end of a function (which has
to do an iput()). One iput by inode. There is also one iput()
at the places where a successful operation is achieved. This
iput() is often done by a sub-function (often from the msdos
file system). So I get one too many iput() ? At the place
where an iput() is done, the inode is simply nulled, disabling
the last one.
 
#
if (a){
if (b){
...
}else if (c){
msdos_rmdir(dir,...);
dir = NULL;
}
}else if (d){
...
}
iput (dir);
return status;
#
 
Note that the umsdos_lockcreate() and umsdos_unlockcreate() function
pair goes against this practice of "forgetting" the inode as soon
as possible.
*/
int ret = umsdos_nevercreat(dir,name,len,-EPERM);
if (ret == 0){
struct inode *sdir;
dir->i_count++;
ret = UMSDOS_lookup (dir,name,len,&sdir);
PRINTK (("rmdir lookup %d ",ret));
if (ret == 0){
int empty;
umsdos_lockcreate(dir);
if (sdir->i_count > 1){
ret = -EBUSY;
}else if ((empty = umsdos_isempty (sdir)) != 0){
PRINTK (("isempty %d i_count %ld ",empty,sdir->i_count));
/* check sticky bit */
if ( !(dir->i_mode & S_ISVTX) || fsuser() ||
current->fsuid == sdir->i_uid ||
current->fsuid == dir->i_uid ) {
if (empty == 1){
/* We have to removed the EMD file */
ret = msdos_unlink(sdir,UMSDOS_EMD_FILE
,UMSDOS_EMD_NAMELEN);
sdir = NULL;
}
/* sdir must be free before msdos_rmdir() */
iput (sdir);
sdir = NULL;
PRINTK (("isempty ret %d nlink %d ",ret,dir->i_nlink));
if (ret == 0){
struct umsdos_info info;
dir->i_count++;
umsdos_parse (name,len,&info);
/* The findentry is there only to complete */
/* the mangling */
umsdos_findentry (dir,&info,2);
ret = msdos_rmdir (dir,info.fake.fname
,info.fake.len);
if (ret == 0){
ret = umsdos_delentry (dir,&info,1);
}
}
}else{
/* sticky bit set and we don't have permission */
PRINTK(("sticky set "));
ret = -EPERM;
}
}else{
/*
The subdirectory is not empty, so leave it there
*/
ret = -ENOTEMPTY;
}
iput(sdir);
umsdos_unlockcreate(dir);
}
}
iput (dir);
PRINTK (("umsdos_rmdir %d\n",ret));
return ret;
}
/*
Remove a file from the directory.
*/
int UMSDOS_unlink (
struct inode * dir,
const char * name,
int len)
{
int ret = umsdos_nevercreat(dir,name,len,-EPERM);
if (ret == 0){
struct umsdos_info info;
ret = umsdos_parse (name,len,&info);
if (ret == 0){
umsdos_lockcreate(dir);
ret = umsdos_findentry(dir,&info,1);
if (ret == 0){
PRINTK (("UMSDOS_unlink %s ",info.fake.fname));
/* check sticky bit */
if ( !(dir->i_mode & S_ISVTX) || fsuser() ||
current->fsuid == info.entry.uid ||
current->fsuid == dir->i_uid ) {
if (info.entry.flags & UMSDOS_HLINK){
/* #Specification: hard link / deleting a link
When we deletes a file, and this file is a link
we must subtract 1 to the nlink field of the
hidden link.
If the count goes to 0, we delete this hidden
link too.
*/
/*
First, get the inode of the hidden link
using the standard lookup function.
*/
struct inode *inode;
dir->i_count++;
ret = UMSDOS_lookup (dir,name,len,&inode);
if (ret == 0){
PRINTK (("unlink nlink = %d ",inode->i_nlink));
inode->i_nlink--;
if (inode->i_nlink == 0){
struct inode *hdir = iget(inode->i_sb
,inode->u.umsdos_i.i_dir_owner);
struct umsdos_dirent entry;
ret = umsdos_inode2entry (hdir,inode,&entry);
if (ret == 0){
ret = UMSDOS_unlink (hdir,entry.name
,entry.name_len);
}else{
iput (hdir);
}
}else{
struct iattr newattrs;
newattrs.ia_valid = 0;
ret = UMSDOS_notify_change (inode, &newattrs);
}
iput (inode);
}
}
if (ret == 0){
ret = umsdos_delentry (dir,&info,0);
if (ret == 0){
PRINTK (("Avant msdos_unlink %s ",info.fake.fname));
dir->i_count++;
ret = msdos_unlink_umsdos (dir,info.fake.fname
,info.fake.len);
PRINTK (("msdos_unlink %s %o ret %d ",info.fake.fname
,info.entry.mode,ret));
}
}
}else{
/* sticky bit set and we've not got permission */
PRINTK(("sticky set "));
ret = -EPERM;
}
}
umsdos_unlockcreate(dir);
}
}
iput (dir);
PRINTK (("umsdos_unlink %d\n",ret));
return ret;
}
 
/*
Rename a file (move) in the file system.
*/
int UMSDOS_rename(
struct inode * old_dir,
const char * old_name,
int old_len,
struct inode * new_dir,
const char * new_name,
int new_len,
int must_be_dir)
{
/* #Specification: weakness / rename
There is a case where UMSDOS rename has a different behavior
than 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 (not sure) is in the linux VFS msdos driver.
I believe this is not a bug but a design feature, because
an inode number represent some sort of directory address
in the MSDOS directory structure. So moving the file into
another directory does not preserve the inode number.
*/
int ret = umsdos_nevercreat(new_dir,new_name,new_len,-EEXIST);
if (ret == 0){
/* umsdos_rename_f eat the inode and we may need those later */
old_dir->i_count++;
new_dir->i_count++;
ret = umsdos_rename_f (old_dir,old_name,old_len,new_dir,new_name
,new_len,0);
if (ret == -EEXIST){
/* #Specification: rename / new name exist
If the destination name already exist, 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 insuring that only
one process could achieve a rename (modification) operation
in the source and destination directory. This was also
insuring the operation was "atomic".
 
This has been changed because this was creating a kernel
stack overflow (stack is only 4k 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 (not sure) 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.
*/
/* This is not super efficient but should work */
new_dir->i_count++;
ret = UMSDOS_unlink (new_dir,new_name,new_len);
chkstk();
PRINTK (("rename unlink ret %d %d -- ",ret,new_len));
if (ret == -EISDIR){
new_dir->i_count++;
ret = UMSDOS_rmdir (new_dir,new_name,new_len);
chkstk();
PRINTK (("rename rmdir ret %d -- ",ret));
}
if (ret == 0){
ret = umsdos_rename_f (old_dir,old_name,old_len
,new_dir,new_name,new_len,0);
new_dir = old_dir = NULL;
}
}
}
iput (new_dir);
iput (old_dir);
return ret;
}
 
/ioctl.c
0,0 → 1,319
/*
* linux/fs/umsdos/ioctl.c
*
* Written 1993 by Jacques Gelinas
*
* Extended MS-DOS ioctl directory handling functions
*/
 
#include <asm/segment.h>
#include <linux/errno.h>
#include <linux/mm.h>
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/fs.h>
#include <linux/msdos_fs.h>
#include <linux/umsdos_fs.h>
 
#define PRINTK(x)
#define Printk(x) printk x
 
struct UMSDOS_DIR_ONCE {
struct dirent *ent;
int count;
};
 
/*
Record a single entry the first call.
Return -EINVAL the next one.
*/
static int umsdos_ioctl_fill(
void * buf,
const char * name,
int name_len,
off_t offset,
ino_t ino)
{
int ret = -EINVAL;
struct UMSDOS_DIR_ONCE *d = (struct UMSDOS_DIR_ONCE *)buf;
if (d->count == 0){
memcpy_tofs (d->ent->d_name,name,name_len);
put_user ('\0',d->ent->d_name+name_len);
put_user (name_len,&d->ent->d_reclen);
put_user (ino,&d->ent->d_ino);
put_user (offset,&d->ent->d_off);
d->count = 1;
ret = 0;
}
return ret;
}
 
 
/*
Perform special function on a directory
*/
int UMSDOS_ioctl_dir (
struct inode *dir,
struct file *filp,
unsigned int cmd,
unsigned long data)
{
int ret = -EPERM;
int err;
/* #Specification: ioctl / acces
Only root (effective id) is allowed to do IOCTL on directory
in UMSDOS. EPERM is returned for other user.
*/
/*
Well, not all cases require write access, but it simplifies
the code, and let's face it, there is only one client (umssync)
for all this.
*/
if ((err = verify_area(VERIFY_WRITE,(void*)data,sizeof(struct umsdos_ioctl))) < 0) {
ret = err;
}else if (current->euid == 0
|| cmd == UMSDOS_GETVERSION){
struct umsdos_ioctl *idata = (struct umsdos_ioctl *)data;
ret = -EINVAL;
/* #Specification: ioctl / prototypes
The official prototype for the umsdos ioctl on directory
is:
 
int ioctl (
int fd, // File handle of the directory
int cmd, // command
struct umsdos_ioctl *data)
 
The struct and the commands are defined in linux/umsdos_fs.h.
 
umsdos_progs/umsdosio.c provide an interface in C++ to all
these ioctl. umsdos_progs/udosctl is a small utility showing
all this.
 
These ioctl generally allow one to work on the EMD or the
DOS directory independently. These are essential to implement
the synchronise.
*/
PRINTK (("ioctl %d ",cmd));
if (cmd == UMSDOS_GETVERSION){
/* #Specification: ioctl / UMSDOS_GETVERSION
The field version and release of the structure
umsdos_ioctl are filled with the version and release
number of the fs code in the kernel. This will allow
some form of checking. Users won't be able to run
incompatible utility such as the synchroniser (umssync).
umsdos_progs/umsdosio.c enforce this checking.
 
Return always 0.
*/
put_fs_byte (UMSDOS_VERSION,&idata->version);
put_fs_byte (UMSDOS_RELEASE,&idata->release);
ret = 0;
}else if (cmd == UMSDOS_READDIR_DOS){
/* #Specification: ioctl / UMSDOS_READDIR_DOS
One entry is read from the DOS directory at the current
file position. The entry is put as is in the dos_dirent
field of struct umsdos_ioctl.
 
Return > 0 if success.
*/
struct UMSDOS_DIR_ONCE bufk;
bufk.count = 0;
bufk.ent = &idata->dos_dirent;
fat_readdir(dir,filp,&bufk,umsdos_ioctl_fill);
ret = bufk.count == 1 ? 1 : 0;
}else if (cmd == UMSDOS_READDIR_EMD){
/* #Specification: ioctl / UMSDOS_READDIR_EMD
One entry is read from the EMD at the current
file position. The entry is put as is in the umsdos_dirent
field of struct umsdos_ioctl. The corresponding mangled
DOS entry name is put in the dos_dirent field.
 
All entries are read including hidden links. Blank
entries are skipped.
 
Return > 0 if success.
*/
struct inode *emd_dir = umsdos_emd_dir_lookup (dir,0);
if (emd_dir != NULL){
while (1){
if (filp->f_pos >= emd_dir->i_size){
ret = 0;
break;
}else{
struct umsdos_dirent entry;
off_t f_pos = filp->f_pos;
ret = umsdos_emd_dir_readentry (emd_dir,filp,&entry);
if (ret < 0){
break;
}else if (entry.name_len > 0){
struct umsdos_info info;
ret = entry.name_len;
umsdos_parse (entry.name,entry.name_len,&info);
info.f_pos = f_pos;
umsdos_manglename(&info);
memcpy_tofs(&idata->umsdos_dirent,&entry
,sizeof(entry));
memcpy_tofs(&idata->dos_dirent.d_name
,info.fake.fname,info.fake.len+1);
break;
}
}
}
iput (emd_dir);
}else{
/* The absence of the EMD is simply seen as an EOF */
ret = 0;
}
}else if (cmd == UMSDOS_INIT_EMD){
/* #Specification: ioctl / UMSDOS_INIT_EMD
The UMSDOS_INIT_EMD command make sure the EMD
exist for a directory. If it does not, it is
created. Also, it makes sure the directory functions
table (struct inode_operations) is set to the UMSDOS
semantic. This mean that umssync may be applied to
an "opened" msdos directory, and it will change behavior
on the fly.
 
Return 0 if success.
*/
extern struct inode_operations umsdos_rdir_inode_operations;
struct inode *emd_dir = umsdos_emd_dir_lookup (dir,1);
ret = emd_dir != NULL;
iput (emd_dir);
dir->i_op = ret
? &umsdos_dir_inode_operations
: &umsdos_rdir_inode_operations;
}else{
struct umsdos_ioctl data;
memcpy_fromfs (&data,idata,sizeof(data));
if (cmd == UMSDOS_CREAT_EMD){
/* #Specification: ioctl / UMSDOS_CREAT_EMD
The umsdos_dirent field of the struct umsdos_ioctl is used
as is to create a new entry in the EMD of the directory.
The DOS directory is not modified.
No validation is done (yet).
 
Return 0 if success.
*/
struct umsdos_info info;
/* This makes sure info.entry and info in general is correctly */
/* initialised */
memcpy (&info.entry,&data.umsdos_dirent
,sizeof(data.umsdos_dirent));
umsdos_parse (data.umsdos_dirent.name
,data.umsdos_dirent.name_len,&info);
ret = umsdos_newentry (dir,&info);
}else if (cmd == UMSDOS_RENAME_DOS){
/* #Specification: ioctl / UMSDOS_RENAME_DOS
A file or directory is rename in a DOS directory
(not moved across directory). The source name
is in the dos_dirent.name field and the destination
is in umsdos_dirent.name field.
 
This ioctl allows umssync to rename a mangle file
name before syncing it back in the EMD.
*/
dir->i_count += 2;
ret = msdos_rename (dir
,data.dos_dirent.d_name,data.dos_dirent.d_reclen
,dir
,data.umsdos_dirent.name,data.umsdos_dirent.name_len,0);
}else if (cmd == UMSDOS_UNLINK_EMD){
/* #Specification: ioctl / UMSDOS_UNLINK_EMD
The umsdos_dirent field of the struct umsdos_ioctl is used
as is to remove an entry from the EMD of the directory.
No validation is done (yet). The mode field is used
to validate S_ISDIR or S_ISREG.
 
Return 0 if success.
*/
struct umsdos_info info;
/* This makes sure info.entry and info in general is correctly */
/* initialised */
memcpy (&info.entry,&data.umsdos_dirent
,sizeof(data.umsdos_dirent));
umsdos_parse (data.umsdos_dirent.name
,data.umsdos_dirent.name_len,&info);
ret = umsdos_delentry (dir,&info
,S_ISDIR(data.umsdos_dirent.mode));
}else if (cmd == UMSDOS_UNLINK_DOS){
/* #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.
*/
dir->i_count++;
ret = msdos_unlink (dir,data.dos_dirent.d_name
,data.dos_dirent.d_reclen);
}else if (cmd == UMSDOS_RMDIR_DOS){
/* #Specification: ioctl / UMSDOS_RMDIR_DOS
The dos_dirent field of the struct umsdos_ioctl is used to
execute a msdos_unlink operation. The d_name and d_reclen
fields are used.
 
Return 0 if success.
*/
dir->i_count++;
ret = msdos_rmdir (dir,data.dos_dirent.d_name
,data.dos_dirent.d_reclen);
}else if (cmd == UMSDOS_STAT_DOS){
/* #Specification: ioctl / UMSDOS_STAT_DOS
The dos_dirent field of the struct umsdos_ioctl is
used to execute a stat operation in the DOS directory.
The d_name and d_reclen fields are used.
 
The following field of umsdos_ioctl.stat are filled.
 
st_ino,st_mode,st_size,st_atime,st_mtime,st_ctime,
Return 0 if success.
*/
struct inode *inode;
ret = umsdos_real_lookup (dir,data.dos_dirent.d_name
,data.dos_dirent.d_reclen,&inode);
if (ret == 0){
data.stat.st_ino = inode->i_ino;
data.stat.st_mode = inode->i_mode;
data.stat.st_size = inode->i_size;
data.stat.st_atime = inode->i_atime;
data.stat.st_ctime = inode->i_ctime;
data.stat.st_mtime = inode->i_mtime;
memcpy_tofs (&idata->stat,&data.stat,sizeof(data.stat));
iput (inode);
}
}else if (cmd == UMSDOS_DOS_SETUP){
/* #Specification: ioctl / UMSDOS_DOS_SETUP
The UMSDOS_DOS_SETUP ioctl allow changing the
default permission of the MsDOS file system driver
on the fly. The MsDOS driver apply global permission
to every file and directory. Normally these permissions
are controlled by a mount option. This is not
available for root partition, so a special utility
(umssetup) is provided to do this, normally in
/etc/rc.local.
 
Be aware that this apply ONLY to MsDOS directory
(those without EMD --linux-.---). Umsdos directory
have independent (standard) permission for each
and every file.
 
The field umsdos_dirent provide the information needed.
umsdos_dirent.uid and gid sets the owner and group.
umsdos_dirent.mode set the permissions flags.
*/
dir->i_sb->u.msdos_sb.options.fs_uid = data.umsdos_dirent.uid;
dir->i_sb->u.msdos_sb.options.fs_gid = data.umsdos_dirent.gid;
dir->i_sb->u.msdos_sb.options.fs_umask = data.umsdos_dirent.mode;
ret = 0;
}
}
}
PRINTK (("ioctl return %d\n",ret));
return ret;
}
 
 
 
/.depend
0,0 → 1,101
check.o: \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/signal.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/sched.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/head.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/kernel.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/errno.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/string.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/types.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/ptrace.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/mman.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/mm.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/asm/system.h
dir.o: \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/sched.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/string.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/fs.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/msdos_fs.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/errno.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/stat.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/limits.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/umsdos_fs.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/malloc.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/asm/segment.h
emd.o: \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/types.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/fcntl.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/kernel.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/sched.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/errno.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/string.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/msdos_fs.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/umsdos_fs.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/asm/segment.h
file.o: \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/sched.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/fs.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/msdos_fs.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/errno.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/fcntl.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/stat.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/msdos_fs.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/umsdos_fs.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/asm/segment.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/asm/system.h
inode.o: \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/module.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/fs.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/msdos_fs.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/kernel.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/sched.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/errno.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/asm/segment.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/string.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/stat.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/umsdos_fs.h
ioctl.o: \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/asm/segment.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/errno.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/mm.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/kernel.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/sched.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/fs.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/msdos_fs.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/umsdos_fs.h
mangle.o: \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/errno.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/string.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/kernel.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/umsdos_fs.h
namei.o: \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/errno.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/kernel.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/sched.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/types.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/fcntl.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/stat.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/string.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/msdos_fs.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/umsdos_fs.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/malloc.h
rdir.o: \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/sched.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/fs.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/msdos_fs.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/errno.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/stat.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/limits.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/umsdos_fs.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/malloc.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/asm/segment.h
symlink.o: \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/sched.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/fs.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/msdos_fs.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/errno.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/fcntl.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/stat.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/umsdos_fs.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/malloc.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/asm/segment.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/asm/system.h
/Makefile
0,0 → 1,24
#
# Makefile for the umsdos unix-like filesystem routines.
#
# Note! Dependencies are done automagically by 'make dep', which also
# removes any old dependencies. DON'T put your own dependencies here
# unless it's something special (ie not a .c file).
#
# Note 2! The CFLAGS definitions are now in the main makefile...
 
O_TARGET := umsdos.o
O_OBJS := dir.o emd.o file.o inode.o ioctl.o mangle.o namei.o \
rdir.o symlink.o #check.o
M_OBJS := $(O_TARGET)
 
include $(TOPDIR)/Rules.make
 
clean:
rm -f core *.o *.a *.s
 
p:
proto *.c >/usr/include/linux/umsdos_fs.p
 
doc:
nadoc -i -p umsdos.doc - /tmp/umsdos.mpg
/check.c
0,0 → 1,51
/*
* linux/fs/umsdos/check.c
*
*
*/
 
#include <linux/signal.h>
#include <linux/sched.h>
#include <linux/head.h>
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/string.h>
#include <linux/types.h>
#include <linux/ptrace.h>
#include <linux/mman.h>
#include <linux/mm.h>
 
#include <asm/system.h>
 
static int check_one_table(struct pde * page_dir)
{
if (pgd_none(*page_dir))
return 0;
if (pgd_bad(*page_dir))
return 1;
return 0;
}
 
/*
* This function checks all page tables of "current"
*/
void check_page_tables(void)
{
struct pgd * pg_dir;
static int err = 0;
 
int stack_level = (long)(&pg_dir)-current->kernel_stack_page;
if (stack_level < 1500) printk ("** %d ** ",stack_level);
pg_dir = PAGE_DIR_OFFSET(current, 0);
if (err == 0) {
int i;
for (i = 0 ; i < PTRS_PER_PAGE ; i++,page_dir++){
int notok = check_one_table(page_dir);
if (notok){
err++;
printk ("|%d:%08lx| ",i, page_dir->pgd);
}
}
if (err) printk ("\nErreur MM %d\n",err);
}
}

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.