diff options
Diffstat (limited to 'fs/umsdos/inode.c')
-rw-r--r-- | fs/umsdos/inode.c | 513 |
1 files changed, 513 insertions, 0 deletions
diff --git a/fs/umsdos/inode.c b/fs/umsdos/inode.c new file mode 100644 index 000000000..40f7feb68 --- /dev/null +++ b/fs/umsdos/inode.c @@ -0,0 +1,513 @@ +/* + * linux/fs/umsdos/inode.c + * + * Written 1993 by Jacques Gelinas + * Inspired from linux/fs/msdos/... by Werner Almesberger + * + */ + +#include <linux/fs.h> +#include <linux/msdos_fs.h> +#include <linux/kernel.h> +#include <linux/sched.h> +#include <linux/errno.h> +#include <asm/segment.h> +#include <linux/string.h> +#include <linux/ctype.h> +#include <linux/stat.h> +#include <linux/umsdos_fs.h> + +#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() */ + +/* #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); + #ifdef MODULE + MOD_DEC_USE_COUNT; + #endif +} + + +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. + Return 0 if ok, or a negative error code if not. +*/ +int umsdos_real_lookup ( + struct inode *dir, + const char *name, + int len, + struct inode **result) /* Will hold inode of the file, if successful */ +{ + int ret; + dir->i_count++; + ret = msdos_lookup (dir,name,len,result); + return ret; +} +/* + Complete the setup of an directory inode. + First, it completes the function pointers, then + it locates the EMD file. If the EMD is there, then plug the + umsdos function table. If not, use the msdos one. +*/ +void umsdos_setup_dir_inode (struct inode *inode) +{ + inode->u.umsdos_i.i_emd_dir = 0; + { + struct inode *emd_dir = umsdos_emd_dir_lookup (inode,0); + extern struct inode_operations umsdos_rdir_inode_operations; + inode->i_op = emd_dir != NULL + ? &umsdos_dir_inode_operations + : &umsdos_rdir_inode_operations; + iput (emd_dir); + } +} +/* + Add some info into an inode so it can find its owner quickly +*/ +void umsdos_set_dirinfo( + struct inode *inode, + struct inode *dir, + off_t f_pos) +{ + struct inode *emd_owner = umsdos_emd_dir_lookup(dir,1); + inode->u.umsdos_i.i_dir_owner = dir->i_ino; + inode->u.umsdos_i.i_emd_owner = emd_owner->i_ino; + iput (emd_owner); + inode->u.umsdos_i.pos = f_pos; +} +/* + Tells if an Umsdos inode has been "patched" once. + Return != 0 if so. +*/ +int umsdos_isinit (struct inode *inode) +{ +#if 1 + return inode->u.umsdos_i.i_emd_owner != 0; +#elif 0 + return inode->i_atime != 0; +#else + return inode->i_count > 1; +#endif +} +/* + Connect the proper tables in the inode and add some info. +*/ +void umsdos_patch_inode ( + struct inode *inode, + struct inode *dir, /* May be NULL */ + off_t f_pos) +{ + /* + This function is called very early to setup the inode, somewhat + too early (called by UMSDOS_read_inode). At this point, we can't + do to much, such as lookup up EMD files and so on. This causes + confusion in the kernel. This is why some initialisation + will be done when dir != NULL only. + + UMSDOS do run piggy back on top of msdos fs. It looks like something + is missing in the VFS to accommodate stacked fs. Still unclear what + (quite honestly). + + Well, maybe one! A new entry "may_unmount" which would allow + the stacked fs to allocate some inode permanently and release + them at the end. Doing that now introduce a problem. unmount + always fail because some inodes are in use. + */ + if (!umsdos_isinit(inode)){ + inode->u.umsdos_i.i_emd_dir = 0; + if (S_ISREG(inode->i_mode)){ + 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; + inode->i_op = &umsdos_file_inode_operations; + is_init = 1; + } + inode->i_op = &umsdos_file_inode_operations; + }else if (S_ISDIR(inode->i_mode)){ + if (dir != NULL){ + umsdos_setup_dir_inode(inode); + } + }else if (S_ISLNK(inode->i_mode)){ + inode->i_op = &umsdos_symlink_inode_operations; + }else if (S_ISCHR(inode->i_mode)){ + inode->i_op = &chrdev_inode_operations; + }else if (S_ISBLK(inode->i_mode)){ + inode->i_op = &blkdev_inode_operations; + }else if (S_ISFIFO(inode->i_mode)){ + init_fifo(inode); + } + if (dir != NULL){ + /* #Specification: inode / umsdos info + The first time an inode is seen (inode->i_count == 1), + the inode number of the EMD file which control this inode + is tagged to this inode. It allows operation such + as notify_change to be handled. + */ + /* + This is done last because it also control the + status of umsdos_isinit() + */ + umsdos_set_dirinfo (inode,dir,f_pos); + } + }else if (dir != NULL){ + /* + Test to see if the info is maintained. + This should be removed when the file system will be proven. + */ + struct inode *emd_owner = umsdos_emd_dir_lookup(dir,1); + iput (emd_owner); + if (emd_owner->i_ino != inode->u.umsdos_i.i_emd_owner){ + printk ("UMSDOS: *** EMD_OWNER ??? *** ino = %ld %ld <> %ld " + ,inode->i_ino,emd_owner->i_ino,inode->u.umsdos_i.i_emd_owner); + } + } +} +/* + Get the inode of the directory which owns this inode. + Return 0 if ok, -EIO if error. +*/ +int umsdos_get_dirowner( + struct inode *inode, + struct inode **result) /* Hold NULL if any error */ + /* else, the inode of the directory */ +{ + int ret = -EIO; + unsigned long ino = inode->u.umsdos_i.i_dir_owner; + *result = NULL; + if (ino == 0){ + printk ("UMSDOS: umsdos_get_dirowner ino == 0\n"); + }else{ + struct inode *dir = *result = iget(inode->i_sb,ino); + if (dir != NULL){ + umsdos_patch_inode (dir,NULL,0); + ret = 0; + } + } + return ret; +} +/* + Load an inode from disk. +*/ +void UMSDOS_read_inode(struct inode *inode) +{ + PRINTK (("read inode %x ino = %d ",inode,inode->i_ino)); + msdos_read_inode(inode); + PRINTK (("ino = %d %d\n",inode->i_ino,inode->i_count)); + if (S_ISDIR(inode->i_mode) + && (inode->u.umsdos_i.u.dir_info.creating != 0 + || inode->u.umsdos_i.u.dir_info.looking != 0 + || inode->u.umsdos_i.u.dir_info.p != NULL)){ + Printk (("read inode %d %d %p\n" + ,inode->u.umsdos_i.u.dir_info.creating + ,inode->u.umsdos_i.u.dir_info.looking + ,inode->u.umsdos_i.u.dir_info.p)); + } + /* #Specification: Inode / post initialisation + To completely initialise an inode, we need access to the owner + directory, so we can locate more info in the EMD file. This is + not available the first time the inode is access, we use + a value in the inode to tell if it has been finally initialised. + + At first, we have tried testing i_count but it was causing + problem. It is possible that two or more process use the + newly accessed inode. While the first one block during + the initialisation (probably while reading the EMD file), the + others believe all is well because i_count > 1. They go banana + with a broken inode. See umsdos_lookup_patch and umsdos_patch_inode. + */ + umsdos_patch_inode(inode,NULL,0); +} + +/* + Update the disk with the inode content +*/ +void UMSDOS_write_inode(struct inode *inode) +{ + struct iattr newattrs; + + PRINTK (("UMSDOS_write_inode emd %d\n",inode->u.umsdos_i.i_emd_owner)); + msdos_write_inode(inode); + newattrs.ia_mtime = inode->i_mtime; + newattrs.ia_atime = inode->i_atime; + newattrs.ia_ctime = inode->i_ctime; + newattrs.ia_valid = ATTR_MTIME | ATTR_ATIME | ATTR_CTIME; + /* + UMSDOS_notify_change is convenient to call here + to update the EMD entry associated with this inode. + But it has the side effect to re"dirt" the inode. + */ + UMSDOS_notify_change (inode, &newattrs); + inode->i_dirt = 0; +} + +int UMSDOS_notify_change(struct inode *inode, struct iattr *attr) +{ + int ret = 0; + + if ((ret = inode_change_ok(inode, attr)) != 0) + return ret; + + if (inode->i_nlink > 0){ + /* #Specification: notify_change / i_nlink > 0 + notify change is only done for inode with nlink > 0. An inode + with nlink == 0 is no longer associated with any entry in + the EMD file, so there is nothing to update. + */ + unsigned long i_emd_owner = inode->u.umsdos_i.i_emd_owner; + if (inode == inode->i_sb->s_mounted){ + /* #Specification: root inode / attributes + I don't know yet how this should work. Normally + the attributes (permissions bits, owner, times) of + a directory are stored in the EMD file of its parent. + + One thing we could do is store the attributes of the root + inode in its own EMD file. A simple entry named "." could + be used for this special case. It would be read once + when the file system is mounted and update in + UMSDOS_notify_change() (right here). + + I am not sure of the behavior of the root inode for + a real UNIX file system. For now, this is a nop. + */ + }else if (i_emd_owner != 0xffffffff && i_emd_owner != 0){ + /* This inode is not a EMD file nor an inode used internally + by MSDOS, so we can update its status. + See emd.c + */ + struct inode *emd_owner = iget (inode->i_sb,i_emd_owner); + PRINTK (("notify change %p ",inode)); + if (emd_owner == NULL){ + printk ("UMSDOS: emd_owner = NULL ???"); + ret = -EPERM; + }else{ + struct file filp; + struct umsdos_dirent entry; + filp.f_pos = inode->u.umsdos_i.pos; + filp.f_reada = 0; + PRINTK (("pos = %d ",filp.f_pos)); + /* Read only the start of the entry since we don't touch */ + /* the name */ + ret = umsdos_emd_dir_read (emd_owner,&filp,(char*)&entry + ,UMSDOS_REC_SIZE); + if (ret == 0){ + if (attr->ia_valid & ATTR_UID) + entry.uid = attr->ia_uid; + if (attr->ia_valid & ATTR_GID) + entry.gid = attr->ia_gid; + if (attr->ia_valid & ATTR_MODE) + entry.mode = attr->ia_mode; + if (attr->ia_valid & ATTR_ATIME) + entry.atime = attr->ia_atime; + if (attr->ia_valid & ATTR_MTIME) + entry.mtime = attr->ia_mtime; + if (attr->ia_valid & ATTR_CTIME) + entry.ctime = attr->ia_ctime; + + entry.nlink = inode->i_nlink; + filp.f_pos = inode->u.umsdos_i.pos; + ret = umsdos_emd_dir_write (emd_owner,&filp,(char*)&entry + ,UMSDOS_REC_SIZE); + + PRINTK (("notify pos %d ret %d nlink %d " + ,inode->u.umsdos_i.pos + ,ret,entry.nlink)); + /* #Specification: notify_change / msdos fs + notify_change operation are done only on the + EMD file. The msdos fs is not even called. + */ + } + iput (emd_owner); + } + PRINTK (("\n")); + } + } + if (ret == 0) + inode_setattr(inode, attr); + return ret; +} + +/* #Specification: function name / convention + A simple convention for function name has been used in + the UMSDOS file system. First all function use the prefix + umsdos_ to avoid name clash with other part of the kernel. + + And standard VFS entry point use the prefix UMSDOS (upper case) + so it's easier to tell them apart. +*/ + +static struct super_operations umsdos_sops = { + UMSDOS_read_inode, + UMSDOS_notify_change, + UMSDOS_write_inode, + UMSDOS_put_inode, + UMSDOS_put_super, + NULL, /* added in 0.96c */ + UMSDOS_statfs, + NULL +}; + +/* + Read the super block of an Extended MS-DOS FS. +*/ +struct super_block *UMSDOS_read_super( + struct super_block *s, + void *data, + int silent) +{ + /* #Specification: mount / options + Umsdos run on top of msdos. Currently, it supports no + mount option, but happily pass all option received to + the msdos driver. I am not sure if all msdos mount option + make sense with Umsdos. Here are at least those who + are useful. + uid= + gid= + + These options affect the operation of umsdos in directories + which do not have an EMD file. They behave like normal + msdos directory, with all limitation of msdos. + */ + struct super_block *sb = msdos_read_super(s,data,silent); + printk ("UMSDOS Alpha 0.5a (compatibility level %d.%d, fast msdos)\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); + } + #ifdef MODULE + MOD_INC_USE_COUNT; + #endif + } + return sb; +} + + +#ifdef MODULE + +char kernel_version[] = UTS_RELEASE; + +static struct file_system_type umsdos_fs_type = { + UMSDOS_read_super, "umsdos", 1, NULL +}; + +int init_module(void) +{ + register_filesystem(&umsdos_fs_type); + return 0; +} + +void cleanup_module(void) +{ + if (MOD_IN_USE) + printk("Umsdos: file system in use, remove delayed\n"); + else + { + unregister_filesystem(&umsdos_fs_type); + } +} + +#endif + |