diff -rc2P linux.9915/config.in linux/config.in *** linux.9915/config.in Thu Feb 3 06:27:36 1994 --- linux/config.in Sat Feb 5 15:41:50 1994 *************** *** 97,100 **** --- 97,101 ---- 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.9915/fs/Makefile linux/fs/Makefile *** linux.9915/fs/Makefile Tue Dec 21 05:30:23 1993 --- linux/fs/Makefile Sat Feb 5 15:36:10 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.9915/fs/Makefile# linux/fs/Makefile# *** linux.9915/fs/Makefile# --- linux/fs/Makefile# Sat Feb 5 15:36:10 1994 *************** *** 0 **** --- 1,13 ---- + *************** + *** 8,12 **** + # Note 2! The CFLAGS definitions are now in the main makefile... + + ! SUBDIRS = minix ext ext2 msdos proc isofs nfs xiafs hpfs + + ifdef CONFIG_MINIX_FS + --- 8,12 ---- + # Note 2! The CFLAGS definitions are now in the main makefile... + + ! SUBDIRS = minix ext ext2 msdos umsdos proc isofs nfs xiafs hpfs + + ifdef CONFIG_MINIX_FS diff -rc2P linux.9915/fs/filesystems.c linux/fs/filesystems.c *** linux.9915/fs/filesystems.c Tue Dec 21 02:17:55 1993 --- linux/fs/filesystems.c Sat Feb 5 15:36:10 1994 *************** *** 27,30 **** --- 27,33 ---- #include #endif + #ifdef CONFIG_UMSDOS_FS + #include + #endif #ifdef CONFIG_NFS_FS #include *************** *** 52,55 **** --- 55,61 ---- #ifdef CONFIG_XIA_FS {xiafs_read_super, "xiafs", 1}, + #endif + #ifdef CONFIG_UMSDOS_FS + {UMSDOS_read_super, "umsdos", 1}, #endif #ifdef CONFIG_MSDOS_FS diff -rc2P linux.9915/fs/msdos/Makefile linux/fs/msdos/Makefile *** linux.9915/fs/msdos/Makefile Wed Dec 1 07:44:15 1993 --- linux/fs/msdos/Makefile Sat Feb 5 15:36:10 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.9915/fs/msdos/dir.c linux/fs/msdos/dir.c *** linux.9915/fs/msdos/dir.c Wed Dec 1 07:44:15 1993 --- linux/fs/msdos/dir.c Sat Feb 5 15:36:10 1994 *************** *** 20,24 **** } ! static int msdos_readdir(struct inode *inode,struct file *filp, struct dirent *dirent,int count); --- 20,24 ---- } ! 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; --- 55,64 ---- }; ! 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; } } --- 73,89 ---- 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++) { --- 92,107 ---- 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++; } --- 108,115 ---- 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; --- 119,132 ---- 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 **** --- 136,148 ---- 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.9915/fs/msdos/file.c linux/fs/msdos/file.c *** linux.9915/fs/msdos/file.c Wed Dec 1 07:44:15 1993 --- linux/fs/msdos/file.c Sat Feb 5 15:36:10 1994 *************** *** 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 */ --- 20,23 ---- *************** *** 33,37 **** NULL, /* select - default */ NULL, /* ioctl - default */ ! NULL, /* mmap */ NULL, /* no special open is needed */ NULL, /* release */ --- 27,31 ---- 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 */ }; --- 48,53 ---- 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; --- 69,86 ---- 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; --- 95,100 ---- 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; --- 108,115 ---- 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; --- 139,175 ---- } ! /* ! 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; --- 182,187 ---- 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); --- 207,216 ---- 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) { --- 254,273 ---- } ! /* ! 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.9915/fs/msdos/mmap.c linux/fs/msdos/mmap.c *** linux.9915/fs/msdos/mmap.c --- linux/fs/msdos/mmap.c Sat Feb 5 15:36:10 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 + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + + 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->min_flt; + return; + } + + ++area->vm_task->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->mmap,NULL,NULL); + return 0; + } diff -rc2P linux.9915/fs/msdos/namei.c linux/fs/msdos/namei.c *** linux.9915/fs/msdos/namei.c Wed Dec 1 07:44:15 1993 --- linux/fs/msdos/namei.c Sat Feb 5 15:36:10 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.9915/fs/umsdos/Makefile linux/fs/umsdos/Makefile *** linux.9915/fs/umsdos/Makefile --- linux/fs/umsdos/Makefile Sat Feb 5 15:36:10 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.9915/fs/umsdos/check.c linux/fs/umsdos/check.c *** linux.9915/fs/umsdos/check.c --- linux/fs/umsdos/check.c Sat Feb 5 15:36:10 1994 *************** *** 0 **** --- 1,55 ---- + #include + + #include + #include + #include + #include + #include + #include + #include + #include + #include + + 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.9915/fs/umsdos/dir.c linux/fs/umsdos/dir.c *** linux.9915/fs/umsdos/dir.c --- linux/fs/umsdos/dir.c Sat Feb 5 15:36:10 1994 *************** *** 0 **** --- 1,669 ---- + /* + * linux/fs/umsdos/dir.c + * + * Written 1993 by Jacques Gelinas + * Inspired from linux/fs/msdos/... : Werner Almesberger + * + * Extended MS-DOS directory handling functions + */ + + #include + + #include + #include + #include + #include + #include + #include + #include + #include + + #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) + { + /* + Do this only if the inode is freshly read, because we will lose + the current (updated) content. + */ + if (inode->i_count == 1){ + /* #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); + } + } + /* + 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; + 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.9915/fs/umsdos/emd.c linux/fs/umsdos/emd.c *** linux.9915/fs/umsdos/emd.c --- linux/fs/umsdos/emd.c Sat Feb 5 15:36:10 1994 *************** *** 0 **** --- 1,454 ---- + /* + * linux/fs/umsdos/emd.c + * + * Written 1993 by Jacques Gelinas + * + * Extended MS-DOS directory handling functions + */ + #include + #include + #include + #include + #include + #include + #include + #include + + #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.9915/fs/umsdos/file.c linux/fs/umsdos/file.c *** linux.9915/fs/umsdos/file.c --- linux/fs/umsdos/file.c Sat Feb 5 15:36:10 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 + #include + + #include + #include + #include + #include + #include + #include + #include + #include + + + #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.9915/fs/umsdos/inode.c linux/fs/umsdos/inode.c *** linux.9915/fs/umsdos/inode.c --- linux/fs/umsdos/inode.c Sat Feb 5 17:16:33 1994 *************** *** 0 **** --- 1,418 ---- + /* + * linux/fs/umsdos/inode.c + * + * Written 1993 by Jacques Gelinas + * Inspired from linux/fs/msdos/... by Werner Almesberger + * + */ + + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + + 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, /* May be NULL, still unclear */ + 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; + } + /* + Connect the proper tables in the inode and add some info. + */ + void umsdos_patch_inode ( + struct inode *inode, + struct inode *dir, /* May be NULL, still unclear */ + off_t f_pos) + { + if (inode->i_count == 1){ + inode->u.umsdos_i.i_emd_dir = 0; + 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. + */ + umsdos_set_dirinfo (inode,dir,f_pos); + } + 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); + } + }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)); + } + 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.9915/fs/umsdos/ioctl.c linux/fs/umsdos/ioctl.c *** linux.9915/fs/umsdos/ioctl.c --- linux/fs/umsdos/ioctl.c Sat Feb 5 15:36:10 1994 *************** *** 0 **** --- 1,259 ---- + /* + * linux/fs/umsdos/ioctl.c + * + * Written 1993 by Jacques Gelinas + * + * Extended MS-DOS ioctl directory handling functions + */ + #include + #include + #include + #include + #include + #include + #include + + #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.9915/fs/umsdos/mangle.c linux/fs/umsdos/mangle.c *** linux.9915/fs/umsdos/mangle.c --- linux/fs/umsdos/mangle.c Sat Feb 5 15:36:11 1994 *************** *** 0 **** --- 1,498 ---- + /* + * 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 + #include + #include + #include + #include + + #ifndef __KERNEL__ + /* + GLU This is needed for stderr, va_list, va_start and va_end. + */ + #include + #include + /* + GLU This is a stub because mangle.c is a kernel module + */ + int printk (const char *msg, ...) + { + int ret; + va_list list; + va_start (list,msg); + ret = vfprintf (stderr,msg,list); + va_end (list); + return ret; + } + #endif + + /* + Complete the mangling of the MSDOS fake name + based on the position of the entry in the EMD file. + + Simply complete the job of umsdos_parse; fill the extension. + + Beware that info->f_pos must be set. + */ + void umsdos_manglename (struct umsdos_info *info) + { + if (info->msdos_reject){ + /* #Specification: file name / non MSDOS conforming / mangling + Each non MSDOS conforming file has a special extension + build from the entry position in the EMD file. + + This number is then transform in a base 32 number, where + each digit is expressed like hexadecimal number, using + digit and letter, except it uses 22 letters from 'a' to 'v'. + The number 32 comes from 2**5. It is faster to split a binary + number using a base which is a power of two. And I was 32 + when I started this project. Pick your answer :-) . + + If the result is '0', it is replace with '_', simply + to make it odd. + + This is true for the first two character of the extension. + The last one is taken from a list of odd character, which + are: + + { } ( ) ! ` ^ & @ + + With this scheme, we can produce 9216 ( 9* 32 * 32) + different extensions which should not clash with any useful + extension already popular or meaningful. Since most directory + have much less than 32 * 32 files in it, the first character + of the extension of any 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; imsdos_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 + 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]; ifake.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; ifname,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 + #include + #include + #include + #include + #include + #include + #include + #include + #include + + #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){ + static volatile long nb_creation=0; + int cur_creation; + 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); + nb_creation++; /* check that umsdos_lockcreate does its job */ + cur_creation = nb_creation; + 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)); + } + if (nb_creation != cur_creation){ + printk ("nb_creation %ld cur_creation %d\n",nb_creation + ,cur_creation); + } + umsdos_unlockcreate(dir); + } + } + iput (dir); + return ret; + } + /* + 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 ")); + 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; + 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); + if (ret == -EISDIR){ + new_dir->i_count++; + ret = UMSDOS_rmdir (new_dir,new_name,new_len); + } + if (ret == 0) 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++; + 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.9915/fs/umsdos/notes linux/fs/umsdos/notes *** linux.9915/fs/umsdos/notes --- linux/fs/umsdos/notes Sat Feb 5 15:36:11 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.9915/fs/umsdos/rdir.c linux/fs/umsdos/rdir.c *** linux.9915/fs/umsdos/rdir.c --- linux/fs/umsdos/rdir.c Sat Feb 5 15:36:11 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 + + #include + #include + #include + #include + #include + #include + #include + #include + + #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; + } + + static 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.9915/fs/umsdos/symlink.c linux/fs/umsdos/symlink.c *** linux.9915/fs/umsdos/symlink.c --- linux/fs/umsdos/symlink.c Sat Feb 5 15:36:11 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 + #include + + #include + #include + #include + #include + #include + #include + #include + #include + + #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->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.9915/include/linux/fs.h linux/include/linux/fs.h *** linux.9915/include/linux/fs.h Mon Jan 3 02:45:26 1994 --- linux/include/linux/fs.h Sat Feb 5 15:36:11 1994 *************** *** 153,156 **** --- 153,157 ---- #include #include + #include #include #include *************** *** 196,199 **** --- 197,201 ---- 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; *************** *** 292,295 **** --- 294,298 ---- void (*truncate) (struct inode *); int (*permission) (struct inode *, int); + int (*smap) (struct inode *,int); }; diff -rc2P linux.9915/include/linux/msdos_fs.h linux/include/linux/msdos_fs.h *** linux.9915/include/linux/msdos_fs.h Wed Dec 1 07:44:15 1993 --- linux/include/linux/msdos_fs.h Sat Feb 5 15:36:11 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.9915/include/linux/msdos_fs_i.h linux/include/linux/msdos_fs_i.h *** linux.9915/include/linux/msdos_fs_i.h Wed Dec 1 07:44:15 1993 --- linux/include/linux/msdos_fs_i.h Sat Feb 5 15:36:11 1994 *************** *** 2,5 **** --- 2,12 ---- #define _MSDOS_FS_I + #ifndef _LINUX_CONFIG_H + #include + #endif + #ifndef _LINUX_PIPE_FS_I_H + #include + #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.9915/include/linux/umsdos_fs.h linux/include/linux/umsdos_fs.h *** linux.9915/include/linux/umsdos_fs.h --- linux/include/linux/umsdos_fs.h Sat Feb 5 15:36:11 1994 *************** *** 0 **** --- 1,105 ---- + #ifndef LINUX_UMSDOS_FS_H + #define LINUX_UMSDOS_FS_H + + #define UMSDOS_VERSION 0 + #define UMSDOS_RELEASE 2 + + #ifndef LINUX_FS_H + #include + #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 + #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 + + #endif diff -rc2P linux.9915/include/linux/umsdos_fs.p linux/include/linux/umsdos_fs.p *** linux.9915/include/linux/umsdos_fs.p --- linux/include/linux/umsdos_fs.p Sat Feb 5 15:36:11 1994 *************** *** 0 **** --- 1,116 ---- + /* check.c 07/12/93 01.02.36 */ + void check_page_tables (void); + /* dir.c 20/01/94 00.17.38 */ + 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/01/94 22.53.24 */ + 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 07/12/93 01.02.50 */ + /* inode.c 17/01/94 22.35.36 */ + 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); + 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 20/01/94 00.57.06 */ + int UMSDOS_ioctl_dir (struct inode *dir, + struct file *filp, + unsigned int cmd, + unsigned long data); + /* mangle.c 20/01/94 01.07.12 */ + 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 20/01/94 01.03.30 */ + 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 19/01/94 23.45.18 */ + /* symlink.c 07/12/93 01.20.26 */ diff -rc2P linux.9915/include/linux/umsdos_fs_i.h linux/include/linux/umsdos_fs_i.h *** linux.9915/include/linux/umsdos_fs_i.h --- linux/include/linux/umsdos_fs_i.h Sat Feb 5 15:36:11 1994 *************** *** 0 **** --- 1,140 ---- + #ifndef UMSDOS_FS_I_H + #define UMSDOS_FS_I_H + + #ifndef _LINUX_TYPES_H + #include + #endif + #include + #include + + /* #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 + #endif + #include + #include + + /* #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.9915/init/main.c linux/init/main.c *** linux.9915/init/main.c Sat Jan 29 07:55:05 1994 --- linux/init/main.c Sat Feb 5 15:36:11 1994 *************** *** 473,476 **** --- 473,492 ---- setup((void *) &drive_info); 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->root = pseudo_root; + current->pwd = pseudo_root; + } + } + #endif + (void) open("/dev/tty1",O_RDWR,0); (void) dup(0); diff -rc2P linux.9915/mm/swap.c linux/mm/swap.c *** linux.9915/mm/swap.c Tue Feb 1 02:03:52 1994 --- linux/mm/swap.c Sat Feb 5 15:36:11 1994 *************** *** 18,21 **** --- 18,22 ---- #include #include + #include #include /* for cli()/sti() */ *************** *** 85,100 **** 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"); --- 86,127 ---- 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");