diff options
Diffstat (limited to 'fs/umsdos')
-rw-r--r-- | fs/umsdos/Makefile | 7 | ||||
-rw-r--r-- | fs/umsdos/README | 20 | ||||
-rw-r--r-- | fs/umsdos/check.c | 31 | ||||
-rw-r--r-- | fs/umsdos/dir.c | 18 | ||||
-rw-r--r-- | fs/umsdos/emd.c | 4 | ||||
-rw-r--r-- | fs/umsdos/file.c | 53 | ||||
-rw-r--r-- | fs/umsdos/inode.c | 91 | ||||
-rw-r--r-- | fs/umsdos/ioctl.c | 19 | ||||
-rw-r--r-- | fs/umsdos/mangle.c | 14 | ||||
-rw-r--r-- | fs/umsdos/namei.c | 269 | ||||
-rw-r--r-- | fs/umsdos/rdir.c | 31 | ||||
-rw-r--r-- | fs/umsdos/symlink.c | 3 |
12 files changed, 362 insertions, 198 deletions
diff --git a/fs/umsdos/Makefile b/fs/umsdos/Makefile index cfba11e63..8bdf98f97 100644 --- a/fs/umsdos/Makefile +++ b/fs/umsdos/Makefile @@ -7,10 +7,6 @@ # # Note 2! The CFLAGS definitions are now in the main makefile... -ifndef CONFIG_UMSDOS_FS -CFLAGS := $(CFLAGS) -DMODULE -endif - .c.s: $(CC) $(CFLAGS) -S $< .c.o: @@ -24,6 +20,9 @@ OBJS= dir.o emd.o file.o inode.o ioctl.o mangle.o namei.o\ umsdos.o: $(OBJS) $(LD) -r -o umsdos.o $(OBJS) +modules: umsdos.o + ln -sf ../fs/umsdos/umsdos.o $(TOPDIR)/modules + clean: rm -f core *.o *.a *.s diff --git a/fs/umsdos/README b/fs/umsdos/README index 4ce8b4148..320dac6ca 100644 --- a/fs/umsdos/README +++ b/fs/umsdos/README @@ -16,7 +16,7 @@ It gives you: There is plenty of documentation on it in the source. A formated document made from those comments is available from -sunsite.unc.edu:/pub/Linux/ALPHA/umsdos +sunsite.unc.edu:/pub/Linux/system/Filesystems/umsdos. Mostly... @@ -53,9 +53,9 @@ Now, how to get those --linux-.---. $5 per directory. Add any applicable taxes. \end joke_section -A utility umssync creates those and maintain them. It is available -from the same directory above (sunsite) in the file umsdos_progs-0.3.tar.gz. -A compiled version is available in umsdos-0.3a.bin.tar.gz. +A utility umssync creates those. The kernel maintain them. It is available +from the same directory above (sunsite) in the file umsdos_progs-0.7.tar.gz. +A compiled version is available in umsdos_progs-0.7.bin.tar.gz. So in our example, after mounting mnt, we do @@ -80,5 +80,17 @@ Umsdos won't notice new files, but will signal removed file (it won't crash). Using umssync in /etc/rc will make sure the DOS directory is in sync with the --linux-.---. +It is a good idea to put the following command in your RC file just +after the "mount -a": + + mount -a + /sbin/umssync -i+ -c+ -r99 /umsdos_mount_point + + (You put one for each umsdos mount point in the fstab) + +This will insure nice operation. A umsdos.fsck is in the making, +so you will be allowed to managed umsdos partition in the same way +other filesystem are, using the generic fsck front end. + Hope this helps! diff --git a/fs/umsdos/check.c b/fs/umsdos/check.c index d1102b4ce..7fb060c7a 100644 --- a/fs/umsdos/check.c +++ b/fs/umsdos/check.c @@ -1,3 +1,7 @@ +#ifdef MODULE +#include <linux/module.h> +#endif + #include <asm/system.h> #include <linux/signal.h> @@ -12,44 +16,35 @@ extern unsigned long high_memory; -static int check_one_table(unsigned long * page_dir) +static int check_one_table(struct pde * page_dir) { - unsigned long pg_table = *page_dir; - - if (!pg_table) + if (pgd_none(*page_dir)) return 0; - if (pg_table >= high_memory || !(pg_table & PAGE_PRESENT)) { + if (pgd_bad(*page_dir)) return 1; - } return 0; } /* - * This function frees up all page tables of a process when it exits. + * This function checks all page tables of "current" */ void check_page_tables(void) { - unsigned long pg_dir; + struct pgd * pg_dir; static int err = 0; int stack_level = (long)(&pg_dir)-current->kernel_stack_page; if (stack_level < 1500) printk ("** %d ** ",stack_level); - pg_dir = 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; + pg_dir = PAGE_DIR_OFFSET(current, 0); + if (err == 0) { int i; for (i = 0 ; i < PTRS_PER_PAGE ; i++,page_dir++){ int notok = check_one_table(page_dir); if (notok){ err++; - printk ("|%d| ",page_dir-base); + printk ("|%d:%08lx| ",i, page_dir->pgd); } } - if (err) printk ("Erreur MM %d\n",err); + if (err) printk ("\nErreur MM %d\n",err); } } - diff --git a/fs/umsdos/dir.c b/fs/umsdos/dir.c index 2a668d102..990260a92 100644 --- a/fs/umsdos/dir.c +++ b/fs/umsdos/dir.c @@ -7,6 +7,10 @@ * Extended MS-DOS directory handling functions */ +#ifdef MODULE +#include <linux/module.h> +#endif + #include <asm/segment.h> #include <linux/sched.h> @@ -32,10 +36,14 @@ int UMSDOS_dir_read(struct inode *inode,struct file *filp,char *buf, { return -EISDIR; } +#define NAME_OFFSET(de) ((int) ((de)->d_name - (char *) (de))) +#define ROUND_UP(x) (((x)+3) & ~3) + /* 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). + Return > 0 if success (The amount of byte written in + dirent round_up to a word size (32 bits). 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 @@ -72,7 +80,7 @@ static int umsdos_readdir_x( 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; + ret = ROUND_UP(NAME_OFFSET(dirent) + 3 + 1); filp->f_pos++; }else if (filp->f_pos < 2 || (dir != dir->i_sb->s_mounted && filp->f_pos == 32)){ @@ -191,7 +199,7 @@ static int umsdos_readdir_x( dirent->d_reclen = entry.name_len; if (u_entry != NULL) *u_entry = entry; } - ret = entry.name_len; + ret = ROUND_UP(NAME_OFFSET(dirent) + entry.name_len + 1); iput (inode); break; } @@ -219,7 +227,7 @@ static int umsdos_readdir_x( /* 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). + Return > 0 if success (the amount of byte written to dirent) */ static int UMSDOS_readdir( struct inode *dir, /* Point to a description of the super block */ @@ -644,7 +652,7 @@ int umsdos_hlink2inode (struct inode *hlink, struct inode **result) 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); + ret = umsdos_rlookup_x(dir,start,len,result,1); }else{ ret = umsdos_lookup_x(dir,start,len,result,1); } diff --git a/fs/umsdos/emd.c b/fs/umsdos/emd.c index e4d6a9470..b1ec47bf6 100644 --- a/fs/umsdos/emd.c +++ b/fs/umsdos/emd.c @@ -5,6 +5,10 @@ * * Extended MS-DOS directory handling functions */ +#ifdef MODULE +#include <linux/module.h> +#endif + #include <linux/types.h> #include <linux/fcntl.h> #include <linux/kernel.h> diff --git a/fs/umsdos/file.c b/fs/umsdos/file.c index d292ea3c2..61eacaac6 100644 --- a/fs/umsdos/file.c +++ b/fs/umsdos/file.c @@ -7,6 +7,10 @@ * Extended MS-DOS regular file handling primitives */ +#ifdef MODULE +#include <linux/module.h> +#endif + #include <asm/segment.h> #include <asm/system.h> @@ -33,8 +37,10 @@ static int UMSDOS_file_read( { /* 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; + if (!IS_RDONLY(inode)){ + inode->i_atime = CURRENT_TIME; + inode->i_dirt = 1; + } return ret; } /* @@ -58,16 +64,8 @@ static void UMSDOS_truncate(struct inode *inode) inode->i_ctime = inode->i_mtime = CURRENT_TIME; inode->i_dirt = 1; } -/* - See inode.c - - Some entry point are filled dynamically with function pointers - from the msdos file_operations and file_inode_operations. - - The idea is to have the code as independent as possible from - the msdos file system. -*/ +/* Function for normal file system (512 bytes hardware sector size) */ struct file_operations umsdos_file_operations = { NULL, /* lseek - default */ UMSDOS_file_read, /* read */ @@ -94,10 +92,41 @@ struct inode_operations umsdos_file_inode_operations = { NULL, /* rename */ NULL, /* readlink */ NULL, /* follow_link */ - NULL, /* bmap */ + msdos_bmap, /* bmap */ UMSDOS_truncate,/* truncate */ NULL, /* permission */ msdos_smap /* smap */ }; +/* For other with larger and unaligned file system */ +struct file_operations umsdos_file_operations_no_bmap = { + NULL, /* lseek - default */ + UMSDOS_file_read, /* read */ + UMSDOS_file_write, /* write */ + NULL, /* readdir - bad */ + NULL, /* select - default */ + NULL, /* ioctl - default */ + msdos_mmap, /* mmap */ + NULL, /* no special open is needed */ + NULL, /* release */ + file_fsync /* fsync */ +}; +struct inode_operations umsdos_file_inode_operations_no_bmap = { + &umsdos_file_operations_no_bmap, /* default file operations */ + NULL, /* create */ + NULL, /* lookup */ + NULL, /* link */ + NULL, /* unlink */ + NULL, /* symlink */ + NULL, /* mkdir */ + NULL, /* rmdir */ + NULL, /* mknod */ + NULL, /* rename */ + NULL, /* readlink */ + NULL, /* follow_link */ + NULL, /* bmap */ + UMSDOS_truncate,/* truncate */ + NULL, /* permission */ + NULL, /* smap */ +}; diff --git a/fs/umsdos/inode.c b/fs/umsdos/inode.c index 40f7feb68..22be740cc 100644 --- a/fs/umsdos/inode.c +++ b/fs/umsdos/inode.c @@ -6,6 +6,14 @@ * */ +#ifdef MODULE +#include <linux/module.h> +#include <linux/version.h> +#else +#define MOD_INC_USE_COUNT +#define MOD_DEC_USE_COUNT +#endif + #include <linux/fs.h> #include <linux/msdos_fs.h> #include <linux/kernel.h> @@ -13,15 +21,9 @@ #include <linux/errno.h> #include <asm/segment.h> #include <linux/string.h> -#include <linux/ctype.h> #include <linux/stat.h> #include <linux/umsdos_fs.h> -#ifdef MODULE - #include <linux/module.h> - #include "../../tools/version.h" -#endif - struct inode *pseudo_root=NULL; /* Useful to simulate the pseudo DOS */ /* directory. See UMSDOS_readdir_x() */ @@ -45,6 +47,9 @@ void UMSDOS_put_inode(struct inode *inode) PRINTK (("put inode %x owner %x pos %d dir %x\n",inode ,inode->u.umsdos_i.i_emd_owner,inode->u.umsdos_i.pos ,inode->u.umsdos_i.i_emd_dir)); + if (inode != NULL && inode == pseudo_root){ + printk ("Umsdos: Oops releasing pseudo_root. Notify jacques@solucorp.qc.ca\n"); + } msdos_put_inode(inode); } @@ -52,15 +57,13 @@ void UMSDOS_put_inode(struct inode *inode) void UMSDOS_put_super(struct super_block *sb) { msdos_put_super(sb); - #ifdef MODULE - MOD_DEC_USE_COUNT; - #endif + MOD_DEC_USE_COUNT; } -void UMSDOS_statfs(struct super_block *sb,struct statfs *buf) +void UMSDOS_statfs(struct super_block *sb,struct statfs *buf, int bufsiz) { - msdos_statfs(sb,buf); + msdos_statfs(sb,buf,bufsiz); } @@ -152,18 +155,11 @@ void umsdos_patch_inode ( if (!umsdos_isinit(inode)){ inode->u.umsdos_i.i_emd_dir = 0; if (S_ISREG(inode->i_mode)){ - static char is_init = 0; - if (!is_init){ - /* - I don't want to change the msdos file system code - so I get the address of some subroutine dynamically - once. - */ - umsdos_file_inode_operations.bmap = inode->i_op->bmap; + if (inode->i_op->bmap != NULL){ inode->i_op = &umsdos_file_inode_operations; - is_init = 1; + }else{ + inode->i_op = &umsdos_file_inode_operations_no_bmap; } - inode->i_op = &umsdos_file_inode_operations; }else if (S_ISDIR(inode->i_mode)){ if (dir != NULL){ umsdos_setup_dir_inode(inode); @@ -408,8 +404,10 @@ struct super_block *UMSDOS_read_super( 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 0.5a (compatibility level %d.%d, fast msdos)\n" + struct super_block *sb; + MOD_INC_USE_COUNT; + sb = msdos_read_super(s,data,silent); + printk ("UMSDOS Beta 0.6 (compatibility level %d.%d, fast msdos)\n" ,UMSDOS_VERSION,UMSDOS_RELEASE); if (sb != NULL){ sb->s_op = &umsdos_sops; @@ -420,8 +418,8 @@ struct super_block *UMSDOS_read_super( /* #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"). + of the file /linux/etc/init or /linux/etc/rc or + /linux/sbin/init. If one is there, we do a chroot("/linux"). We check both because (see init/main.c) the kernel try to exec init at different place and if it fails @@ -454,32 +452,48 @@ struct super_block *UMSDOS_read_super( ,UMSDOS_PSDROOT_LEN,&pseudo)==0 && S_ISDIR(pseudo->i_mode)){ struct inode *etc = NULL; - struct inode *rc = NULL; + struct inode *sbin = NULL; + int pseudo_ok = 0; Printk (("/%s is there\n",UMSDOS_PSDROOT_NAME)); if (umsdos_real_lookup (pseudo,"etc",3,&etc)==0 && S_ISDIR(etc->i_mode)){ - struct inode *init; + struct inode *init = NULL; + struct inode *rc = NULL; Printk (("/%s/etc is there\n",UMSDOS_PSDROOT_NAME)); if ((umsdos_real_lookup (etc,"init",4,&init)==0 && S_ISREG(init->i_mode)) || (umsdos_real_lookup (etc,"rc",2,&rc)==0 && S_ISREG(rc->i_mode))){ - umsdos_setup_dir_inode (pseudo); - Printk (("Activating pseudo root /%s\n",UMSDOS_PSDROOT_NAME)); - pseudo_root = pseudo; - pseudo->i_count++; - pseudo = NULL; + pseudo_ok = 1; } iput (init); iput (rc); } + if (!pseudo_ok + && umsdos_real_lookup (pseudo,"sbin",4,&sbin)==0 + && S_ISDIR(sbin->i_mode)){ + struct inode *init = NULL; + Printk (("/%s/sbin is there\n",UMSDOS_PSDROOT_NAME)); + if (umsdos_real_lookup (sbin,"init",4,&init)==0 + && S_ISREG(init->i_mode)){ + pseudo_ok = 1; + } + iput (init); + } + if (pseudo_ok){ + umsdos_setup_dir_inode (pseudo); + Printk (("Activating pseudo root /%s\n",UMSDOS_PSDROOT_NAME)); + pseudo_root = pseudo; + pseudo->i_count++; + pseudo = NULL; + } + iput (sbin); iput (etc); } iput (pseudo); } - #ifdef MODULE - MOD_INC_USE_COUNT; - #endif + } else { + MOD_DEC_USE_COUNT; } return sb; } @@ -501,12 +515,7 @@ int init_module(void) void cleanup_module(void) { - if (MOD_IN_USE) - printk("Umsdos: file system in use, remove delayed\n"); - else - { - unregister_filesystem(&umsdos_fs_type); - } + unregister_filesystem(&umsdos_fs_type); } #endif diff --git a/fs/umsdos/ioctl.c b/fs/umsdos/ioctl.c index 972571796..d766ef939 100644 --- a/fs/umsdos/ioctl.c +++ b/fs/umsdos/ioctl.c @@ -5,6 +5,10 @@ * * Extended MS-DOS ioctl directory handling functions */ +#ifdef MODULE +#include <linux/module.h> +#endif + #include <asm/segment.h> #include <linux/errno.h> #include <linux/kernel.h> @@ -160,6 +164,21 @@ int UMSDOS_ioctl_dir ( umsdos_parse (data.umsdos_dirent.name ,data.umsdos_dirent.name_len,&info); ret = umsdos_newentry (dir,&info); + }else if (cmd == UMSDOS_RENAME_DOS){ + /* #Specification: ioctl / UMSDOS_RENAME_DOS + A file or directory is rename in a DOS directory + (not moved across directory). The source name + is in the dos_dirent.name field and the destination + is in umsdos_dirent.name field. + + This ioctl allows umssync to rename a mangle file + name before syncing it back in the EMD. + */ + dir->i_count += 2; + ret = msdos_rename (dir + ,data.dos_dirent.d_name,data.dos_dirent.d_reclen + ,dir + ,data.umsdos_dirent.name,data.umsdos_dirent.name_len); }else if (cmd == UMSDOS_UNLINK_EMD){ /* #Specification: ioctl / UMSDOS_UNLINK_EMD The umsdos_dirent field of the struct umsdos_ioctl is used diff --git a/fs/umsdos/mangle.c b/fs/umsdos/mangle.c index 1f59447e9..a7649a39a 100644 --- a/fs/umsdos/mangle.c +++ b/fs/umsdos/mangle.c @@ -6,6 +6,10 @@ * Control the mangling of file name to fit msdos name space. * Many optimisation by GLU == dglaude@is1.vub.ac.be (GLAUDE DAVID) */ +#ifdef MODULE +#include <linux/module.h> +#endif + #include <linux/errno.h> #include <linux/ctype.h> #include <linux/string.h> @@ -222,13 +226,15 @@ int umsdos_parse ( 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' @@ -270,7 +276,9 @@ int umsdos_parse ( 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. @@ -294,7 +302,7 @@ int umsdos_parse ( */ }else{ /* Conforming MSDOS file name */ - strcpy (info->fake.fname,fname); /* GLU C'est sur on a un 0 a la fin */ + strncpy (info->fake.fname,fname,len); info->msdos_reject = 0; base_len = firstpt != NULL ? (int)(firstpt - fname) : len; } @@ -316,10 +324,12 @@ int umsdos_parse ( 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 diff --git a/fs/umsdos/namei.c b/fs/umsdos/namei.c index 567039e14..42820bb98 100644 --- a/fs/umsdos/namei.c +++ b/fs/umsdos/namei.c @@ -6,6 +6,10 @@ * * Maintain and access the --linux alternate directory file. */ +#ifdef MODULE +#include <linux/module.h> +#endif + #include <linux/errno.h> #include <linux/kernel.h> #include <linux/sched.h> @@ -315,7 +319,7 @@ static int umsdos_rename_f( int flags) /* 0 == copy flags from old_name */ /* != 0, this is the value of flags */ { - int ret = EPERM; + int ret = -EPERM; struct umsdos_info old_info; int old_ret = umsdos_parse (old_name,old_len,&old_info); struct umsdos_info new_info; @@ -330,58 +334,79 @@ chkstk(); chkstk(); PRINTK (("ret %d ",ret)); if (ret == 0){ - PRINTK (("new newentry ")); - umsdos_ren_init(&new_info,&old_info,flags); - ret = umsdos_newentry (new_dir,&new_info); -chkstk(); - PRINTK (("ret %d %d ",ret,new_info.fake.len)); - if (ret == 0){ - PRINTK (("msdos_rename ")); - old_dir->i_count++; - new_dir->i_count++; /* Both inode are needed later */ - ret = msdos_rename (old_dir - ,old_info.fake.fname,old_info.fake.len - ,new_dir - ,new_info.fake.fname,new_info.fake.len); -chkstk(); - PRINTK (("after m_rename ret %d ",ret)); - if (ret != 0){ - umsdos_delentry (new_dir,&new_info - ,S_ISDIR(new_info.entry.mode)); -chkstk(); - }else{ - ret = umsdos_delentry (old_dir,&old_info - ,S_ISDIR(old_info.entry.mode)); + /* check sticky bit on old_dir */ + if ( !(old_dir->i_mode & S_ISVTX) || fsuser() || + current->fsuid == old_info.entry.uid || + current->fsuid == old_dir->i_uid ) { + /* Does new_name already exist? */ + PRINTK(("new findentry ")); + ret = umsdos_findentry(new_dir,&new_info,0); + if (ret != 0 || /* if destination file exists, are we allowed to replace it ? */ + !(new_dir->i_mode & S_ISVTX) || fsuser() || + current->fsuid == new_info.entry.uid || + current->fsuid == new_dir->i_uid ) { + PRINTK (("new newentry ")); + umsdos_ren_init(&new_info,&old_info,flags); + ret = umsdos_newentry (new_dir,&new_info); chkstk(); + PRINTK (("ret %d %d ",ret,new_info.fake.len)); if (ret == 0){ - /* - This UMSDOS_lookup does not look very useful. - It makes sure that the inode of the file will - be correctly setup (umsdos_patch_inode()) in - case it is already in use. - - Not very efficient ... - */ - struct inode *inode; - new_dir->i_count++; - PRINTK (("rename lookup len %d %d -- ",new_len,new_info.entry.flags)); - ret = UMSDOS_lookup (new_dir,new_name,new_len - ,&inode); + 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); chkstk(); + PRINTK (("after m_rename ret %d ",ret)); if (ret != 0){ - printk ("UMSDOS: partial rename for file %s\n" - ,new_info.entry.name); + umsdos_delentry (new_dir,&new_info + ,S_ISDIR(new_info.entry.mode)); +chkstk(); }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); + ret = umsdos_delentry (old_dir,&old_info + ,S_ISDIR(old_info.entry.mode)); chkstk(); - iput (inode); + if (ret == 0){ + /* + This UMSDOS_lookup does not look very useful. + It makes sure that the inode of the file will + be correctly setup (umsdos_patch_inode()) in + case it is already in use. + + Not very efficient ... + */ + struct inode *inode; + new_dir->i_count++; + PRINTK (("rename lookup len %d %d -- ",new_len,new_info.entry.flags)); + ret = UMSDOS_lookup (new_dir,new_name,new_len + ,&inode); +chkstk(); + if (ret != 0){ + printk ("UMSDOS: partial rename for file %s\n" + ,new_info.entry.name); + }else{ + /* + Update f_pos so notify_change will succeed + if the file was already in use. + */ + umsdos_set_dirinfo (inode,new_dir,new_info.f_pos); +chkstk(); + iput (inode); + } + } } } + }else{ + /* sticky bit set on new_dir */ + PRINTK(("sticky set on new ")); + ret = -EPERM; } + }else{ + /* sticky bit set on old_dir */ + PRINTK(("sticky set on old ")); + ret = -EPERM; } } umsdos_unlockcreate(old_dir); @@ -483,6 +508,7 @@ int UMSDOS_link ( Given a file /foo/file + # ln /foo/file /tmp/file2 become internally @@ -490,6 +516,7 @@ int UMSDOS_link ( 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. @@ -502,6 +529,7 @@ int UMSDOS_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 @@ -509,6 +537,7 @@ int UMSDOS_link ( 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. @@ -519,12 +548,14 @@ int UMSDOS_link ( 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 @@ -765,6 +796,7 @@ int UMSDOS_rmdir( but you rapidly get iput() all around. Here is an exemple of what I am trying to avoid. + # if (a){ ... if(b){ @@ -783,10 +815,12 @@ int UMSDOS_rmdir( } // 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){ ... @@ -797,6 +831,7 @@ int UMSDOS_rmdir( ... } 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 @@ -812,6 +847,7 @@ int UMSDOS_rmdir( where an iput() is done, the inode is simply nulled, disabling the last one. + # if (a){ if (b){ ... @@ -824,6 +860,7 @@ int UMSDOS_rmdir( } iput (dir); return status; + # Note that the umsdos_lockcreate() and umsdos_unlockcreate() function pair goes against this practice of "forgetting" the inode as soon @@ -842,28 +879,37 @@ int UMSDOS_rmdir( ret = -EBUSY; }else 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); + /* check sticky bit */ + if ( !(dir->i_mode & S_ISVTX) || fsuser() || + current->fsuid == sdir->i_uid || + current->fsuid == dir->i_uid ) { + if (empty == 1){ + /* We have to removed the EMD file */ + ret = msdos_unlink(sdir,UMSDOS_EMD_FILE + ,UMSDOS_EMD_NAMELEN); + sdir = NULL; + } + /* sdir must be free before msdos_rmdir() */ + iput (sdir); sdir = NULL; - } - /* 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); + PRINTK (("isempty ret %d nlink %d ",ret,dir->i_nlink)); if (ret == 0){ - ret = umsdos_delentry (dir,&info,1); + struct umsdos_info info; + dir->i_count++; + umsdos_parse (name,len,&info); + /* The findentry is there only to complete */ + /* the mangling */ + umsdos_findentry (dir,&info,2); + ret = msdos_rmdir (dir,info.fake.fname + ,info.fake.len); + if (ret == 0){ + ret = umsdos_delentry (dir,&info,1); + } } + }else{ + /* sticky bit set and we don't have permission */ + PRINTK(("sticky set ")); + ret = -EPERM; } }else{ /* @@ -887,63 +933,72 @@ int UMSDOS_unlink ( const char * name, int len) { - struct umsdos_info info; int ret = umsdos_nevercreat(dir,name,len,-EPERM); if (ret == 0){ + struct umsdos_info info; ret = umsdos_parse (name,len,&info); if (ret == 0){ umsdos_lockcreate(dir); ret = umsdos_findentry(dir,&info,1); if (ret == 0){ PRINTK (("UMSDOS_unlink %s ",info.fake.fname)); - if (info.entry.flags & UMSDOS_HLINK){ - /* #Specification: hard link / deleting a link - When we deletes a file, and this file is a link - we must subtract 1 to the nlink field of the - hidden link. - - If the count goes to 0, we delete this hidden - link too. - */ - /* - First, get the inode of the hidden link - using the standard lookup function. - */ - struct inode *inode; - dir->i_count++; - ret = UMSDOS_lookup (dir,name,len,&inode); - if (ret == 0){ - PRINTK (("unlink nlink = %d ",inode->i_nlink)); - inode->i_nlink--; - if (inode->i_nlink == 0){ - struct inode *hdir = iget(inode->i_sb - ,inode->u.umsdos_i.i_dir_owner); - struct umsdos_dirent entry; - ret = umsdos_inode2entry (hdir,inode,&entry); - if (ret == 0){ - ret = UMSDOS_unlink (hdir,entry.name - ,entry.name_len); + /* check sticky bit */ + if ( !(dir->i_mode & S_ISVTX) || fsuser() || + current->fsuid == info.entry.uid || + current->fsuid == dir->i_uid ) { + if (info.entry.flags & UMSDOS_HLINK){ + /* #Specification: hard link / deleting a link + When we deletes a file, and this file is a link + we must subtract 1 to the nlink field of the + hidden link. + + If the count goes to 0, we delete this hidden + link too. + */ + /* + First, get the inode of the hidden link + using the standard lookup function. + */ + struct inode *inode; + dir->i_count++; + ret = UMSDOS_lookup (dir,name,len,&inode); + if (ret == 0){ + PRINTK (("unlink nlink = %d ",inode->i_nlink)); + inode->i_nlink--; + if (inode->i_nlink == 0){ + struct inode *hdir = iget(inode->i_sb + ,inode->u.umsdos_i.i_dir_owner); + struct umsdos_dirent entry; + ret = umsdos_inode2entry (hdir,inode,&entry); + if (ret == 0){ + ret = UMSDOS_unlink (hdir,entry.name + ,entry.name_len); + }else{ + iput (hdir); + } }else{ - iput (hdir); + struct iattr newattrs; + newattrs.ia_valid = 0; + ret = UMSDOS_notify_change (inode, &newattrs); } - }else{ - struct iattr newattrs; - newattrs.ia_valid = 0; - ret = UMSDOS_notify_change (inode, &newattrs); + iput (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)); + ret = umsdos_delentry (dir,&info,0); + if (ret == 0){ + PRINTK (("Avant msdos_unlink %s ",info.fake.fname)); + dir->i_count++; + ret = msdos_unlink_umsdos (dir,info.fake.fname + ,info.fake.len); + PRINTK (("msdos_unlink %s %o ret %d ",info.fake.fname + ,info.entry.mode,ret)); + } } + }else{ + /* sticky bit set and we've not got permission */ + PRINTK(("sticky set ")); + ret = -EPERM; } } umsdos_unlockcreate(dir); @@ -986,7 +1041,7 @@ int UMSDOS_rename( ,new_len,0); if (ret == -EEXIST){ /* #Specification: rename / new name exist - If the destination name already exist, it will + 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. diff --git a/fs/umsdos/rdir.c b/fs/umsdos/rdir.c index d7272ed96..d708709bd 100644 --- a/fs/umsdos/rdir.c +++ b/fs/umsdos/rdir.c @@ -7,6 +7,10 @@ * (For directory without EMD file). */ +#ifdef MODULE +#include <linux/module.h> +#endif + #include <asm/segment.h> #include <linux/sched.h> @@ -32,8 +36,10 @@ static int UMSDOS_rreaddir ( { int ret = 0; while (1){ + int len = -1; ret = msdos_readdir(dir,filp,dirent,count); - if (ret == 5 + if (ret > 0) len = get_fs_word(&dirent->d_reclen); + if (len == 5 && pseudo_root != NULL && dir->i_sb->s_mounted == pseudo_root->i_sb->s_mounted){ /* @@ -45,7 +51,7 @@ static int UMSDOS_rreaddir ( if (memcmp(name,UMSDOS_PSDROOT_NAME,UMSDOS_PSDROOT_LEN)!=0) break; }else{ if (pseudo_root != NULL - && ret == 2 + && len == 2 && dir == dir->i_sb->s_mounted && dir == pseudo_root->i_sb->s_mounted){ char name[2]; @@ -60,11 +66,18 @@ static int UMSDOS_rreaddir ( return ret; } -int UMSDOS_rlookup( +/* + Lookup into a non promoted directory. + If the result is a directory, make sure we find out if it is + a promoted one or not (calling umsdos_setup_dir_inode(inode)). +*/ +int umsdos_rlookup_x( struct inode *dir, const char *name, int len, - struct inode **result) /* Will hold inode of the file, if successful */ + struct inode **result, /* Will hold inode of the file, if successful */ + int nopseudo) /* Don't care about pseudo root mode */ + /* so locating "linux" will work */ { int ret; if (pseudo_root != NULL @@ -84,7 +97,7 @@ int UMSDOS_rlookup( ret = umsdos_real_lookup (dir,name,len,result); if (ret == 0){ struct inode *inode = *result; - if (inode == pseudo_root){ + if (inode == pseudo_root && !nopseudo){ /* #Specification: pseudo root / DOS/linux Even in the real root directory (c:\), the directory /linux won't show @@ -102,6 +115,14 @@ int UMSDOS_rlookup( iput (dir); return ret; } +int UMSDOS_rlookup( + struct inode *dir, + const char *name, + int len, + struct inode **result) /* Will hold inode of the file, if successful */ +{ + return umsdos_rlookup_x(dir,name,len,result,0); +} static int UMSDOS_rrmdir ( struct inode *dir, diff --git a/fs/umsdos/symlink.c b/fs/umsdos/symlink.c index 1b1e561c2..6ab27c5dd 100644 --- a/fs/umsdos/symlink.c +++ b/fs/umsdos/symlink.c @@ -6,6 +6,9 @@ * * Extended MS-DOS regular file handling primitives */ +#ifdef MODULE +#include <linux/module.h> +#endif #include <asm/segment.h> #include <asm/system.h> |