home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
PC-Online 1996 May
/
PCOnline_05_1996.bin
/
linux
/
source
/
kernel-s
/
umsdos
/
umsdos-0.001
/
umsdos-0.3.diff
Wrap
Text File
|
1994-05-21
|
161KB
|
5,275 lines
diff -rc2P linux.1.1.12/config.in linux/config.in
*** linux.1.1.12/config.in Fri May 20 23:49:24 1994
--- linux/config.in Mon May 16 22:12:53 1994
***************
*** 113,116 ****
--- 113,117 ----
bool 'xiafs filesystem support' CONFIG_XIA_FS n
bool 'msdos fs support' CONFIG_MSDOS_FS y
+ bool 'umsdos: Unix like fs on top of std MSDOS FAT fs' CONFIG_UMSDOS_FS y
bool '/proc filesystem support' CONFIG_PROC_FS y
bool 'NFS filesystem support' CONFIG_NFS_FS y
diff -rc2P linux.1.1.12/fs/Makefile linux/fs/Makefile
*** linux.1.1.12/fs/Makefile Tue Dec 21 05:30:23 1993
--- linux/fs/Makefile Wed May 11 23:38:29 1994
***************
*** 34,37 ****
--- 34,40 ----
FS_SUBDIRS := $(FS_SUBDIRS) xiafs
endif
+ ifdef CONFIG_UMSDOS_FS
+ FS_SUBDIRS := $(FS_SUBDIRS) umsdos
+ endif
ifdef CONFIG_SYSV_FS
FS_SUBDIRS := $(FS_SUBDIRS) sysv
diff -rc2P linux.1.1.12/fs/filesystems.c linux/fs/filesystems.c
*** linux.1.1.12/fs/filesystems.c Fri May 20 23:49:07 1994
--- linux/fs/filesystems.c Wed May 11 23:38:29 1994
***************
*** 27,30 ****
--- 27,33 ----
{
static int callable = 1;
+ #ifdef CONFIG_UMSDOS_FS
+ #include <linux/umsdos_fs.h>
+ #endif
if (!callable)
***************
*** 52,55 ****
--- 55,62 ----
register_filesystem(&(struct file_system_type)
{xiafs_read_super, "xiafs", 1, NULL});
+ #endif
+ #ifdef CONFIG_UMSDOS_FS
+ register_filesystem(&(struct file_system_type)
+ {UMSDOS_read_super, "umsdos", 1, NULL});
#endif
diff -rc2P linux.1.1.12/fs/msdos/Makefile linux/fs/msdos/Makefile
*** linux.1.1.12/fs/msdos/Makefile Wed Dec 1 07:44:15 1993
--- linux/fs/msdos/Makefile Wed May 11 23:48:19 1994
***************
*** 15,19 ****
$(AS) -o $*.o $<
! OBJS= namei.o inode.o file.o dir.o misc.o fat.o
msdos.o: $(OBJS)
--- 15,19 ----
$(AS) -o $*.o $<
! OBJS= namei.o inode.o file.o dir.o misc.o fat.o mmap.o
msdos.o: $(OBJS)
diff -rc2P linux.1.1.12/fs/msdos/dir.c linux/fs/msdos/dir.c
*** linux.1.1.12/fs/msdos/dir.c Wed Dec 1 07:44:15 1993
--- linux/fs/msdos/dir.c Wed May 11 23:48:19 1994
***************
*** 13,16 ****
--- 13,17 ----
#include <linux/errno.h>
#include <linux/stat.h>
+ #include <linux/string.h>
***************
*** 20,24 ****
}
! static int msdos_readdir(struct inode *inode,struct file *filp,
struct dirent *dirent,int count);
--- 21,25 ----
}
! int msdos_readdir(struct inode *inode,struct file *filp,
struct dirent *dirent,int count);
***************
*** 55,60 ****
};
! static int msdos_readdir(struct inode *inode,struct file *filp,
! struct dirent *dirent,int count)
{
int ino,i,i2,last;
--- 56,65 ----
};
! static int msdos_readdir_x(
! struct inode *inode,
! struct file *filp,
! struct dirent *dirent,
! int count,
! int dirent_in_fs)
{
int ino,i,i2,last;
***************
*** 69,78 ****
else if (filp->f_pos < 2) {
walk = filp->f_pos++ ? ".." : ".";
! for (i = 0; *walk; walk++)
! put_fs_byte(*walk,dirent->d_name+i++);
! put_fs_long(MSDOS_ROOT_INO,&dirent->d_ino);
! put_fs_byte(0,dirent->d_name+i);
! put_fs_word(i,&dirent->d_reclen);
! return i;
}
}
--- 74,90 ----
else if (filp->f_pos < 2) {
walk = filp->f_pos++ ? ".." : ".";
! if (dirent_in_fs){
! for (i = 0; *walk; walk++)
! put_fs_byte(*walk,dirent->d_name+i++);
! put_fs_long(MSDOS_ROOT_INO,&dirent->d_ino);
! put_fs_byte(0,dirent->d_name+i);
! put_fs_word(i,&dirent->d_reclen);
! return i;
! }else{
! dirent->d_ino = MSDOS_ROOT_INO;
! strcpy (dirent->d_name,walk);
! dirent->d_reclen = filp->f_pos;
! return dirent->d_reclen;
! }
}
}
***************
*** 81,92 ****
while ((ino = msdos_get_entry(inode,&filp->f_pos,&bh,&de)) > -1) {
if (!IS_FREE(de->name) && !(de->attr & ATTR_VOLUME)) {
for (i = last = 0; i < 8; i++) {
if (!(c = de->name[i])) break;
if (c >= 'A' && c <= 'Z') c += 32;
! if (c != ' ') last = i+1;
! put_fs_byte(c,i+dirent->d_name);
}
i = last;
! put_fs_byte('.',i+dirent->d_name);
i++;
for (i2 = 0; i2 < 3; i2++) {
--- 93,108 ----
while ((ino = msdos_get_entry(inode,&filp->f_pos,&bh,&de)) > -1) {
if (!IS_FREE(de->name) && !(de->attr & ATTR_VOLUME)) {
+ char bufname[13];
+ char *ptname = bufname;
for (i = last = 0; i < 8; i++) {
if (!(c = de->name[i])) break;
if (c >= 'A' && c <= 'Z') c += 32;
! if (c != ' '){
! last = i+1;
! *ptname++ = c;
! }
}
i = last;
! *ptname++ = '.';
i++;
for (i2 = 0; i2 < 3; i2++) {
***************
*** 93,98 ****
if (!(c = de->ext[i2])) break;
if (c >= 'A' && c <= 'Z') c += 32;
! if (c != ' ') last = i+1;
! put_fs_byte(c,i+dirent->d_name);
i++;
}
--- 109,116 ----
if (!(c = de->ext[i2])) break;
if (c >= 'A' && c <= 'Z') c += 32;
! if (c != ' '){
! last = i+1;
! *ptname++ = c;
! }
i++;
}
***************
*** 102,108 ****
else if (!strcmp(de->name,MSDOS_DOTDOT))
ino = msdos_parent_ino(inode,0);
! put_fs_long(ino,&dirent->d_ino);
! put_fs_byte(0,i+dirent->d_name);
! put_fs_word(i,&dirent->d_reclen);
brelse(bh);
return i;
--- 120,133 ----
else if (!strcmp(de->name,MSDOS_DOTDOT))
ino = msdos_parent_ino(inode,0);
! bufname[i] = '\0';
! if (dirent_in_fs){
! put_fs_long(ino,&dirent->d_ino);
! memcpy_tofs(dirent->d_name,bufname,i+1);
! put_fs_word(i,&dirent->d_reclen);
! }else{
! dirent->d_ino = ino;
! memcpy (dirent->d_name,bufname,i+1);
! dirent->d_reclen = i;
! }
brelse(bh);
return i;
***************
*** 112,114 ****
--- 137,149 ----
if (bh) brelse(bh);
return 0;
+ }
+ int msdos_readdir(struct inode *inode,struct file *filp,
+ struct dirent *dirent,int count)
+ {
+ return msdos_readdir_x(inode,filp,dirent,count,1);
+ }
+ int msdos_readdir_kmem(struct inode *inode,struct file *filp,
+ struct dirent *dirent,int count)
+ {
+ return msdos_readdir_x(inode,filp,dirent,count,0);
}
diff -rc2P linux.1.1.12/fs/msdos/file.c linux/fs/msdos/file.c
*** linux.1.1.12/fs/msdos/file.c Fri May 20 23:48:37 1994
--- linux/fs/msdos/file.c Wed May 11 23:48:20 1994
***************
*** 16,19 ****
--- 16,20 ----
#include <linux/fcntl.h>
#include <linux/stat.h>
+ #include <linux/string.h>
#define MIN(a,b) (((a) < (b)) ? (a) : (b))
***************
*** 20,29 ****
#define MAX(a,b) (((a) > (b)) ? (a) : (b))
- static int msdos_file_read(struct inode *inode,struct file *filp,char *buf,
- int count);
- static int msdos_file_write(struct inode *inode,struct file *filp,char *buf,
- int count);
-
-
static struct file_operations msdos_file_operations = {
NULL, /* lseek - default */
--- 21,24 ----
***************
*** 33,37 ****
NULL, /* select - default */
NULL, /* ioctl - default */
! NULL, /* mmap */
NULL, /* no special open is needed */
NULL, /* release */
--- 28,32 ----
NULL, /* select - default */
NULL, /* ioctl - default */
! msdos_mmap, /* mmap */
NULL, /* no special open is needed */
NULL, /* release */
***************
*** 54,58 ****
msdos_bmap, /* bmap */
msdos_truncate, /* truncate */
! NULL /* permission */
};
--- 49,54 ----
msdos_bmap, /* bmap */
msdos_truncate, /* truncate */
! NULL, /* permission */
! msdos_smap /* smap */
};
***************
*** 74,83 ****
NULL, /* bmap */
msdos_truncate, /* truncate */
! NULL /* permission */
};
! static int msdos_file_read(struct inode *inode,struct file *filp,char *buf,
! int count)
{
char *start;
--- 70,87 ----
NULL, /* bmap */
msdos_truncate, /* truncate */
! NULL, /* permission */
! msdos_smap /* smap */
};
! /*
! Read a file into user space or kernel space
! */
! static int msdos_file_readx(
! struct inode *inode,
! struct file *filp,
! char *buf,
! int count,
! int from_fs)
{
char *start;
***************
*** 92,96 ****
return -EINVAL;
}
! if (!S_ISREG(inode->i_mode)) {
printk("msdos_file_read: mode = %07o\n",inode->i_mode);
return -EINVAL;
--- 96,101 ----
return -EINVAL;
}
! /* S_ISLNK allows for UMSDOS. Should never happen for normal MSDOS */
! if (!S_ISREG(inode->i_mode) && !S_ISLNK(inode->i_mode)) {
printk("msdos_file_read: mode = %07o\n",inode->i_mode);
return -EINVAL;
***************
*** 104,108 ****
if (!(bh = msdos_sread(inode->i_dev,sector,&data))) break;
filp->f_pos += (size = MIN(SECTOR_SIZE-offset,left));
! if (MSDOS_I(inode)->i_binary) {
memcpy_tofs(buf,data+offset,size);
buf += size;
--- 109,116 ----
if (!(bh = msdos_sread(inode->i_dev,sector,&data))) break;
filp->f_pos += (size = MIN(SECTOR_SIZE-offset,left));
! if (!from_fs){
! memcpy (buf,data+offset,size);
! buf += size;
! }else if (MSDOS_I(inode)->i_binary) {
memcpy_tofs(buf,data+offset,size);
buf += size;
***************
*** 132,139 ****
}
!
! static int msdos_file_write(struct inode *inode,struct file *filp,char *buf,
int count)
{
int sector,offset,size,left,written;
int error,carry;
--- 140,176 ----
}
! /*
! Read a file into user space memory
! */
! int msdos_file_read(
! struct inode *inode,
! struct file *filp,
! char *buf,
int count)
{
+ return msdos_file_readx(inode,filp,buf,count,1);
+ }
+ /*
+ Read a file into kernel space memory
+ */
+ int msdos_file_read_kmem(
+ struct inode *inode,
+ struct file *filp,
+ char *buf,
+ int count)
+ {
+ return msdos_file_readx(inode,filp,buf,count,0);
+ }
+
+ /*
+ Write to a file either from kernel memory or user space
+ */
+ static int msdos_file_writex(
+ struct inode *inode,
+ struct file *filp,
+ char *buf,
+ int count,
+ int from_fs)
+ {
int sector,offset,size,left,written;
int error,carry;
***************
*** 146,150 ****
return -EINVAL;
}
! if (!S_ISREG(inode->i_mode)) {
printk("msdos_file_write: mode = %07o\n",inode->i_mode);
return -EINVAL;
--- 183,188 ----
return -EINVAL;
}
! /* S_ISLNK allows for UMSDOS. Should never happen for normal MSDOS */
! if (!S_ISREG(inode->i_mode) && !S_ISLNK(inode->i_mode)) {
printk("msdos_file_write: mode = %07o\n",inode->i_mode);
return -EINVAL;
***************
*** 170,174 ****
break;
}
! if (MSDOS_I(inode)->i_binary) {
memcpy_fromfs(data+(filp->f_pos & (SECTOR_SIZE-1)),
buf,written = size);
--- 208,217 ----
break;
}
! if (!from_fs){
! /* Assume binary data when from kernel memory */
! memcpy(data+(filp->f_pos & (SECTOR_SIZE-1)),
! buf,written = size);
! buf += size;
! }else if (MSDOS_I(inode)->i_binary) {
memcpy_fromfs(data+(filp->f_pos & (SECTOR_SIZE-1)),
buf,written = size);
***************
*** 212,216 ****
}
!
void msdos_truncate(struct inode *inode)
{
--- 255,274 ----
}
! /*
! Write to a file from user space
! */
! int msdos_file_write(struct inode *inode,struct file *filp,char *buf,
! int count)
! {
! return msdos_file_writex(inode,filp,buf,count,1);
! }
! /*
! Write to a file from kernel space
! */
! int msdos_file_write_kmem(struct inode *inode,struct file *filp,char *buf,
! int count)
! {
! return msdos_file_writex(inode,filp,buf,count,0);
! }
void msdos_truncate(struct inode *inode)
{
diff -rc2P linux.1.1.12/fs/msdos/mmap.c linux/fs/msdos/mmap.c
*** linux.1.1.12/fs/msdos/mmap.c
--- linux/fs/msdos/mmap.c Wed May 11 23:48:23 1994
***************
*** 0 ****
--- 1,156 ----
+ /*
+ * fs/msdos/mmap.c
+ *
+ * Written by Jacques Gelinas (jacques@solucorp.qc.ca)
+ * Inspired by fs/nfs/mmap.c (Jaon Tombs 15 Aug 1993)
+ *
+ * msdos mmap handling
+ */
+ #include <linux/stat.h>
+ #include <linux/sched.h>
+ #include <linux/kernel.h>
+ #include <linux/mm.h>
+ #include <linux/shm.h>
+ #include <linux/errno.h>
+ #include <linux/mman.h>
+ #include <linux/string.h>
+ #include <linux/malloc.h>
+ #include <asm/segment.h>
+ #include <asm/system.h>
+ #include <linux/msdos_fs.h>
+
+ extern int share_page(struct vm_area_struct * area, struct task_struct * tsk,
+ struct inode * inode, unsigned long address, unsigned long error_code,
+ unsigned long newpage);
+
+ extern unsigned long put_page(struct task_struct * tsk,unsigned long page,
+ unsigned long address,int prot);
+
+ extern void file_mmap_free(struct vm_area_struct * area);
+ extern int file_mmap_share(struct vm_area_struct * from, struct vm_area_struct * to,
+ unsigned long address);
+
+ /*
+ Perform the mapping of an adresse in memory
+ */
+ static void msdos_file_mmap_nopage(
+ int error_code,
+ struct vm_area_struct * area,
+ unsigned long address)
+ {
+ struct inode * inode = area->vm_inode;
+ unsigned int clear;
+ unsigned long page;
+ int pos;
+ long gap; /* distance from eof to pos */
+
+ address &= PAGE_MASK;
+ pos = address - area->vm_start + area->vm_offset;
+
+ page = __get_free_page(GFP_KERNEL);
+ if (share_page(area, area->vm_task, inode, address, error_code, page)) {
+ ++area->vm_task->mm->min_flt;
+ return;
+ }
+
+ ++area->vm_task->mm->maj_flt;
+ if (!page) {
+ oom(current);
+ put_page(area->vm_task, BAD_PAGE, address, PAGE_PRIVATE);
+ return;
+ }
+
+ clear = 0;
+ gap = inode->i_size - pos;
+ if (gap <= 0){
+ /* mmaping beyong end of file */
+ clear = PAGE_SIZE;
+ }else{
+ int cur_read;
+ int need_read;
+ struct file filp;
+ if (gap < PAGE_SIZE){
+ clear = PAGE_SIZE - gap;
+ }
+ filp.f_pos = pos;
+ need_read = PAGE_SIZE - clear;
+ cur_read = msdos_file_read_kmem (inode,&filp,(char*)page,need_read);
+ if (cur_read != need_read){
+ printk ("MSDOS: Error while reading an mmap file %d <> %d\n"
+ ,cur_read,need_read);
+ free_page (page);
+ oom(current);
+ put_page(area->vm_task, BAD_PAGE, address, PAGE_PRIVATE);
+ return;
+ }
+ }
+ if (!(error_code & PAGE_RW)) {
+ if (share_page(area, area->vm_task, inode, address, error_code, page))
+ return;
+ }
+ if (clear > 0){
+ #if 1
+ memset ((char*)page+PAGE_SIZE-clear,0,clear);
+ #else
+ unsigned long tmp = page + PAGE_SIZE;
+ while (clear--) {
+ *(char *)--tmp = 0;
+ }
+ #endif
+ }
+ if (put_page(area->vm_task,page,address,area->vm_page_prot))
+ return;
+ free_page(page);
+ oom(current);
+ }
+ struct vm_operations_struct msdos_file_mmap = {
+ NULL, /* open */
+ file_mmap_free, /* close */
+ msdos_file_mmap_nopage,/* nopage */
+ NULL, /* wppage */
+ file_mmap_share, /* share */
+ NULL, /* unmap */
+ };
+ /*
+ Initialise a mmap operation on a file.
+ Return 0 if ok, or a negative error code if not.
+ */
+ int msdos_mmap(
+ struct inode * inode,
+ struct file * file,
+ unsigned long addr,
+ size_t len,
+ int prot,
+ unsigned long off)
+ {
+ struct vm_area_struct * mpnt;
+
+ if (prot & PAGE_RW) /* only PAGE_COW or read-only supported now */
+ return -EINVAL;
+ if (off & (inode->i_sb->s_blocksize - 1))
+ return -EINVAL;
+ if (!inode->i_sb || !S_ISREG(inode->i_mode))
+ return -EACCES;
+ if (!IS_RDONLY(inode)) {
+ inode->i_atime = CURRENT_TIME;
+ inode->i_dirt = 1;
+ }
+
+ mpnt = (struct vm_area_struct * ) kmalloc(sizeof(struct vm_area_struct), GFP_KERNEL);
+ if (!mpnt)
+ return -ENOMEM;
+
+ unmap_page_range(addr, len);
+ mpnt->vm_task = current;
+ mpnt->vm_start = addr;
+ mpnt->vm_end = addr + len;
+ mpnt->vm_page_prot = prot;
+ mpnt->vm_share = NULL;
+ mpnt->vm_inode = inode;
+ inode->i_count++;
+ mpnt->vm_offset = off;
+ mpnt->vm_ops = &msdos_file_mmap;
+ insert_vm_struct (current,mpnt);
+ merge_segments (current->mm->mmap,NULL,NULL);
+ return 0;
+ }
diff -rc2P linux.1.1.12/fs/msdos/namei.c linux/fs/msdos/namei.c
*** linux.1.1.12/fs/msdos/namei.c Fri May 20 23:48:37 1994
--- linux/fs/msdos/namei.c Wed May 11 23:48:24 1994
***************
*** 361,365 ****
! int msdos_unlink(struct inode *dir,const char *name,int len)
{
int res,ino;
--- 361,369 ----
! static int msdos_unlinkx(
! struct inode *dir,
! const char *name,
! int len,
! int nospc) /* Flag special file ? */
{
int res,ino;
***************
*** 376,380 ****
goto unlink_done;
}
! if (!S_ISREG(inode->i_mode)) {
res = -EPERM;
goto unlink_done;
--- 380,384 ----
goto unlink_done;
}
! if (!S_ISREG(inode->i_mode) && nospc){
res = -EPERM;
goto unlink_done;
***************
*** 393,396 ****
--- 397,411 ----
}
+ int msdos_unlink(struct inode *dir,const char *name,int len)
+ {
+ return msdos_unlinkx (dir,name,len,1);
+ }
+ /*
+ Special entry for umsdos
+ */
+ int msdos_unlink_umsdos(struct inode *dir,const char *name,int len)
+ {
+ return msdos_unlinkx (dir,name,len,0);
+ }
static int rename_same_dir(struct inode *old_dir,char *old_name,
diff -rc2P linux.1.1.12/fs/umsdos/Makefile linux/fs/umsdos/Makefile
*** linux.1.1.12/fs/umsdos/Makefile
--- linux/fs/umsdos/Makefile Wed May 11 23:47:27 1994
***************
*** 0 ****
--- 1,40 ----
+ #
+ # 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...
+
+ .c.s:
+ $(CC) $(CFLAGS) -S $<
+ .c.o:
+ $(CC) $(CFLAGS) -c $<
+ .s.o:
+ $(AS) -o $*.o $<
+
+ OBJS= dir.o emd.o file.o inode.o ioctl.o mangle.o namei.o\
+ rdir.o symlink.o #check.o
+
+ umsdos.o: $(OBJS)
+ $(LD) -r -o umsdos.o $(OBJS)
+
+ clean:
+ rm -f core *.o *.a *.s
+
+ dep:
+ $(CPP) -M *.c > .depend
+
+ p:
+ proto *.c >/usr/include/linux/umsdos_fs.p
+
+ doc:
+ nadoc -i -p umsdos.doc - /tmp/umsdos.mpg
+
+ #
+ # include a dependency file if one exists
+ #
+ ifeq (.depend,$(wildcard .depend))
+ include .depend
+ endif
diff -rc2P linux.1.1.12/fs/umsdos/check.c linux/fs/umsdos/check.c
*** linux.1.1.12/fs/umsdos/check.c
--- linux/fs/umsdos/check.c Wed May 11 23:47:27 1994
***************
*** 0 ****
--- 1,55 ----
+ #include <asm/system.h>
+
+ #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>
+
+ extern unsigned long high_memory;
+
+ static int check_one_table(unsigned long * page_dir)
+ {
+ unsigned long pg_table = *page_dir;
+
+ if (!pg_table)
+ return 0;
+ if (pg_table >= high_memory || !(pg_table & PAGE_PRESENT)) {
+ return 1;
+ }
+ return 0;
+ }
+
+ /*
+ * This function frees up all page tables of a process when it exits.
+ */
+ void check_page_tables(void)
+ {
+ unsigned long 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 = current->tss.cr3;
+ if (mem_map[MAP_NR(pg_dir)] > 1) {
+ return;
+ }
+ if (err == 0){
+ unsigned long *page_dir = (unsigned long *) pg_dir;
+ unsigned long *base = page_dir;
+ int i;
+ for (i = 0 ; i < PTRS_PER_PAGE ; i++,page_dir++){
+ int notok = check_one_table(page_dir);
+ if (notok){
+ err++;
+ printk ("|%d| ",page_dir-base);
+ }
+ }
+ if (err) printk ("Erreur MM %d\n",err);
+ }
+ }
+
diff -rc2P linux.1.1.12/fs/umsdos/dir.c linux/fs/umsdos/dir.c
*** linux.1.1.12/fs/umsdos/dir.c
--- linux/fs/umsdos/dir.c Thu May 12 00:29:23 1994
***************
*** 0 ****
--- 1,703 ----
+ /*
+ * linux/fs/umsdos/dir.c
+ *
+ * Written 1993 by Jacques Gelinas
+ * Inspired from linux/fs/msdos/... : Werner Almesberger
+ *
+ * Extended MS-DOS directory handling functions
+ */
+
+ #include <asm/segment.h>
+
+ #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>
+
+ #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;
+ }
+ /*
+ Read count directory entries from directory filp
+ Return a negative value from linux/errno.h.
+ Return > 0 if success (the length of the file name).
+
+ 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 */
+ struct dirent *dirent, /* Will hold count directory entry */
+ int dirent_in_fs, /* dirent point in user's space ? */
+ int count,
+ struct umsdos_dirent *u_entry, /* Optionnal umsdos entry */
+ int follow_hlink,
+ off_t *pt_f_pos) /* will hold the offset of the entry in EMD */
+ {
+ int ret = 0;
+
+ umsdos_startlookup(dir);
+ if (filp->f_pos == UMSDOS_SPECIAL_DIRFPOS
+ && dir == pseudo_root
+ && dirent_in_fs){
+ /*
+ 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.
+ */
+ put_fs_long(dir->i_sb->s_mounted->i_ino,&dirent->d_ino);
+ memcpy_tofs (dirent->d_name,"DOS",3);
+ put_fs_byte(0,dirent->d_name+3);
+ put_fs_word (3,&dirent->d_reclen);
+ if (u_entry != NULL) u_entry->flags = 0;
+ ret = 3;
+ 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.
+ */
+ ret = msdos_readdir(dir,filp,dirent,count);
+ if (filp->f_pos == 64) 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){
+ if (filp->f_pos <= UMSDOS_SPECIAL_DIRFPOS+1) filp->f_pos = 0;
+ PRINTK (("f_pos %ld i_size %d\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;
+ *pt_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){
+ PRINTK (("Trouve ino %d ",inode->i_ino));
+ if (dirent_in_fs){
+ put_fs_long(inode->i_ino,&dirent->d_ino);
+ memcpy_tofs (dirent->d_name,entry.name
+ ,entry.name_len);
+ put_fs_byte(0,dirent->d_name+entry.name_len);
+ put_fs_word (entry.name_len
+ ,&dirent->d_reclen);
+ /* In this case, the caller only needs */
+ /* flags */
+ if (u_entry != NULL){
+ u_entry->flags = entry.flags;
+ }
+ }else{
+ dirent->d_ino = inode->i_ino;
+ memcpy (dirent->d_name,entry.name
+ ,entry.name_len);
+ dirent->d_name[entry.name_len] = '\0';
+ dirent->d_reclen = entry.name_len;
+ if (u_entry != NULL) *u_entry = entry;
+ }
+ ret = entry.name_len;
+ 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;
+ }
+ }
+ }
+ }
+ iput(emd_dir);
+ }
+ }
+ umsdos_endlookup(dir);
+ PRINTK (("read dir %p pos %d 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 if success (the length of the file name).
+ */
+ 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 */
+ struct dirent *dirent, /* Will hold count directory entry */
+ int count)
+ {
+ int ret = -ENOENT;
+ while (1){
+ struct umsdos_dirent entry;
+ off_t f_pos;
+ ret = umsdos_readdir_x (dir,filp,dirent,1,count,&entry,1,&f_pos);
+ if (ret <= 0 || !(entry.flags & UMSDOS_HIDDEN)) break;
+ }
+ return 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 automaticly.
+ */
+ 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 = entry->rdev;
+ inode->i_atime = entry->atime;
+ inode->i_ctime = entry->ctime;
+ inode->i_mtime = entry->mtime;
+ inode->i_uid = entry->uid;
+ inode->i_gid = entry->gid;
+ /* #Specification: umsdos / i_nlink
+ The nlink field of an inode is 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");
+ }
+ }
+ /*
+ 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 file filp;
+ filp.f_pos = 0;
+ while (1){
+ struct dirent dirent;
+ if (msdos_readdir_kmem (dir,&filp,&dirent,1) <= 0){
+ printk ("UMSDOS: can't locate inode %ld in DOS directory???\n"
+ ,inode->i_ino);
+ }else if (dirent.d_ino == inode->i_ino){
+ ret = 0;
+ memcpy (entry->name,dirent.d_name,dirent.d_reclen);
+ entry->name[dirent.d_reclen] = '\0';
+ entry->name_len = dirent.d_reclen;
+ inode->u.umsdos_i.i_dir_owner = dir->i_ino;
+ inode->u.umsdos_i.i_emd_owner = 0;
+ umsdos_setup_dir_inode(inode);
+ break;
+ }
+ }
+ }else{
+ /* skip . and .. see umsdos_readdir_x() */
+ struct file filp;
+ filp.f_pos = UMSDOS_SPECIAL_DIRFPOS;
+ while (1){
+ struct dirent dirent;
+ off_t f_pos;
+ if (umsdos_readdir_x(dir,&filp,&dirent
+ ,0,1,entry,0,&f_pos) <= 0){
+ printk ("UMSDOS: can't locate inode %ld in EMD file???\n"
+ ,inode->i_ino);
+ break;
+ }else if (dirent.d_ino == inode->i_ino){
+ ret = 0;
+ umsdos_lookup_patch (dir,inode,entry,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 %x ",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 %d 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 %d 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 independantly 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_pos = 0;
+ PRINTK (("hlink2inode "));
+ if (msdos_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 = msdos_lookup(dir,start,len,result);
+ }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, /* bmap */
+ NULL, /* truncate */
+ NULL /* permission */
+ };
+
+
+
+
+
+
+
+
+
+
diff -rc2P linux.1.1.12/fs/umsdos/emd.c linux/fs/umsdos/emd.c
*** linux.1.1.12/fs/umsdos/emd.c
--- linux/fs/umsdos/emd.c Wed May 11 23:47:28 1994
***************
*** 0 ****
--- 1,454 ----
+ /*
+ * 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>
+
+ #define PRINTK(x)
+ #define Printk(x) printk x
+
+ /*
+ 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 = msdos_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.
+ Retourne 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 = msdos_file_read_kmem (emd_dir,filp,buf,count);
+ if (sizeread != count){
+ printk ("UMSDOS: problem with EMD file. Can't read\n");
+ ret = -EIO;
+ }
+ return ret;
+
+ }
+ /*
+ Locate the EMD file in a directory and optionnally, 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 [%d] "
+ ,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 corretion :-( ),
+ 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;
+ 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 (16*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 negativ 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;
+ 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_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;
+ }
+
diff -rc2P linux.1.1.12/fs/umsdos/file.c linux/fs/umsdos/file.c
*** linux.1.1.12/fs/umsdos/file.c
--- linux/fs/umsdos/file.c Wed May 11 23:47:28 1994
***************
*** 0 ****
--- 1,103 ----
+ /*
+ * linux/fs/umsdos/file.c
+ *
+ * Written 1993 by Jacques Gelinas
+ * inpired from linux/fs/msdos/file.c Werner Almesberger
+ *
+ * Extended MS-DOS regular file handling primitives
+ */
+
+ #include <asm/segment.h>
+ #include <asm/system.h>
+
+ #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>
+
+
+ #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 = msdos_file_read(inode,filp,buf,count);
+ 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,
+ char *buf,
+ int count)
+ {
+ return msdos_file_write(inode,filp,buf,count);
+ }
+ /*
+ Truncate a file to 0 length.
+ */
+ static void UMSDOS_truncate(struct inode *inode)
+ {
+ PRINTK (("UMSDOS_truncate\n"));
+ msdos_truncate (inode);
+ inode->i_ctime = inode->i_mtime = CURRENT_TIME;
+ inode->i_dirt = 1;
+ }
+ /*
+ See inode.c
+
+ Some entry point are filled dynamicly with function pointers
+ from the msdos file_operations and file_inode_operations.
+
+ The idea is to have the code as independant as possible from
+ the msdos file system.
+ */
+
+ 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 */
+ msdos_mmap, /* mmap */
+ NULL, /* no special open is needed */
+ NULL, /* release */
+ NULL /* 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 */
+ NULL, /* bmap */
+ UMSDOS_truncate,/* truncate */
+ NULL, /* permission */
+ msdos_smap /* smap */
+ };
+
+
diff -rc2P linux.1.1.12/fs/umsdos/inode.c linux/fs/umsdos/inode.c
*** linux.1.1.12/fs/umsdos/inode.c
--- linux/fs/umsdos/inode.c Thu May 12 09:44:21 1994
***************
*** 0 ****
--- 1,465 ----
+ /*
+ * linux/fs/umsdos/inode.c
+ *
+ * Written 1993 by Jacques Gelinas
+ * Inspired from linux/fs/msdos/... by Werner Almesberger
+ *
+ */
+
+ #include <stdlib.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/ctype.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));
+ msdos_put_inode(inode);
+ }
+
+
+ void UMSDOS_put_super(struct super_block *sb)
+ {
+ msdos_put_super(sb);
+ }
+
+
+ void UMSDOS_statfs(struct super_block *sb,struct statfs *buf)
+ {
+ msdos_statfs(sb,buf);
+ }
+
+
+ /*
+ Call msdos_lookup, but set back the original msdos function table.
+ Retourne 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.
+ Returne != 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 accomodate 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)){
+ static char is_init = 0;
+ if (!is_init){
+ /*
+ I don't want to change the msdos file system code
+ so I get the adress of some subroutine dynamicly
+ once.
+ */
+ umsdos_file_inode_operations.bmap = inode->i_op->bmap;
+ inode->i_op = &umsdos_file_inode_operations;
+ is_init = 1;
+ }
+ inode->i_op = &umsdos_file_inode_operations;
+ }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 %d\n",inode->i_ino,inode->i_count));
+ if (S_ISDIR(inode->i_mode)
+ && (inode->u.umsdos_i.u.dir_info.creating != 0
+ || inode->u.umsdos_i.u.dir_info.looking != 0
+ || inode->u.umsdos_i.u.dir_info.p != NULL)){
+ 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 completly 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)
+ {
+ PRINTK (("UMSDOS_write_inode emd %d\n",inode->u.umsdos_i.i_emd_owner));
+ msdos_write_inode(inode);
+ UMSDOS_notify_change (NOTIFY_TIME,inode);
+ }
+ int UMSDOS_notify_change (int flags, struct inode *inode)
+ {
+ int ret = 0;
+ 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;
+ 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 (flags & NOTIFY_UIDGID){
+ entry.uid = inode->i_uid;
+ entry.gid = inode->i_gid;
+ /* Remove those flags msdos don't like */
+ flags &= ~NOTIFY_UIDGID;
+ }
+ if (flags & NOTIFY_MODE){
+ entry.mode = inode->i_mode;
+ flags &= ~NOTIFY_MODE;
+ }
+ if (flags & NOTIFY_TIME){
+ entry.atime = inode->i_atime;
+ entry.mtime = inode->i_mtime;
+ entry.ctime = inode->i_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.
+ */
+ #if 0
+ if (ret == 0
+ && (S_ISDIR(inode->i_mode)
+ || S_ISREG(inode->i_mode))){
+ ret = msdos_notify_change(flags, inode);
+ printk ("msdos_notify %x %d",inode,ret);
+ }
+ #endif
+ }
+ iput (emd_owner);
+ }
+ PRINTK (("\n"));
+ }
+ }
+ 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 sens 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 = msdos_read_super(s,data,silent);
+ printk ("UMSDOS Alpha %d.%d\n",UMSDOS_VERSION,UMSDOS_RELEASE);
+ if (sb != NULL){
+ 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.
+ 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 *rc = NULL;
+ 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;
+ 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))){
+ umsdos_setup_dir_inode (pseudo);
+ Printk (("Activating pseudo root /%s\n",UMSDOS_PSDROOT_NAME));
+ pseudo_root = pseudo;
+ pseudo->i_count++;
+ pseudo = NULL;
+ }
+ iput (init);
+ iput (rc);
+ }
+ iput (etc);
+ }
+ iput (pseudo);
+ }
+ }
+ return sb;
+ }
+
+
diff -rc2P linux.1.1.12/fs/umsdos/ioctl.c linux/fs/umsdos/ioctl.c
*** linux.1.1.12/fs/umsdos/ioctl.c
--- linux/fs/umsdos/ioctl.c Wed May 11 23:47:30 1994
***************
*** 0 ****
--- 1,259 ----
+ /*
+ * 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/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
+
+ /*
+ 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;
+ /* #Specification: ioctl / acces
+ Only root (effective id) is allowed to do IOCTL on directory
+ in UMSDOS. EPERM is returned for other user.
+ */
+ 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 independantly. These are essential to implement
+ the synchroniser.
+ */
+ 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.
+ */
+ ret = msdos_readdir(dir,filp,&idata->dos_dirent,1);
+ }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_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 independant (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.fs_uid = data.umsdos_dirent.uid;
+ dir->i_sb->u.msdos_sb.fs_gid = data.umsdos_dirent.gid;
+ dir->i_sb->u.msdos_sb.fs_umask = data.umsdos_dirent.mode;
+ ret = 0;
+ }
+ }
+ }
+ PRINTK (("ioctl return %d\n",ret));
+ return ret;
+ }
+
+
+
diff -rc2P linux.1.1.12/fs/umsdos/mangle.c linux/fs/umsdos/mangle.c
*** linux.1.1.12/fs/umsdos/mangle.c
--- linux/fs/umsdos/mangle.c Thu May 12 07:10:16 1994
***************
*** 0 ****
--- 1,478 ----
+ /*
+ * 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/ctype.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 unchange,
+ 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 absolutly 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 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 wich 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 lenght is the lenght.
+ */
+ 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 */
+ strcpy (info->fake.fname,fname); /* GLU C'est sur on a un 0 a la fin */
+ 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 "Colision",
+ par exemple: entre "HELLO" et "Hello", mais ces problemes
+ peuvent etre traiter ailleur avec les autres colisions.
+ */
+ "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
diff -rc2P linux.1.1.12/fs/umsdos/namei.c linux/fs/umsdos/namei.c
*** linux.1.1.12/fs/umsdos/namei.c
--- linux/fs/umsdos/namei.c Fri May 20 23:30:18 1994
***************
*** 0 ****
--- 1,992 ----
+ /*
+ * 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 recursivly 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++;
+ }
+ void check_page_tables(void);
+
+ /*
+ 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 successfull, 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->euid;
+ info.entry.gid = (dir->i_mode & S_ISGID)
+ ? dir->i_gid : current->egid;
+ 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[%d] ",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;
+ }
+ /*
+ 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);
+ PRINTK (("umsdos_rename %d %d ",old_ret,new_ret));
+ if (old_ret == 0 && new_ret == 0){
+ umsdos_lockcreate2(old_dir,new_dir);
+ PRINTK (("old findentry "));
+ ret = umsdos_findentry(old_dir,&old_info,0);
+ PRINTK (("ret %d ",ret));
+ if (ret == 0){
+ PRINTK (("new newentry "));
+ umsdos_ren_init(&new_info,&old_info,flags);
+ ret = umsdos_newentry (new_dir,&new_info);
+ PRINTK (("ret %d %d ",ret,new_info.fake.len));
+ 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.
+ */
+ /* This is not super efficient but should work */
+ new_dir->i_count++;
+ ret = UMSDOS_unlink (new_dir,new_name,new_len);
+ 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);
+ PRINTK (("rename rmdir ret %d -- ",ret));
+ }
+ if (ret == 0){
+ /*
+ The last umsdos_newentry had a side effect.
+ new_info was filled with information from the
+ existing entry (the one we just deleted).
+ */
+ umsdos_parse (new_name,new_len,&new_info);
+ umsdos_ren_init(&new_info,&old_info,flags);
+ ret = umsdos_newentry (new_dir,&new_info);
+ }
+ }
+ 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);
+ PRINTK (("after m_rename ret %d ",ret));
+ if (ret != 0){
+ umsdos_delentry (new_dir,&new_info
+ ,S_ISDIR(new_info.entry.mode));
+ }else{
+ ret = umsdos_delentry (old_dir,&old_info
+ ,S_ISDIR(old_info.entry.mode));
+ 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);
+ 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);
+ iput (inode);
+ }
+ }
+ }
+ }
+ }
+ 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.
+
+ Lets 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 = msdos_file_write_kmem (inode,&filp,(char*)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 functionnality 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 symbolicly (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 ou /tmp/file2 independantly.
+
+ 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 [%d] "
+ ,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
+ simbolic 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[%d] ",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[%d] ",olddir->i_count));
+ ret = umsdos_locate_path (oldinode,path);
+ PRINTK (("olddir[%d] ",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){
+ oldinode->i_nlink++;
+ ret = UMSDOS_notify_change (0,oldinode);
+ }
+ 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 successfull, 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->euid;
+ info.entry.gid = (dir->i_mode & S_ISGID)
+ ? dir->i_gid : current->egid;
+ 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 immediatly 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 thoses 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 independant 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){
+ // Complexe 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
+ paire 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 ((empty = umsdos_isempty (sdir)) != 0){
+ PRINTK (("isempty %d i_count %d ",empty,sdir->i_count));
+ 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{
+ /*
+ 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)
+ {
+ struct umsdos_info info;
+ int ret = umsdos_nevercreat(dir,name,len,-EPERM);
+ if (ret == 0){
+ 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));
+ 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 substract 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{
+ ret = UMSDOS_notify_change (0,inode);
+ }
+ 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));
+ }
+ }
+ }
+ 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)
+ {
+ /* #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){
+ ret = umsdos_rename_f (old_dir,old_name,old_len,new_dir,new_name
+ ,new_len,0);
+ }
+ return ret;
+ }
+
diff -rc2P linux.1.1.12/fs/umsdos/notes linux/fs/umsdos/notes
*** linux.1.1.12/fs/umsdos/notes
--- linux/fs/umsdos/notes Wed May 11 23:47:33 1994
***************
*** 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
+
+
diff -rc2P linux.1.1.12/fs/umsdos/rdir.c linux/fs/umsdos/rdir.c
*** linux.1.1.12/fs/umsdos/rdir.c
--- linux/fs/umsdos/rdir.c Wed May 11 23:47:33 1994
***************
*** 0 ****
--- 1,239 ----
+ /*
+ * 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 <asm/segment.h>
+
+ #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>
+
+ #define PRINTK(x)
+ #define Printk(x) printk x
+
+
+ extern struct inode *pseudo_root;
+
+ static int UMSDOS_rreaddir (
+ struct inode *dir,
+ struct file *filp,
+ struct dirent *dirent,
+ int count)
+ {
+ int ret = 0;
+ while (1){
+ ret = msdos_readdir(dir,filp,dirent,count);
+ if (ret == 5
+ && pseudo_root != NULL
+ && dir->i_sb->s_mounted == pseudo_root->i_sb->s_mounted){
+ /*
+ In pseudo root mode, we must eliminate logically
+ the directory linux from the real root.
+ */
+ char name[5];
+ memcpy_fromfs (name,dirent->d_name,5);
+ if (memcmp(name,UMSDOS_PSDROOT_NAME,UMSDOS_PSDROOT_LEN)!=0) break;
+ }else{
+ if (pseudo_root != NULL
+ && ret == 2
+ && dir == dir->i_sb->s_mounted
+ && dir == pseudo_root->i_sb->s_mounted){
+ char name[2];
+ memcpy_fromfs (name,dirent->d_name,2);
+ if (name[0] == '.' && name[1] == '.'){
+ put_fs_long (pseudo_root->i_ino,&dirent->d_ino);
+ }
+ }
+ break;
+ }
+ }
+ return ret;
+ }
+
+ int UMSDOS_rlookup(
+ struct inode *dir,
+ const char *name,
+ int len,
+ struct inode **result) /* Will hold inode of the file, if successful */
+ {
+ 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){
+ /* #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;
+ }
+
+ 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 %d ",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, /* bmap */
+ NULL, /* truncate */
+ NULL /* permission */
+ };
+
+
diff -rc2P linux.1.1.12/fs/umsdos/symlink.c linux/fs/umsdos/symlink.c
*** linux.1.1.12/fs/umsdos/symlink.c
--- linux/fs/umsdos/symlink.c Thu May 12 00:21:54 1994
***************
*** 0 ****
--- 1,143 ----
+ /*
+ * linux/fs/umsdos/file.c
+ *
+ * Written 1992 by Jacques Gelinas
+ * inpired from linux/fs/msdos/file.c Werner Almesberger
+ *
+ * Extended MS-DOS regular file handling primitives
+ */
+
+ #include <asm/segment.h>
+ #include <asm/system.h>
+
+ #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>
+
+ #define PRINTK(x)
+ #define Printk(x) printk x
+ /*
+ Read the data associate with the symlink.
+ Return lenght 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;
+ 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 recursivly
+ 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,msdos_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,msdos_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, /* bmap */
+ NULL, /* truncate */
+ NULL /* permission */
+ };
+
+
+
diff -rc2P linux.1.1.12/include/linux/fs.h linux/include/linux/fs.h
*** linux.1.1.12/include/linux/fs.h Fri May 20 23:49:07 1994
--- linux/include/linux/fs.h Wed May 11 23:48:43 1994
***************
*** 158,161 ****
--- 158,162 ----
#include <linux/hpfs_fs_i.h>
#include <linux/msdos_fs_i.h>
+ #include <linux/umsdos_fs_i.h>
#include <linux/iso_fs_i.h>
#include <linux/nfs_fs_i.h>
***************
*** 202,205 ****
--- 203,207 ----
struct hpfs_inode_info hpfs_i;
struct msdos_inode_info msdos_i;
+ struct umsdos_inode_info umsdos_i;
struct iso_inode_info isofs_i;
struct nfs_inode_info nfs_i;
***************
*** 300,303 ****
--- 302,306 ----
void (*truncate) (struct inode *);
int (*permission) (struct inode *, int);
+ int (*smap) (struct inode *,int);
};
diff -rc2P linux.1.1.12/include/linux/msdos_fs.h linux/include/linux/msdos_fs.h
*** linux.1.1.12/include/linux/msdos_fs.h Wed Dec 1 07:44:15 1993
--- linux/include/linux/msdos_fs.h Wed May 11 23:48:51 1994
***************
*** 162,165 ****
--- 162,166 ----
extern int msdos_rmdir(struct inode *dir,const char *name,int len);
extern int msdos_unlink(struct inode *dir,const char *name,int len);
+ extern int msdos_unlink_umsdos(struct inode *dir,const char *name,int len);
extern int msdos_rename(struct inode *old_dir,const char *old_name,int old_len,
struct inode *new_dir,const char *new_name,int new_len);
***************
*** 180,190 ****
extern struct inode_operations msdos_dir_inode_operations;
!
/* file.c */
extern struct inode_operations msdos_file_inode_operations;
extern struct inode_operations msdos_file_inode_operations_no_bmap;
extern void msdos_truncate(struct inode *inode);
#endif
--- 181,202 ----
extern struct inode_operations msdos_dir_inode_operations;
! extern int msdos_readdir (struct inode *inode, struct file *filp,
! struct dirent *dirent, int count);
! extern int msdos_readdir_kmem (struct inode *inode, struct file *filp,
! struct dirent *dirent, int count);
/* file.c */
extern struct inode_operations msdos_file_inode_operations;
+ extern int msdos_file_read_kmem(struct inode *, struct file *, char *, int);
+ extern int msdos_file_read(struct inode *, struct file *, char *, int);
+ extern int msdos_file_write_kmem(struct inode *, struct file *, char *, int);
+ extern int msdos_file_write(struct inode *, struct file *, char *, int);
extern struct inode_operations msdos_file_inode_operations_no_bmap;
extern void msdos_truncate(struct inode *inode);
+
+ /* mmap.c */
+ extern int msdos_mmap (struct inode *, struct file *, unsigned long, size_t
+ ,int , unsigned long);
#endif
diff -rc2P linux.1.1.12/include/linux/msdos_fs_i.h linux/include/linux/msdos_fs_i.h
*** linux.1.1.12/include/linux/msdos_fs_i.h Wed Dec 1 07:44:15 1993
--- linux/include/linux/msdos_fs_i.h Wed May 11 23:48:51 1994
***************
*** 2,5 ****
--- 2,12 ----
#define _MSDOS_FS_I
+ #ifndef _LINUX_CONFIG_H
+ #include <linux/config.h>
+ #endif
+ #ifndef _LINUX_PIPE_FS_I_H
+ #include <linux/pipe_fs_i.h>
+ #endif
+
/*
* MS-DOS file system inode data in memory
***************
*** 7,10 ****
--- 14,32 ----
struct msdos_inode_info {
+ /*
+ UMSDOS manage special file and fifo as normal empty
+ msdos file. fifo inode processing conflict with msdos
+ processing. So I insert the pipe_inode_info so the
+ information does not overlap. This increases the size of
+ the msdos_inode_info, but the clear winner here is
+ the ext2_inode_info. So it does not change anything to
+ the total size of a struct inode.
+
+ I have not put it conditionnal. With the advent of loadable
+ file system drivers, it would be very easy to compile
+ a MsDOS FS driver unaware of UMSDOS and then later to
+ load a (then incompatible) UMSDOS FS driver.
+ */
+ struct pipe_inode_info reserved;
int i_start; /* first cluster or 0 */
int i_attrs; /* unused attribute bits */
diff -rc2P linux.1.1.12/include/linux/umsdos_fs.h linux/include/linux/umsdos_fs.h
*** linux.1.1.12/include/linux/umsdos_fs.h
--- linux/include/linux/umsdos_fs.h Wed May 11 23:54:05 1994
***************
*** 0 ****
--- 1,105 ----
+ #ifndef LINUX_UMSDOS_FS_H
+ #define LINUX_UMSDOS_FS_H
+
+ #define UMSDOS_VERSION 0
+ #define UMSDOS_RELEASE 3
+
+ #ifndef LINUX_FS_H
+ #include <linux/fs.h>
+ #endif
+
+ /* This is the file acting as a directory extension */
+ #define UMSDOS_EMD_FILE "--linux-.---"
+ #define UMSDOS_EMD_NAMELEN 12
+ #define UMSDOS_PSDROOT_NAME "linux"
+ #define UMSDOS_PSDROOT_LEN 5
+
+ struct umsdos_fake_info {
+ char fname[13];
+ int len;
+ };
+
+ #define UMSDOS_MAXNAME 220
+ /* This structure is 256 bytes large, depending on the name, only part */
+ /* of it is written to disk */
+ struct umsdos_dirent {
+ unsigned char name_len; /* if == 0, then this entry is not used */
+ unsigned char flags; /* UMSDOS_xxxx */
+ unsigned short nlink; /* How many hard links point to this entry */
+ uid_t uid; /* Owner user id */
+ gid_t gid; /* Group id */
+ time_t atime; /* Access time */
+ time_t mtime; /* Last modification time */
+ time_t ctime; /* Creation time */
+ dev_t rdev; /* major and minor number of a device */
+ /* special file */
+ umode_t mode; /* Standard UNIX permissions bits + type of */
+ char spare[12]; /* unused bytes for future extensions */
+ /* file, see linux/stat.h */
+ char name[UMSDOS_MAXNAME]; /* Not '\0' terminated */
+ /* but '\0' padded, so it will allow */
+ /* for adding news fields in this record */
+ /* by reducing the size of name[] */
+ };
+ #define UMSDOS_HIDDEN 1 /* Never show this entry in directory search */
+ #define UMSDOS_HLINK 2 /* It is a (pseudo) hard link */
+
+ /* #Specification: EMD file / record size
+ Entry are 64 bytes wide in the EMD file. It allows for a 30 characters
+ name. If a name is longer, contiguous entries are allocated. So a
+ umsdos_dirent may span multiple records.
+ */
+ #define UMSDOS_REC_SIZE 64
+
+ /* Translation between MSDOS name and UMSDOS name */
+ struct umsdos_info{
+ int msdos_reject; /* Tell if the file name is invalid for MSDOS */
+ /* See umsdos_parse */
+ struct umsdos_fake_info fake;
+ struct umsdos_dirent entry;
+ off_t f_pos; /* offset of the entry in the EMD file */
+ /* or offset where the entry may be store */
+ /* if it is a new entry */
+ int recsize; /* Record size needed to store entry */
+ };
+
+ /* Definitions for ioctl (number randomly chosen) */
+ /* The next ioctl commands operate only on the DOS directory */
+ /* The file umsdos_progs/umsdosio.c contain a string table */
+ /* based on the order of those definition. Keep it in sync */
+ #define UMSDOS_READDIR_DOS 1234 /* Do a readdir of the DOS directory */
+ #define UMSDOS_UNLINK_DOS 1235 /* Erase in the DOS directory only */
+ #define UMSDOS_RMDIR_DOS 1236 /* rmdir in the DOS directory only */
+ #define UMSDOS_STAT_DOS 1237 /* Get info about a file */
+ /* The next ioctl commands operate only on the EMD file */
+ #define UMSDOS_CREAT_EMD 1238 /* Create a file */
+ #define UMSDOS_UNLINK_EMD 1239 /* unlink (rmdir) a file */
+ #define UMSDOS_READDIR_EMD 1240 /* read the EMD file only. */
+ #define UMSDOS_GETVERSION 1241 /* Get the release number of UMSDOS */
+ #define UMSDOS_INIT_EMD 1242 /* Create the EMD file if not there */
+ #define UMSDOS_DOS_SETUP 1243 /* Set the defaults of the MsDOS driver */
+
+ #ifndef _SYS_STAT_H
+ #include <sys/stat.h>
+ #endif
+
+ struct umsdos_ioctl{
+ struct dirent dos_dirent;
+ struct umsdos_dirent umsdos_dirent;
+ struct stat stat;
+ char version,release;
+ };
+
+ /* Different macros to access struct umsdos_dirent */
+ #define EDM_ENTRY_ISUSED(e) ((e)->name_len!=0)
+
+ extern struct inode_operations umsdos_dir_inode_operations;
+ extern struct file_operations umsdos_file_operations;
+ extern struct inode_operations umsdos_file_inode_operations;
+ extern struct inode_operations umsdos_file_inode_operations_no_bmap;
+ extern struct inode_operations umsdos_symlink_inode_operations;
+
+
+ #include <linux/umsdos_fs.p>
+
+ #endif
diff -rc2P linux.1.1.12/include/linux/umsdos_fs.p linux/include/linux/umsdos_fs.p
*** linux.1.1.12/include/linux/umsdos_fs.p
--- linux/include/linux/umsdos_fs.p Wed May 11 23:49:37 1994
***************
*** 0 ****
--- 1,122 ----
+ /* check.c 05/02/94 15.36.10 */
+ void check_page_tables (void);
+ /* dir.c 24/04/94 00.00.40 */
+ int UMSDOS_dir_read (struct inode *inode,
+ struct file *filp,
+ char *buf,
+ int count);
+ void umsdos_lookup_patch (struct inode *dir,
+ struct inode *inode,
+ struct umsdos_dirent *entry,
+ off_t emd_pos);
+ int umsdos_inode2entry (struct inode *dir,
+ struct inode *inode,
+ struct umsdos_dirent *entry);
+ int umsdos_locate_path (struct inode *inode, char *path);
+ int umsdos_is_pseudodos (struct inode *dir, const char *name, int len);
+ int UMSDOS_lookup (struct inode *dir,
+ const char *name,
+ int len,
+ struct inode **result);
+ int umsdos_hlink2inode (struct inode *hlink, struct inode **result);
+ /* emd.c 05/02/94 15.36.10 */
+ int umsdos_emd_dir_write (struct inode *emd_dir,
+ struct file *filp,
+ char *buf,
+ int count);
+ int umsdos_emd_dir_read (struct inode *emd_dir,
+ struct file *filp,
+ char *buf,
+ int count);
+ struct inode *umsdos_emd_dir_lookup (struct inode *dir, int creat);
+ int umsdos_emd_dir_readentry (struct inode *emd_dir,
+ struct file *filp,
+ struct umsdos_dirent *entry);
+ int umsdos_writeentry (struct inode *dir,
+ struct inode *emd_dir,
+ struct umsdos_info *info,
+ int free_entry);
+ int umsdos_newentry (struct inode *dir, struct umsdos_info *info);
+ int umsdos_newhidden (struct inode *dir, struct umsdos_info *info);
+ int umsdos_delentry (struct inode *dir,
+ struct umsdos_info *info,
+ int isdir);
+ int umsdos_isempty (struct inode *dir);
+ int umsdos_findentry (struct inode *dir,
+ struct umsdos_info *info,
+ int expect);
+ /* file.c 05/02/94 15.36.10 */
+ /* inode.c 23/04/94 23.57.54 */
+ void UMSDOS_put_inode (struct inode *inode);
+ void UMSDOS_put_super (struct super_block *sb);
+ void UMSDOS_statfs (struct super_block *sb, struct statfs *buf);
+ int umsdos_real_lookup (struct inode *dir,
+ const char *name,
+ int len,
+ struct inode **result);
+ void umsdos_setup_dir_inode (struct inode *inode);
+ void umsdos_set_dirinfo (struct inode *inode,
+ struct inode *dir,
+ off_t f_pos);
+ int umsdos_isinit (struct inode *inode);
+ void umsdos_patch_inode (struct inode *inode,
+ struct inode *dir,
+ off_t f_pos);
+ int umsdos_get_dirowner (struct inode *inode, struct inode **result);
+ void UMSDOS_read_inode (struct inode *inode);
+ void UMSDOS_write_inode (struct inode *inode);
+ int UMSDOS_notify_change (int flags, struct inode *inode);
+ struct super_block *UMSDOS_read_super (struct super_block *s,
+ void *data,
+ int silent);
+ /* ioctl.c 05/02/94 15.36.10 */
+ int UMSDOS_ioctl_dir (struct inode *dir,
+ struct file *filp,
+ unsigned int cmd,
+ unsigned long data);
+ /* mangle.c 05/02/94 15.36.10 */
+ int printk (const char *msg, ...);
+ void umsdos_manglename (struct umsdos_info *info);
+ int umsdos_evalrecsize (int len);
+ int umsdos_parse (const char *fname, int len, struct umsdos_info *info);
+ /* namei.c 06/04/94 18.24.58 */
+ void umsdos_lockcreate (struct inode *dir);
+ void umsdos_startlookup (struct inode *dir);
+ void umsdos_unlockcreate (struct inode *dir);
+ void umsdos_endlookup (struct inode *dir);
+ int UMSDOS_symlink (struct inode *dir,
+ const char *name,
+ int len,
+ const char *symname);
+ int UMSDOS_link (struct inode *oldinode,
+ struct inode *dir,
+ const char *name,
+ int len);
+ int UMSDOS_create (struct inode *dir,
+ const char *name,
+ int len,
+ int mode,
+ struct inode **result);
+ int UMSDOS_mkdir (struct inode *dir,
+ const char *name,
+ int len,
+ int mode);
+ int UMSDOS_mknod (struct inode *dir,
+ const char *name,
+ int len,
+ int mode,
+ int rdev);
+ int UMSDOS_rmdir (struct inode *dir, const char *name, int len);
+ int UMSDOS_unlink (struct inode *dir, const char *name, int len);
+ 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);
+ /* rdir.c 23/04/94 10.51.44 */
+ int UMSDOS_rlookup (struct inode *dir,
+ const char *name,
+ int len,
+ struct inode **result);
+ /* symlink.c 05/02/94 15.36.10 */
diff -rc2P linux.1.1.12/include/linux/umsdos_fs_i.h linux/include/linux/umsdos_fs_i.h
*** linux.1.1.12/include/linux/umsdos_fs_i.h
--- linux/include/linux/umsdos_fs_i.h Wed May 11 23:49:38 1994
***************
*** 0 ****
--- 1,140 ----
+ #ifndef UMSDOS_FS_I_H
+ #define UMSDOS_FS_I_H
+
+ #ifndef _LINUX_TYPES_H
+ #include <linux/types.h>
+ #endif
+ #include <linux/msdos_fs_i.h>
+ #include <linux/pipe_fs_i.h>
+
+ /* #Specification: strategy / in memory inode
+ Here is the information specific to the inode of the UMSDOS file
+ system. This information is added to the end of the standard struct
+ inode. Each file system has its own extension to struct inode,
+ so do the umsdos file system.
+
+ The strategy is to have the umsdos_inode_info as a superset of
+ the msdos_inode_info, since most of the time the job is done
+ by the msdos fs code.
+
+ So we duplicate the msdos_inode_info, and add our own info at the
+ end.
+
+ For all file type (and directory) the inode has a reference to:
+ the directory which hold this entry: i_dir_owner
+ The EMD file of i_dir_owner: i_emd_owner
+ The offset in this EMD file of the entry: pos
+
+ For directory, we also have a reference to the inode of its
+ own EMD file. Also, we have dir_locking_info to help synchronise
+ file creation and file lookup. This data is sharing space with
+ the pipe_inode_info not used by directory. See also msdos_fs_i.h
+ for more information about pipe_inode_info and msdos_inode_info.
+
+ Special file and fifo do have an inode which correspond to an
+ empty MSDOS file.
+
+ symlink are processed mostly like regular file. The content is the
+ link.
+
+ fifos add there own extension to the inode. I have reserved some
+ space for fifos side by side with msdos_inode_info. This is just
+ to for the show, because msdos_inode_info already include the
+ pipe_inode_info.
+
+ The UMSDOS specific extension is placed after the union.
+ */
+ struct dir_locking_info {
+ struct wait_queue *p;
+ short int looking; /* How many process doing a lookup */
+ short int creating; /* Is there any creation going on here */
+ /* Only one at a time, although one */
+ /* may recursivly lock, so it is a counter */
+ long pid; /* pid of the process owning the creation */
+ /* lock */
+ };
+ struct umsdos_inode_info {
+ union {
+ struct msdos_inode_info msdos_info;
+ struct pipe_inode_info pipe_info;
+ struct dir_locking_info dir_info;
+ }u; /* Simply a filler, never referenced by fs/umsdos/... */
+ unsigned long i_dir_owner; /* Inode of the dir which hold this */
+ /* entry */
+ unsigned long i_emd_owner; /* Inode of the EMD file of i_dir_owner */
+ off_t pos; /* Entry offset in the emd_owner file */
+ /* The rest is used only if this inode describe a directory */
+ unsigned long i_emd_dir; /* Inode of the EMD file of this inode */
+ };
+
+ #endif
+ #ifndef UMSDOS_FS_I_H
+ #define UMSDOS_FS_I_H
+
+ #ifndef _LINUX_TYPES_H
+ #include <linux/types.h>
+ #endif
+ #include <linux/msdos_fs_i.h>
+ #include <linux/pipe_fs_i.h>
+
+ /* #Specification: strategy / in memory inode
+ Here is the information specific to the inode of the UMSDOS file
+ system. This information is added to the end of the standard struct
+ inode. Each file system has its own extension to struct inode,
+ so do the umsdos file system.
+
+ The strategy is to have the umsdos_inode_info as a superset of
+ the msdos_inode_info, since most of the time the job is done
+ by the msdos fs code.
+
+ So we duplicate the msdos_inode_info, and add our own info at the
+ end.
+
+ For all file type (and directory) the inode has a reference to:
+ the directory which hold this entry: i_dir_owner
+ The EMD file of i_dir_owner: i_emd_owner
+ The offset in this EMD file of the entry: pos
+
+ For directory, we also have a reference to the inode of its
+ own EMD file. Also, we have dir_locking_info to help synchronise
+ file creation and file lookup. This data is sharing space with
+ the pipe_inode_info not used by directory. See also msdos_fs_i.h
+ for more information about pipe_inode_info and msdos_inode_info.
+
+ Special file and fifo do have an inode which correspond to an
+ empty MSDOS file.
+
+ symlink are processed mostly like regular file. The content is the
+ link.
+
+ fifos add there own extension to the inode. I have reserved some
+ space for fifos side by side with msdos_inode_info. This is just
+ to for the show, because msdos_inode_info already include the
+ pipe_inode_info.
+
+ The UMSDOS specific extension is placed after the union.
+ */
+ struct dir_locking_info {
+ struct wait_queue *p;
+ short int looking; /* How many process doing a lookup */
+ short int creating; /* Is there any creation going on here */
+ /* Only one at a time, although one */
+ /* may recursivly lock, so it is a counter */
+ long pid; /* pid of the process owning the creation */
+ /* lock */
+ };
+ struct umsdos_inode_info {
+ union {
+ struct msdos_inode_info msdos_info;
+ struct pipe_inode_info pipe_info;
+ struct dir_locking_info dir_info;
+ }u; /* Simply a filler, never referenced by fs/umsdos/... */
+ unsigned long i_dir_owner; /* Inode of the dir which hold this */
+ /* entry */
+ unsigned long i_emd_owner; /* Inode of the EMD file of i_dir_owner */
+ off_t pos; /* Entry offset in the emd_owner file */
+ /* The rest is used only if this inode describe a directory */
+ unsigned long i_emd_dir; /* Inode of the EMD file of this inode */
+ };
+
+ #endif
diff -rc2P linux.1.1.12/init/main.c linux/init/main.c
*** linux.1.1.12/init/main.c Fri May 20 23:49:25 1994
--- linux/init/main.c Wed May 11 23:38:32 1994
***************
*** 495,498 ****
--- 495,514 ----
setup();
sprintf(term, "TERM=con%dx%d", ORIG_VIDEO_COLS, ORIG_VIDEO_LINES);
+
+ #ifdef CONFIG_UMSDOS_FS
+ {
+ /*
+ When mounting a umsdos fs as root, we detect
+ the pseudo_root (/linux) and initialise it here.
+ pseudo_root is defined in fs/umsdos/inode.c
+ */
+ extern struct inode *pseudo_root;
+ if (pseudo_root != NULL){
+ current->fs->root = pseudo_root;
+ current->fs->pwd = pseudo_root;
+ }
+ }
+ #endif
+
(void) open("/dev/tty1",O_RDWR,0);
(void) dup(0);
diff -rc2P linux.1.1.12/mm/swap.c linux/mm/swap.c
*** linux.1.1.12/mm/swap.c Fri May 20 23:49:21 1994
--- linux/mm/swap.c Wed May 11 23:38:32 1994
***************
*** 18,21 ****
--- 18,22 ----
#include <linux/string.h>
#include <linux/stat.h>
+ #include <linux/fs.h>
#include <asm/system.h> /* for cli()/sti() */
***************
*** 84,99 ****
ll_rw_page(rw,p->swap_device,offset,buf);
} else if (p->swap_file) {
unsigned int zones[8];
! unsigned int block;
! int i, j;
!
! block = offset << (12 - p->swap_file->i_sb->s_blocksize_bits);
!
! for (i=0, j=0; j< PAGE_SIZE ; i++, j +=p->swap_file->i_sb->s_blocksize)
! if (!(zones[i] = bmap(p->swap_file,block++))) {
! printk("rw_swap_page: bad swap file\n");
! return;
}
! ll_rw_swap_file(rw,p->swap_file->i_dev, zones, i,buf);
} else
printk("re_swap_page: no swap file or device\n");
--- 85,126 ----
ll_rw_page(rw,p->swap_device,offset,buf);
} else if (p->swap_file) {
+ struct inode *swapf = p->swap_file;
unsigned int zones[8];
! int i;
! if (swapf->i_op->bmap == NULL
! && swapf->i_op->smap != NULL){
! /*
! With MsDOS, we use msdos_smap which return
! a sector number (not a cluster or block number).
! It is a patch to enable the UMSDOS project.
! Other people are working on better solution.
!
! It sounds like ll_rw_swap_file defined
! it operation size (sector size) based on
! PAGE_SIZE and the number of block to read.
! So using bmap ou smap should work even if
! smap will requiered more blocks.
! */
! int j;
! unsigned int block = offset << 3;
!
! for (i=0, j=0; j< PAGE_SIZE ; i++, j += 512){
! if (!(zones[i] = swapf->i_op->smap(swapf,block++))) {
! printk("rw_swap_page: bad swap file\n");
! return;
! }
}
! }else{
! int j;
! unsigned int block = offset
! << (12 - swapf->i_sb->s_blocksize_bits);
!
! for (i=0, j=0; j< PAGE_SIZE ; i++, j +=swapf->i_sb->s_blocksize)
! if (!(zones[i] = bmap(swapf,block++))) {
! printk("rw_swap_page: bad swap file\n");
! return;
! }
! }
! ll_rw_swap_file(rw,swapf->i_dev, zones, i,buf);
} else
printk("re_swap_page: no swap file or device\n");