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); |
} |
} |