diff -Naurp linux-2.4.20-ck4/arch/ia64/kernel/sys_ia64.c linux-2.4.20-ck5/arch/ia64/kernel/sys_ia64.c --- linux-2.4.20-ck4/arch/ia64/kernel/sys_ia64.c 2002-11-30 17:02:01.000000000 +1100 +++ linux-2.4.20-ck5/arch/ia64/kernel/sys_ia64.c 2003-04-08 10:52:50.000000000 +1000 @@ -280,120 +280,55 @@ ia64_create_module (const char *name_use * call - it will be removed later once everybody migrates to the new * kernel stat structure that matches the glibc one - Jes */ -static __inline__ int -do_revalidate (struct dentry *dentry) -{ - struct inode * inode = dentry->d_inode; - if (inode->i_op && inode->i_op->revalidate) - return inode->i_op->revalidate(dentry); - return 0; -} static int -cp_ia64_old_stat (struct inode *inode, struct ia64_oldstat *statbuf) +cp_ia64_old_stat (struct kstat *stat, struct ia64_oldstat *statbuf) { struct ia64_oldstat tmp; - unsigned int blocks, indirect; memset(&tmp, 0, sizeof(tmp)); - tmp.st_dev = kdev_t_to_nr(inode->i_dev); - tmp.st_ino = inode->i_ino; - tmp.st_mode = inode->i_mode; - tmp.st_nlink = inode->i_nlink; - SET_STAT_UID(tmp, inode->i_uid); - SET_STAT_GID(tmp, inode->i_gid); - tmp.st_rdev = kdev_t_to_nr(inode->i_rdev); - tmp.st_size = inode->i_size; - tmp.st_atime = inode->i_atime; - tmp.st_mtime = inode->i_mtime; - tmp.st_ctime = inode->i_ctime; -/* - * st_blocks and st_blksize are approximated with a simple algorithm if - * they aren't supported directly by the filesystem. The minix and msdos - * filesystems don't keep track of blocks, so they would either have to - * be counted explicitly (by delving into the file itself), or by using - * this simple algorithm to get a reasonable (although not 100% accurate) - * value. - */ - -/* - * Use minix fs values for the number of direct and indirect blocks. The - * count is now exact for the minix fs except that it counts zero blocks. - * Everything is in units of BLOCK_SIZE until the assignment to - * tmp.st_blksize. - */ -#define D_B 7 -#define I_B (BLOCK_SIZE / sizeof(unsigned short)) - - if (!inode->i_blksize) { - blocks = (tmp.st_size + BLOCK_SIZE - 1) / BLOCK_SIZE; - if (blocks > D_B) { - indirect = (blocks - D_B + I_B - 1) / I_B; - blocks += indirect; - if (indirect > 1) { - indirect = (indirect - 1 + I_B - 1) / I_B; - blocks += indirect; - if (indirect > 1) - blocks++; - } - } - tmp.st_blocks = (BLOCK_SIZE / 512) * blocks; - tmp.st_blksize = BLOCK_SIZE; - } else { - tmp.st_blocks = inode->i_blocks; - tmp.st_blksize = inode->i_blksize; - } + tmp.st_dev = stat->dev; + tmp.st_ino = stat->ino; + tmp.st_mode = stat->mode; + tmp.st_nlink = stat->nlink; + SET_STAT_UID(tmp, stat->uid); + SET_STAT_GID(tmp, stat->gid); + tmp.st_rdev = stat->rdev; + tmp.st_size = stat->size; + tmp.st_atime = stat->atime; + tmp.st_mtime = stat->mtime; + tmp.st_ctime = stat->ctime; + tmp.st_blocks = stat->blocks; + tmp.st_blksize = stat->blksize; return copy_to_user(statbuf,&tmp,sizeof(tmp)) ? -EFAULT : 0; } asmlinkage long ia64_oldstat (char *filename, struct ia64_oldstat *statbuf) { - struct nameidata nd; - int error; + struct kstat stat; + int error = vfs_stat(filename, &stat); - error = user_path_walk(filename, &nd); - if (!error) { - error = do_revalidate(nd.dentry); - if (!error) - error = cp_ia64_old_stat(nd.dentry->d_inode, statbuf); - path_release(&nd); - } - return error; + return error ? error : cp_ia64_old_stat(&stat, statbuf); } asmlinkage long -ia64_oldlstat (char *filename, struct ia64_oldstat *statbuf) { - struct nameidata nd; - int error; - - error = user_path_walk_link(filename, &nd); - if (!error) { - error = do_revalidate(nd.dentry); - if (!error) - error = cp_ia64_old_stat(nd.dentry->d_inode, statbuf); - path_release(&nd); - } - return error; +ia64_oldlstat (char *filename, struct ia64_oldstat *statbuf) +{ + struct kstat stat; + int error = vfs_lstat(filename, &stat); + + return error ? error : cp_ia64_old_stat(&stat, statbuf); } asmlinkage long ia64_oldfstat (unsigned int fd, struct ia64_oldstat *statbuf) { - struct file * f; - int err = -EBADF; + struct kstat stat; + int error = vfs_fstat(fd, &stat); - f = fget(fd); - if (f) { - struct dentry * dentry = f->f_dentry; - - err = do_revalidate(dentry); - if (!err) - err = cp_ia64_old_stat(dentry->d_inode, statbuf); - fput(f); - } - return err; + return error ? error : cp_ia64_old_stat(&stat, statbuf); } #endif diff -Naurp linux-2.4.20-ck4/arch/mips/kernel/sysirix.c linux-2.4.20-ck5/arch/mips/kernel/sysirix.c --- linux-2.4.20-ck4/arch/mips/kernel/sysirix.c 2002-11-30 17:02:01.000000000 +1100 +++ linux-2.4.20-ck5/arch/mips/kernel/sysirix.c 2003-04-08 10:52:50.000000000 +1000 @@ -1178,8 +1178,9 @@ linux_to_irix_dev_t (dev_t t) return MAJOR (t) << 18 | MINOR (t); } -static inline int irix_xstat32_xlate(struct stat *kb, void *ubuf) +static inline int irix_xstat32_xlate(struct kstat *kb, void *ubuf) { + /* XXX_SIZE */ struct xstat32 { u32 st_dev, st_pad1[3], st_ino, st_mode, st_nlink, st_uid, st_gid; u32 st_rdev, st_pad2[2], st_size, st_pad3; @@ -1191,29 +1192,30 @@ static inline int irix_xstat32_xlate(str u32 st_pad4[8]; } ub; - ub.st_dev = linux_to_irix_dev_t (kb->st_dev); - ub.st_ino = kb->st_ino; - ub.st_mode = kb->st_mode; - ub.st_nlink = kb->st_nlink; - ub.st_uid = kb->st_uid; - ub.st_gid = kb->st_gid; - ub.st_rdev = linux_to_irix_dev_t (kb->st_rdev); - ub.st_size = kb->st_size; - ub.st_atime0 = kb->st_atime; + ub.st_dev = linux_to_irix_dev_t (kb->dev); + ub.st_ino = kb->ino; + ub.st_mode = kb->mode; + ub.st_nlink = kb->nlink; + ub.st_uid = kb->uid; + ub.st_gid = kb->gid; + ub.st_rdev = linux_to_irix_dev_t (kb->rdev); + ub.st_size = kb->size; + ub.st_atime0 = kb->atime; ub.st_atime1 = 0; - ub.st_mtime0 = kb->st_mtime; + ub.st_mtime0 = kb->mtime; ub.st_mtime1 = 0; - ub.st_ctime0 = kb->st_ctime; + ub.st_ctime0 = kb->ctime; ub.st_ctime1 = 0; - ub.st_blksize = kb->st_blksize; - ub.st_blocks = kb->st_blocks; + ub.st_blksize = kb->blksize; + ub.st_blocks = kb->blocks; strcpy (ub.st_fstype, "efs"); return copy_to_user(ubuf, &ub, sizeof(ub)) ? -EFAULT : 0; } -static inline void irix_xstat64_xlate(struct stat *sb) +static inline int irix_xstat64_xlate(struct kstat *sb, void *ubuf) { + /* XXX_SIZE */ struct xstat64 { u32 st_dev; s32 st_pad1[3]; unsigned long long st_ino; @@ -1229,38 +1231,37 @@ static inline void irix_xstat64_xlate(st s32 st_pad4[8]; } ks; - ks.st_dev = linux_to_irix_dev_t (sb->st_dev); + ks.st_dev = sb->dev; ks.st_pad1[0] = ks.st_pad1[1] = ks.st_pad1[2] = 0; - ks.st_ino = (unsigned long long) sb->st_ino; - ks.st_mode = (u32) sb->st_mode; - ks.st_nlink = (u32) sb->st_nlink; - ks.st_uid = (s32) sb->st_uid; - ks.st_gid = (s32) sb->st_gid; - ks.st_rdev = linux_to_irix_dev_t (sb->st_rdev); + ks.st_ino = (unsigned long long) sb->ino; + ks.st_mode = (u32) sb->mode; + ks.st_nlink = (u32) sb->nlink; + ks.st_uid = (s32) sb->uid; + ks.st_gid = (s32) sb->gid; + ks.st_rdev = linux_to_irix_dev_t (sb->rdev); ks.st_pad2[0] = ks.st_pad2[1] = 0; - ks.st_size = (long long) sb->st_size; + ks.st_size = (long long) sb->size; ks.st_pad3 = 0; /* XXX hackety hack... */ - ks.st_atime.tv_sec = (s32) sb->st_atime; ks.st_atime.tv_nsec = 0; - ks.st_mtime.tv_sec = (s32) sb->st_atime; ks.st_mtime.tv_nsec = 0; - ks.st_ctime.tv_sec = (s32) sb->st_atime; ks.st_ctime.tv_nsec = 0; + ks.st_atime.tv_sec = (s32) sb->atime; ks.st_atime.tv_nsec = 0; + ks.st_mtime.tv_sec = (s32) sb->atime; ks.st_mtime.tv_nsec = 0; + ks.st_ctime.tv_sec = (s32) sb->atime; ks.st_ctime.tv_nsec = 0; - ks.st_blksize = (s32) sb->st_blksize; - ks.st_blocks = (long long) sb->st_blocks; + ks.st_blksize = (s32) sb->blksize; + ks.st_blocks = (long long) sb->blocks; memset(ks.st_fstype, 0, 16); ks.st_pad4[0] = ks.st_pad4[1] = ks.st_pad4[2] = ks.st_pad4[3] = 0; ks.st_pad4[4] = ks.st_pad4[5] = ks.st_pad4[6] = ks.st_pad4[7] = 0; /* Now write it all back. */ - copy_to_user(sb, &ks, sizeof(struct xstat64)); + copy_to_user(ubuf, &ks, sizeof(struct xstat64)); } -extern asmlinkage int sys_newstat(char * filename, struct stat * statbuf); - asmlinkage int irix_xstat(int version, char *filename, struct stat *statbuf) { int retval; + struct kstat kb; #ifdef DEBUG_XSTAT printk("[%s:%d] Wheee.. irix_xstat(%d,%s,%p) ", @@ -1268,12 +1269,7 @@ asmlinkage int irix_xstat(int version, c #endif switch(version) { case 2: { - struct stat kb; - mm_segment_t old_fs; - - old_fs = get_fs(); set_fs(get_ds()); - retval = sys_newstat(filename, &kb); - set_fs(old_fs); + retval = vfs_stat(filename, &kb); #ifdef DEBUG_XSTAT printk("retval[%d]\n", retval); #endif @@ -1284,16 +1280,14 @@ asmlinkage int irix_xstat(int version, c } case 3: { - retval = sys_newstat(filename, statbuf); + retval = vfs_stat(filename, &kb); #ifdef DEBUG_XSTAT printk("retval[%d]\n", retval); #endif if(retval) goto out; - - irix_xstat64_xlate(statbuf); - retval = 0; - break; + retval = irix_xstat64_xlate(&kb, statbuf); + goto out; } default: @@ -1305,11 +1299,10 @@ out: return retval; } -extern asmlinkage int sys_newlstat(char * filename, struct stat * statbuf); - asmlinkage int irix_lxstat(int version, char *filename, struct stat *statbuf) { int error; + struct kstat kb; #ifdef DEBUG_XSTAT printk("[%s:%d] Wheee.. irix_lxstat(%d,%s,%p) ", @@ -1317,12 +1310,7 @@ asmlinkage int irix_lxstat(int version, #endif switch(version) { case 2: { - struct stat kb; - mm_segment_t old_fs; - - old_fs = get_fs(); set_fs(get_ds()); - error = sys_newlstat(filename, &kb); - set_fs(old_fs); + error = vfs_lstat(filename, &kb); #ifdef DEBUG_XSTAT printk("error[%d]\n", error); #endif @@ -1333,18 +1321,15 @@ asmlinkage int irix_lxstat(int version, } case 3: { - error = sys_newlstat(filename, statbuf); + error = vfs_lstat(filename, &kb); #ifdef DEBUG_XSTAT printk("error[%d]\n", error); #endif if(error) goto out; - - irix_xstat64_xlate(statbuf); - error = 0; + error = irix_xstat64_xlate(&kb, statbuf); goto out; } - default: error = -EINVAL; goto out; @@ -1354,11 +1339,10 @@ out: return error; } -extern asmlinkage int sys_newfstat(unsigned int fd, struct stat * statbuf); - asmlinkage int irix_fxstat(int version, int fd, struct stat *statbuf) { int error; + struct stat kb; #ifdef DEBUG_XSTAT printk("[%s:%d] Wheee.. irix_fxstat(%d,%d,%p) ", @@ -1366,12 +1350,7 @@ asmlinkage int irix_fxstat(int version, #endif switch(version) { case 2: { - struct stat kb; - mm_segment_t old_fs; - - old_fs = get_fs(); set_fs(get_ds()); - error = sys_newfstat(fd, &kb); - set_fs(old_fs); + error = vfs_fstat(fd, &kb); #ifdef DEBUG_XSTAT printk("error[%d]\n", error); #endif @@ -1382,15 +1361,13 @@ asmlinkage int irix_fxstat(int version, } case 3: { - error = sys_newfstat(fd, statbuf); + error = vfs_fstat(fd, &kb); #ifdef DEBUG_XSTAT printk("error[%d]\n", error); #endif if(error) goto out; - - irix_xstat64_xlate(statbuf); - error = 0; + error = irix_xstat64_xlate(&kb, statbuf); goto out; } diff -Naurp linux-2.4.20-ck4/arch/mips64/kernel/linux32.c linux-2.4.20-ck5/arch/mips64/kernel/linux32.c --- linux-2.4.20-ck4/arch/mips64/kernel/linux32.c 2002-11-30 17:02:02.000000000 +1100 +++ linux-2.4.20-ck5/arch/mips64/kernel/linux32.c 2003-04-08 10:52:50.000000000 +1000 @@ -55,128 +55,51 @@ #define merge_64(r1,r2) ((((r2) & 0xffffffffUL) << 32) + ((r1) & 0xffffffffUL)) #endif -/* - * Revalidate the inode. This is required for proper NFS attribute caching. - */ -static __inline__ int -do_revalidate(struct dentry *dentry) -{ - struct inode * inode = dentry->d_inode; - - if (inode->i_op && inode->i_op->revalidate) - return inode->i_op->revalidate(dentry); - - return 0; -} - -static int cp_new_stat32(struct inode * inode, struct stat32 * statbuf) +static int cp_new_stat32(struct kstat *stat, struct stat32 * statbuf) { + /* XXX_SIZE */ struct stat32 tmp; - unsigned int blocks, indirect; memset(&tmp, 0, sizeof(tmp)); - tmp.st_dev = kdev_t_to_nr(inode->i_dev); - tmp.st_ino = inode->i_ino; - tmp.st_mode = inode->i_mode; - tmp.st_nlink = inode->i_nlink; - SET_STAT_UID(tmp, inode->i_uid); - SET_STAT_GID(tmp, inode->i_gid); - tmp.st_rdev = kdev_t_to_nr(inode->i_rdev); - tmp.st_size = inode->i_size; - tmp.st_atime = inode->i_atime; - tmp.st_mtime = inode->i_mtime; - tmp.st_ctime = inode->i_ctime; - - /* - * st_blocks and st_blksize are approximated with a simple algorithm if - * they aren't supported directly by the filesystem. The minix and msdos - * filesystems don't keep track of blocks, so they would either have to - * be counted explicitly (by delving into the file itself), or by using - * this simple algorithm to get a reasonable (although not 100% - * accurate) value. - */ - - /* - * Use minix fs values for the number of direct and indirect blocks. - * The count is now exact for the minix fs except that it counts zero - * blocks. Everything is in units of BLOCK_SIZE until the assignment - * to tmp.st_blksize. - */ -#define D_B 7 -#define I_B (BLOCK_SIZE / sizeof(unsigned short)) - - if (!inode->i_blksize) { - blocks = (tmp.st_size + BLOCK_SIZE - 1) / BLOCK_SIZE; - if (blocks > D_B) { - indirect = (blocks - D_B + I_B - 1) / I_B; - blocks += indirect; - if (indirect > 1) { - indirect = (indirect - 1 + I_B - 1) / I_B; - blocks += indirect; - if (indirect > 1) - blocks++; - } - } - tmp.st_blocks = (BLOCK_SIZE / 512) * blocks; - tmp.st_blksize = BLOCK_SIZE; - } else { - tmp.st_blocks = inode->i_blocks; - tmp.st_blksize = inode->i_blksize; - } + tmp.st_dev = stat->dev; + tmp.st_ino = stat->ino; + tmp.st_mode = stat->mode; + tmp.st_nlink = stat->nlink; + SET_STAT_UID(tmp, stat->uid); + SET_STAT_GID(tmp, stat->gid); + tmp.st_rdev = stat->rdev; + tmp.st_size = stat->size; + tmp.st_atime = stat->atime; + tmp.st_mtime = stat->mtime; + tmp.st_ctime = stat->ctime; + tmp.st_blocks = stat->blocks; + tmp.st_blksize = stat->blksize; return copy_to_user(statbuf,&tmp,sizeof(tmp)) ? -EFAULT : 0; } asmlinkage int sys32_newstat(char * filename, struct stat32 *statbuf) { - struct nameidata nd; - int error; + struct kstat stat; + int error = vfs_stat(filename, &stat); - error = user_path_walk(filename, &nd); - if (!error) { - error = do_revalidate(nd.dentry); - if (!error) - error = cp_new_stat32(nd.dentry->d_inode, statbuf); - - path_release(&nd); - } - - return error; + return error ? cp_new_stat32(&stat, statbuf); } asmlinkage int sys32_newlstat(char * filename, struct stat32 *statbuf) { - struct nameidata nd; - int error; - - error = user_path_walk_link(filename, &nd); - if (!error) { - error = do_revalidate(nd.dentry); - if (!error) - error = cp_new_stat32(nd.dentry->d_inode, statbuf); - - path_release(&nd); - } + struct kstat stat; + int error = vfs_lstat(filename, &stat); - return error; + return error ? cp_new_stat32(&stat, statbuf); } asmlinkage long sys32_newfstat(unsigned int fd, struct stat32 * statbuf) { - struct file * f; - int err = -EBADF; - - f = fget(fd); - if (f) { - struct dentry * dentry = f->f_dentry; - - err = do_revalidate(dentry); - if (!err) - err = cp_new_stat32(dentry->d_inode, statbuf); - fput(f); - } + struct kstat stat; + int error = vfs_fstat(fd, &stat); - return err; + return error ? cp_new_stat32(&stat, statbuf); } asmlinkage int sys_mmap2(void) {return 0;} diff -Naurp linux-2.4.20-ck4/arch/parisc/hpux/fs.c linux-2.4.20-ck5/arch/parisc/hpux/fs.c --- linux-2.4.20-ck4/arch/parisc/hpux/fs.c 2002-11-30 17:02:02.000000000 +1100 +++ linux-2.4.20-ck5/arch/parisc/hpux/fs.c 2003-04-08 10:52:50.000000000 +1000 @@ -120,110 +120,47 @@ int hpux_mount(const char *fs, const cha return -ENOSYS; } -static int cp_hpux_stat(struct inode * inode, struct hpux_stat64 * statbuf) +static int cp_hpux_stat(struct kstat *stat, struct hpux_stat64 * statbuf) { struct hpux_stat64 tmp; - unsigned int blocks, indirect; memset(&tmp, 0, sizeof(tmp)); - tmp.st_dev = kdev_t_to_nr(inode->i_dev); - tmp.st_ino = inode->i_ino; - tmp.st_mode = inode->i_mode; - tmp.st_nlink = inode->i_nlink; - tmp.st_uid = inode->i_uid; - tmp.st_gid = inode->i_gid; - tmp.st_rdev = kdev_t_to_nr(inode->i_rdev); - tmp.st_size = inode->i_size; - tmp.st_atime = inode->i_atime; - tmp.st_mtime = inode->i_mtime; - tmp.st_ctime = inode->i_ctime; - -#define D_B 7 -#define I_B (BLOCK_SIZE / sizeof(unsigned short)) - - if (!inode->i_blksize) { - blocks = (tmp.st_size + BLOCK_SIZE - 1) / BLOCK_SIZE; - if (blocks > D_B) { - indirect = (blocks - D_B + I_B - 1) / I_B; - blocks += indirect; - if (indirect > 1) { - indirect = (indirect - 1 + I_B - 1) / I_B; - blocks += indirect; - if (indirect > 1) - blocks++; - } - } - tmp.st_blocks = (BLOCK_SIZE / 512) * blocks; - tmp.st_blksize = BLOCK_SIZE; - } else { - tmp.st_blocks = inode->i_blocks; - tmp.st_blksize = inode->i_blksize; - } + tmp.st_dev = stat->dev; + tmp.st_ino = stat->ino; + tmp.st_mode = stat->mode; + tmp.st_nlink = stat->nlink; + tmp.st_uid = stat->uid; + tmp.st_gid = stat->gid; + tmp.st_rdev = stat->rdev; + tmp.st_size = stat->size; + tmp.st_atime = stat->atime; + tmp.st_mtime = stat->mtime; + tmp.st_ctime = stat->ctime; + tmp.st_blocks = stat->blocks; + tmp.st_blksize = stat->blksize; return copy_to_user(statbuf,&tmp,sizeof(tmp)) ? -EFAULT : 0; } -/* - * Revalidate the inode. This is required for proper NFS attribute caching. - * Blatently copied wholesale from fs/stat.c - */ -static __inline__ int -do_revalidate(struct dentry *dentry) -{ - struct inode * inode = dentry->d_inode; - if (inode->i_op && inode->i_op->revalidate) - return inode->i_op->revalidate(dentry); - return 0; -} - long hpux_stat64(const char *path, struct hpux_stat64 *buf) { - struct nameidata nd; - int error; + struct kstat stat; + int error = vfs_stat(path, &stat); - lock_kernel(); - error = user_path_walk(path, &nd); - if (!error) { - error = do_revalidate(nd.dentry); - if (!error) - error = cp_hpux_stat(nd.dentry->d_inode, buf); - path_release(&nd); - } - unlock_kernel(); - return error; + return error ? error : cp_hpux_stat(&stat, buf); } long hpux_fstat64(unsigned int fd, struct hpux_stat64 *statbuf) { - struct file * f; - int err = -EBADF; + struct kstat stat; + int error = vfs_fstat(fd, &stat); - lock_kernel(); - f = fget(fd); - if (f) { - struct dentry * dentry = f->f_dentry; - - err = do_revalidate(dentry); - if (!err) - err = cp_hpux_stat(dentry->d_inode, statbuf); - fput(f); - } - unlock_kernel(); - return err; + return error ? error : cp_hpux_stat(&stat, buf); } long hpux_lstat64(char *filename, struct hpux_stat64 *statbuf) { - struct nameidata nd; - int error; + struct kstat stat; + int error = vfs_lstat(path, &stat); - lock_kernel(); - error = user_path_walk_link(filename, &nd); - if (!error) { - error = do_revalidate(nd.dentry); - if (!error) - error = cp_hpux_stat(nd.dentry->d_inode, statbuf); - path_release(&nd); - } - unlock_kernel(); - return error; + return error ? error : cp_hpux_stat(&stat, buf); } diff -Naurp linux-2.4.20-ck4/arch/s390x/kernel/linux32.c linux-2.4.20-ck5/arch/s390x/kernel/linux32.c --- linux-2.4.20-ck4/arch/s390x/kernel/linux32.c 2002-08-03 13:14:44.000000000 +1000 +++ linux-2.4.20-ck5/arch/s390x/kernel/linux32.c 2003-04-08 10:52:50.000000000 +1000 @@ -1486,76 +1486,27 @@ out_nofds: return ret; } -static int cp_new_stat32(struct inode *inode, struct stat32 *statbuf) +static int cp_new_stat32(struct kstat *stat, struct stat32 *statbuf) { - unsigned long ino, blksize, blocks; - kdev_t dev, rdev; - umode_t mode; - nlink_t nlink; - uid_t uid; - gid_t gid; - off_t size; - time_t atime, mtime, ctime; + /* XXX_SIZE */ int err; - /* Stream the loads of inode data into the load buffer, - * then we push it all into the store buffer below. This - * should give optimal cache performance. - */ - ino = inode->i_ino; - dev = inode->i_dev; - mode = inode->i_mode; - nlink = inode->i_nlink; - uid = inode->i_uid; - gid = inode->i_gid; - rdev = inode->i_rdev; - size = inode->i_size; - atime = inode->i_atime; - mtime = inode->i_mtime; - ctime = inode->i_ctime; - blksize = inode->i_blksize; - blocks = inode->i_blocks; - - err = put_user(kdev_t_to_nr(dev), &statbuf->st_dev); - err |= put_user(ino, &statbuf->st_ino); - err |= put_user(mode, &statbuf->st_mode); - err |= put_user(nlink, &statbuf->st_nlink); - err |= put_user(high2lowuid(uid), &statbuf->st_uid); - err |= put_user(high2lowgid(gid), &statbuf->st_gid); - err |= put_user(kdev_t_to_nr(rdev), &statbuf->st_rdev); - err |= put_user(size, &statbuf->st_size); - err |= put_user(atime, &statbuf->st_atime); + err = put_user(stat->dev, &statbuf->st_dev); + err |= put_user(stat->ino, &statbuf->st_ino); + err |= put_user(stat->mode, &statbuf->st_mode); + err |= put_user(stat->nlink, &statbuf->st_nlink); + err |= put_user(high2lowuid(stat->uid), &statbuf->st_uid); + err |= put_user(high2lowgid(stat->gid), &statbuf->st_gid); + err |= put_user(stat->rdev, &statbuf->st_rdev); + err |= put_user(stat->size, &statbuf->st_size); + err |= put_user(stat->atime, &statbuf->st_atime); err |= put_user(0, &statbuf->__unused1); - err |= put_user(mtime, &statbuf->st_mtime); + err |= put_user(stat->mtime, &statbuf->st_mtime); err |= put_user(0, &statbuf->__unused2); - err |= put_user(ctime, &statbuf->st_ctime); + err |= put_user(stat->ctime, &statbuf->st_ctime); err |= put_user(0, &statbuf->__unused3); - if (blksize) { - err |= put_user(blksize, &statbuf->st_blksize); - err |= put_user(blocks, &statbuf->st_blocks); - } else { - unsigned int tmp_blocks; - -#define D_B 7 -#define I_B (BLOCK_SIZE / sizeof(unsigned short)) - tmp_blocks = (size + BLOCK_SIZE - 1) / BLOCK_SIZE; - if (tmp_blocks > D_B) { - unsigned int indirect; - - indirect = (tmp_blocks - D_B + I_B - 1) / I_B; - tmp_blocks += indirect; - if (indirect > 1) { - indirect = (indirect - 1 + I_B - 1) / I_B; - tmp_blocks += indirect; - if (indirect > 1) - tmp_blocks++; - } - } - err |= put_user(BLOCK_SIZE, &statbuf->st_blksize); - err |= put_user((BLOCK_SIZE / 512) * tmp_blocks, &statbuf->st_blocks); -#undef D_B -#undef I_B - } + err |= put_user(stat->blksize, &statbuf->st_blksize); + err |= put_user(stat->blocks, &statbuf->st_blocks); /* fixme err |= put_user(0, &statbuf->__unused4[0]); err |= put_user(0, &statbuf->__unused4[1]); @@ -1564,62 +1515,28 @@ static int cp_new_stat32(struct inode *i return err; } -/* Perhaps this belongs in fs.h or similar. -DaveM */ -static __inline__ int -do_revalidate(struct dentry *dentry) -{ - struct inode * inode = dentry->d_inode; - if (inode->i_op && inode->i_op->revalidate) - return inode->i_op->revalidate(dentry); - return 0; -} - asmlinkage int sys32_newstat(char * filename, struct stat32 *statbuf) { - struct nameidata nd; - int error; + struct kstat stat; + int error = vfs_stat(filename, &stat); - error = user_path_walk(filename, &nd); - if (!error) { - error = do_revalidate(nd.dentry); - if (!error) - error = cp_new_stat32(nd.dentry->d_inode, statbuf); - path_release(&nd); - } - return error; + return error ? error : cp_new_stat32(&stat, statbuf); } asmlinkage int sys32_newlstat(char * filename, struct stat32 *statbuf) { - struct nameidata nd; - int error; + struct kstat stat; + int error = vfs_lstat(filename, &stat); - error = user_path_walk_link(filename, &nd); - if (!error) { - error = do_revalidate(nd.dentry); - if (!error) - error = cp_new_stat32(nd.dentry->d_inode, statbuf); - - path_release(&nd); - } - return error; + return error ? error : cp_new_stat32(&stat, statbuf); } asmlinkage int sys32_newfstat(unsigned int fd, struct stat32 *statbuf) { - struct file *f; - int err = -EBADF; - - f = fget(fd); - if (f) { - struct dentry * dentry = f->f_dentry; + struct kstat stat; + int error = vfs_fstat(fd, &stat); - err = do_revalidate(dentry); - if (!err) - err = cp_new_stat32(dentry->d_inode, statbuf); - fput(f); - } - return err; + return error ? error : cp_new_stat32(&stat, statbuf); } extern asmlinkage int sys_sysfs(int option, unsigned long arg1, unsigned long arg2); @@ -4080,89 +3997,55 @@ struct stat64_emu31 { }; static inline int -putstat64 (struct stat64_emu31 *ubuf, struct stat *kbuf) +putstat64 (struct stat64_emu31 *ubuf, struct kstat *kbuf) { struct stat64_emu31 tmp; memset(&tmp, 0, sizeof(tmp)); - tmp.st_dev = (unsigned short)kbuf->st_dev; - tmp.st_ino = kbuf->st_ino; - tmp.__st_ino = (u32)kbuf->st_ino; - tmp.st_mode = kbuf->st_mode; - tmp.st_nlink = (unsigned int)kbuf->st_nlink; - tmp.st_uid = kbuf->st_uid; - tmp.st_gid = kbuf->st_gid; - tmp.st_rdev = (unsigned short)kbuf->st_rdev; - tmp.st_size = kbuf->st_size; - tmp.st_blksize = (u32)kbuf->st_blksize; - tmp.st_blocks = (u32)kbuf->st_blocks; - tmp.st_atime = (u32)kbuf->st_atime; - tmp.st_mtime = (u32)kbuf->st_mtime; - tmp.st_ctime = (u32)kbuf->st_ctime; + tmp.st_dev = (unsigned short)kbuf->dev; + tmp.st_ino = kbuf->ino; + tmp.__st_ino = (u32)kbuf->ino; + tmp.st_mode = kbuf->mode; + tmp.st_nlink = (unsigned int)kbuf->nlink; + tmp.st_uid = kbuf->uid; + tmp.st_gid = kbuf->gid; + tmp.st_rdev = (unsigned short)kbuf->rdev; + tmp.st_size = kbuf->size; + tmp.st_blksize = (u32)kbuf->blksize; + tmp.st_blocks = (u32)kbuf->blocks; + tmp.st_atime = (u32)kbuf->atime; + tmp.st_mtime = (u32)kbuf->mtime; + tmp.st_ctime = (u32)kbuf->ctime; return copy_to_user(ubuf,&tmp,sizeof(tmp)) ? -EFAULT : 0; } -extern asmlinkage long sys_newstat(char * filename, struct stat * statbuf); - asmlinkage long sys32_stat64(char * filename, struct stat64_emu31 * statbuf, long flags) { - int ret; - struct stat s; - char * tmp; - int err; - mm_segment_t old_fs = get_fs(); - - tmp = getname(filename); - err = PTR_ERR(tmp); - if (IS_ERR(tmp)) - return err; - - set_fs (KERNEL_DS); - ret = sys_newstat(tmp, &s); - set_fs (old_fs); - putname(tmp); + struct kstat s; + int ret = vfs_stat(filename, &s); + if (putstat64 (statbuf, &s)) return -EFAULT; return ret; } -extern asmlinkage long sys_newlstat(char * filename, struct stat * statbuf); - asmlinkage long sys32_lstat64(char * filename, struct stat64_emu31 * statbuf, long flags) { - int ret; - struct stat s; - char * tmp; - int err; - mm_segment_t old_fs = get_fs(); - - tmp = getname(filename); - err = PTR_ERR(tmp); - if (IS_ERR(tmp)) - return err; - - set_fs (KERNEL_DS); - ret = sys_newlstat(tmp, &s); - set_fs (old_fs); - putname(tmp); + struct kstat s; + int ret = vfs_lstat(filename, &s); + if (putstat64 (statbuf, &s)) return -EFAULT; return ret; } -extern asmlinkage long sys_newfstat(unsigned int fd, struct stat * statbuf); - asmlinkage long sys32_fstat64(unsigned long fd, struct stat64_emu31 * statbuf, long flags) { - int ret; - struct stat s; - mm_segment_t old_fs = get_fs(); - - set_fs (KERNEL_DS); - ret = sys_newfstat(fd, &s); - set_fs (old_fs); + struct kstat s; + int ret = vfs_fstat(fd, &s); + if (putstat64 (statbuf, &s)) return -EFAULT; return ret; diff -Naurp linux-2.4.20-ck4/arch/sparc64/kernel/sys_sparc32.c linux-2.4.20-ck5/arch/sparc64/kernel/sys_sparc32.c --- linux-2.4.20-ck4/arch/sparc64/kernel/sys_sparc32.c 2002-11-30 17:02:06.000000000 +1100 +++ linux-2.4.20-ck5/arch/sparc64/kernel/sys_sparc32.c 2003-04-08 10:52:50.000000000 +1000 @@ -1475,138 +1475,54 @@ out_nofds: return ret; } -static int cp_new_stat32(struct inode *inode, struct stat32 *statbuf) +static int cp_new_stat32(struct kstat *stat, struct stat32 *statbuf) { - unsigned long ino, blksize, blocks; - kdev_t dev, rdev; - umode_t mode; - nlink_t nlink; - uid_t uid; - gid_t gid; - off_t size; - time_t atime, mtime, ctime; + /* XXX_SIZE */ int err; - - /* Stream the loads of inode data into the load buffer, - * then we push it all into the store buffer below. This - * should give optimal cache performance. - */ - ino = inode->i_ino; - dev = inode->i_dev; - mode = inode->i_mode; - nlink = inode->i_nlink; - uid = inode->i_uid; - gid = inode->i_gid; - rdev = inode->i_rdev; - size = inode->i_size; - atime = inode->i_atime; - mtime = inode->i_mtime; - ctime = inode->i_ctime; - blksize = inode->i_blksize; - blocks = inode->i_blocks; - - err = put_user(kdev_t_to_nr(dev), &statbuf->st_dev); - err |= put_user(ino, &statbuf->st_ino); - err |= put_user(mode, &statbuf->st_mode); - err |= put_user(nlink, &statbuf->st_nlink); - err |= put_user(high2lowuid(uid), &statbuf->st_uid); - err |= put_user(high2lowgid(gid), &statbuf->st_gid); - err |= put_user(kdev_t_to_nr(rdev), &statbuf->st_rdev); - err |= put_user(size, &statbuf->st_size); - err |= put_user(atime, &statbuf->st_atime); + err = put_user(stat->dev, &statbuf->st_dev); + err |= put_user(stat->ino, &statbuf->st_ino); + err |= put_user(stat->mode, &statbuf->st_mode); + err |= put_user(stat->nlink, &statbuf->st_nlink); + err |= put_user(high2lowuid(stat->uid), &statbuf->st_uid); + err |= put_user(high2lowgid(stat->gid), &statbuf->st_gid); + err |= put_user(kdev_t_to_nr(stat->rdev), &statbuf->st_rdev); + err |= put_user(stat->size, &statbuf->st_size); + err |= put_user(stat->atime, &statbuf->st_atime); err |= put_user(0, &statbuf->__unused1); - err |= put_user(mtime, &statbuf->st_mtime); + err |= put_user(stat->mtime, &statbuf->st_mtime); err |= put_user(0, &statbuf->__unused2); - err |= put_user(ctime, &statbuf->st_ctime); + err |= put_user(stat->ctime, &statbuf->st_ctime); err |= put_user(0, &statbuf->__unused3); - if (blksize) { - err |= put_user(blksize, &statbuf->st_blksize); - err |= put_user(blocks, &statbuf->st_blocks); - } else { - unsigned int tmp_blocks; - -#define D_B 7 -#define I_B (BLOCK_SIZE / sizeof(unsigned short)) - tmp_blocks = (size + BLOCK_SIZE - 1) / BLOCK_SIZE; - if (tmp_blocks > D_B) { - unsigned int indirect; - - indirect = (tmp_blocks - D_B + I_B - 1) / I_B; - tmp_blocks += indirect; - if (indirect > 1) { - indirect = (indirect - 1 + I_B - 1) / I_B; - tmp_blocks += indirect; - if (indirect > 1) - tmp_blocks++; - } - } - err |= put_user(BLOCK_SIZE, &statbuf->st_blksize); - err |= put_user((BLOCK_SIZE / 512) * tmp_blocks, &statbuf->st_blocks); -#undef D_B -#undef I_B - } + err |= put_user(stat->blksize, &statbuf->st_blksize); + err |= put_user(stat->blocks, &statbuf->st_blocks); err |= put_user(0, &statbuf->__unused4[0]); err |= put_user(0, &statbuf->__unused4[1]); return err; } -/* Perhaps this belongs in fs.h or similar. -DaveM */ -static __inline__ int -do_revalidate(struct dentry *dentry) -{ - struct inode * inode = dentry->d_inode; - if (inode->i_op && inode->i_op->revalidate) - return inode->i_op->revalidate(dentry); - return 0; -} - asmlinkage int sys32_newstat(char * filename, struct stat32 *statbuf) { - struct nameidata nd; - int error; - - error = user_path_walk(filename, &nd); - if (!error) { - error = do_revalidate(nd.dentry); - if (!error) - error = cp_new_stat32(nd.dentry->d_inode, statbuf); - path_release(&nd); - } - return error; + struct kstat stat; + int error = vfs_stat(filename, &stat); + + return error ? error : cp_new_stat32(&stat, statbuf); } asmlinkage int sys32_newlstat(char * filename, struct stat32 *statbuf) { - struct nameidata nd; - int error; + struct kstat stat; + int error = vfs_lstat(filename, &stat); - error = user_path_walk_link(filename, &nd); - if (!error) { - error = do_revalidate(nd.dentry); - if (!error) - error = cp_new_stat32(nd.dentry->d_inode, statbuf); - - path_release(&nd); - } - return error; + return error ? error : cp_new_stat32(&stat, statbuf); } asmlinkage int sys32_newfstat(unsigned int fd, struct stat32 *statbuf) { - struct file *f; - int err = -EBADF; - - f = fget(fd); - if (f) { - struct dentry * dentry = f->f_dentry; - - err = do_revalidate(dentry); - if (!err) - err = cp_new_stat32(dentry->d_inode, statbuf); - fput(f); - } - return err; + struct kstat stat; + int error = vfs_fstat(fd, &stat); + + return error ? error : cp_new_stat32(&stat, statbuf); } extern asmlinkage int sys_sysfs(int option, unsigned long arg1, unsigned long arg2); diff -Naurp linux-2.4.20-ck4/arch/sparc64/solaris/fs.c linux-2.4.20-ck5/arch/sparc64/solaris/fs.c --- linux-2.4.20-ck4/arch/sparc64/solaris/fs.c 2001-09-21 07:11:57.000000000 +1000 +++ linux-2.4.20-ck5/arch/sparc64/solaris/fs.c 2003-04-08 10:52:50.000000000 +1000 @@ -79,47 +79,49 @@ struct sol_stat64 { #define UFSMAGIC (((unsigned)'u'<<24)||((unsigned)'f'<<16)||((unsigned)'s'<<8)) -static inline int putstat(struct sol_stat *ubuf, struct stat *kbuf) +static inline int putstat(struct sol_stat *ubuf, struct kstat *kbuf) { - if (put_user (R4_DEV(kbuf->st_dev), &ubuf->st_dev) || - __put_user (kbuf->st_ino, &ubuf->st_ino) || - __put_user (kbuf->st_mode, &ubuf->st_mode) || - __put_user (kbuf->st_nlink, &ubuf->st_nlink) || - __put_user (kbuf->st_uid, &ubuf->st_uid) || - __put_user (kbuf->st_gid, &ubuf->st_gid) || - __put_user (R4_DEV(kbuf->st_rdev), &ubuf->st_rdev) || - __put_user (kbuf->st_size, &ubuf->st_size) || - __put_user (kbuf->st_atime, &ubuf->st_atime.tv_sec) || + /* XXX_UID, XXX_SIZE */ + if (put_user (R4_DEV(kbuf->dev), &ubuf->st_dev) || + __put_user (kbuf->ino, &ubuf->st_ino) || + __put_user (kbuf->mode, &ubuf->st_mode) || + __put_user (kbuf->nlink, &ubuf->st_nlink) || + __put_user (kbuf->uid, &ubuf->st_uid) || + __put_user (kbuf->gid, &ubuf->st_gid) || + __put_user (R4_DEV(kbuf->rdev), &ubuf->st_rdev) || + __put_user (kbuf->size, &ubuf->st_size) || + __put_user (kbuf->atime, &ubuf->st_atime.tv_sec) || __put_user (0, &ubuf->st_atime.tv_nsec) || - __put_user (kbuf->st_mtime, &ubuf->st_mtime.tv_sec) || + __put_user (kbuf->mtime, &ubuf->st_mtime.tv_sec) || __put_user (0, &ubuf->st_mtime.tv_nsec) || - __put_user (kbuf->st_ctime, &ubuf->st_ctime.tv_sec) || + __put_user (kbuf->ctime, &ubuf->st_ctime.tv_sec) || __put_user (0, &ubuf->st_ctime.tv_nsec) || - __put_user (kbuf->st_blksize, &ubuf->st_blksize) || - __put_user (kbuf->st_blocks, &ubuf->st_blocks) || + __put_user (kbuf->blksize, &ubuf->st_blksize) || + __put_user (kbuf->blocks, &ubuf->st_blocks) || __put_user (UFSMAGIC, (unsigned *)ubuf->st_fstype)) return -EFAULT; return 0; } -static inline int putstat64(struct sol_stat64 *ubuf, struct stat *kbuf) +static inline int putstat64(struct sol_stat64 *ubuf, struct kstat *kbuf) { - if (put_user (R4_DEV(kbuf->st_dev), &ubuf->st_dev) || - __put_user (kbuf->st_ino, &ubuf->st_ino) || - __put_user (kbuf->st_mode, &ubuf->st_mode) || - __put_user (kbuf->st_nlink, &ubuf->st_nlink) || - __put_user (kbuf->st_uid, &ubuf->st_uid) || - __put_user (kbuf->st_gid, &ubuf->st_gid) || - __put_user (R4_DEV(kbuf->st_rdev), &ubuf->st_rdev) || - __put_user (kbuf->st_size, &ubuf->st_size) || - __put_user (kbuf->st_atime, &ubuf->st_atime.tv_sec) || + /* XXX_UID, XXX_SIZE */ + if (put_user (R4_DEV(kbuf->dev), &ubuf->st_dev) || + __put_user (kbuf->ino, &ubuf->st_ino) || + __put_user (kbuf->mode, &ubuf->st_mode) || + __put_user (kbuf->nlink, &ubuf->st_nlink) || + __put_user (kbuf->uid, &ubuf->st_uid) || + __put_user (kbuf->gid, &ubuf->st_gid) || + __put_user (R4_DEV(kbuf->rdev), &ubuf->st_rdev) || + __put_user (kbuf->size, &ubuf->st_size) || + __put_user (kbuf->atime, &ubuf->st_atime.tv_sec) || __put_user (0, &ubuf->st_atime.tv_nsec) || - __put_user (kbuf->st_mtime, &ubuf->st_mtime.tv_sec) || + __put_user (kbuf->mtime, &ubuf->st_mtime.tv_sec) || __put_user (0, &ubuf->st_mtime.tv_nsec) || - __put_user (kbuf->st_ctime, &ubuf->st_ctime.tv_sec) || + __put_user (kbuf->ctime, &ubuf->st_ctime.tv_sec) || __put_user (0, &ubuf->st_ctime.tv_nsec) || - __put_user (kbuf->st_blksize, &ubuf->st_blksize) || - __put_user (kbuf->st_blocks, &ubuf->st_blocks) || + __put_user (kbuf->blksize, &ubuf->st_blksize) || + __put_user (kbuf->blocks, &ubuf->st_blocks) || __put_user (UFSMAGIC, (unsigned *)ubuf->st_fstype)) return -EFAULT; return 0; @@ -127,23 +129,11 @@ static inline int putstat64(struct sol_s asmlinkage int solaris_stat(u32 filename, u32 statbuf) { - int ret; - struct stat s; - char *filenam; - mm_segment_t old_fs = get_fs(); - int (*sys_newstat)(char *,struct stat *) = - (int (*)(char *,struct stat *))SYS(stat); - - filenam = getname ((char *)A(filename)); - ret = PTR_ERR(filenam); - if (!IS_ERR(filenam)) { - set_fs (KERNEL_DS); - ret = sys_newstat(filenam, &s); - set_fs (old_fs); - putname (filenam); - if (putstat ((struct sol_stat *)A(statbuf), &s)) - return -EFAULT; - } + struct kstat s; + int ret = vfs_stat((char*)A(filename), &s); + + if (putstat ((struct sol_stat *)A(statbuf), &s)) + return -EFAULT; return ret; } @@ -155,45 +145,21 @@ asmlinkage int solaris_xstat(int vers, u asmlinkage int solaris_stat64(u32 filename, u32 statbuf) { - int ret; - struct stat s; - char *filenam; - mm_segment_t old_fs = get_fs(); - int (*sys_newstat)(char *,struct stat *) = - (int (*)(char *,struct stat *))SYS(stat); - - filenam = getname ((char *)A(filename)); - ret = PTR_ERR(filenam); - if (!IS_ERR(filenam)) { - set_fs (KERNEL_DS); - ret = sys_newstat(filenam, &s); - set_fs (old_fs); - putname (filenam); - if (putstat64 ((struct sol_stat64 *)A(statbuf), &s)) - return -EFAULT; - } + struct kstat s; + int ret = vfs_stat((char*)A(filename), &s); + + if (putstat64 ((struct sol_stat64 *)A(statbuf), &s)) + return -EFAULT; return ret; } asmlinkage int solaris_lstat(u32 filename, u32 statbuf) { - int ret; - struct stat s; - char *filenam; - mm_segment_t old_fs = get_fs(); - int (*sys_newlstat)(char *,struct stat *) = - (int (*)(char *,struct stat *))SYS(lstat); - - filenam = getname ((char *)A(filename)); - ret = PTR_ERR(filenam); - if (!IS_ERR(filenam)) { - set_fs (KERNEL_DS); - ret = sys_newlstat(filenam, &s); - set_fs (old_fs); - putname (filenam); - if (putstat ((struct sol_stat *)A(statbuf), &s)) - return -EFAULT; - } + struct kstat s; + int ret = vfs_lstat((char*)A(filename), &s); + + if (putstat ((struct sol_stat *)A(statbuf), &s)) + return -EFAULT; return ret; } @@ -204,37 +170,19 @@ asmlinkage int solaris_lxstat(int vers, asmlinkage int solaris_lstat64(u32 filename, u32 statbuf) { - int ret; - struct stat s; - char *filenam; - mm_segment_t old_fs = get_fs(); - int (*sys_newlstat)(char *,struct stat *) = - (int (*)(char *,struct stat *))SYS(lstat); - - filenam = getname ((char *)A(filename)); - ret = PTR_ERR(filenam); - if (!IS_ERR(filenam)) { - set_fs (KERNEL_DS); - ret = sys_newlstat(filenam, &s); - set_fs (old_fs); - putname (filenam); - if (putstat64 ((struct sol_stat64 *)A(statbuf), &s)) - return -EFAULT; - } + struct kstat s; + int ret = vfs_lstat((char*)A(filename), &s); + + if (putstat64 ((struct sol_stat64 *)A(statbuf), &s)) + return -EFAULT; return ret; } asmlinkage int solaris_fstat(unsigned int fd, u32 statbuf) { - int ret; - struct stat s; - mm_segment_t old_fs = get_fs(); - int (*sys_newfstat)(unsigned,struct stat *) = - (int (*)(unsigned,struct stat *))SYS(fstat); - - set_fs (KERNEL_DS); - ret = sys_newfstat(fd, &s); - set_fs (old_fs); + struct kstat s; + int ret = vfs_fstat(fd, &s); + if (putstat ((struct sol_stat *)A(statbuf), &s)) return -EFAULT; return ret; @@ -247,15 +195,9 @@ asmlinkage int solaris_fxstat(int vers, asmlinkage int solaris_fstat64(unsigned int fd, u32 statbuf) { - int ret; - struct stat s; - mm_segment_t old_fs = get_fs(); - int (*sys_newfstat)(unsigned,struct stat *) = - (int (*)(unsigned,struct stat *))SYS(fstat); - - set_fs (KERNEL_DS); - ret = sys_newfstat(fd, &s); - set_fs (old_fs); + struct kstat s; + int ret = vfs_fstat(fd, &s); + if (putstat64 ((struct sol_stat64 *)A(statbuf), &s)) return -EFAULT; return ret; diff -Naurp linux-2.4.20-ck4/Documentation/filesystems/00-INDEX linux-2.4.20-ck5/Documentation/filesystems/00-INDEX --- linux-2.4.20-ck4/Documentation/filesystems/00-INDEX 2002-11-30 17:02:00.000000000 +1100 +++ linux-2.4.20-ck5/Documentation/filesystems/00-INDEX 2003-04-08 10:52:50.000000000 +1000 @@ -36,6 +36,8 @@ romfs.txt - Description of the ROMFS filesystem. smbfs.txt - info on using filesystems with the SMB protocol (Windows 3.11 and NT) +supermount.txt + - info on using supermount for removable media. sysv-fs.txt - info on the SystemV/V7/Xenix/Coherent filesystem. udf.txt diff -Naurp linux-2.4.20-ck4/Documentation/filesystems/supermount.txt linux-2.4.20-ck5/Documentation/filesystems/supermount.txt --- linux-2.4.20-ck4/Documentation/filesystems/supermount.txt 1970-01-01 10:00:00.000000000 +1000 +++ linux-2.4.20-ck5/Documentation/filesystems/supermount.txt 2003-04-08 10:52:50.000000000 +1000 @@ -0,0 +1,66 @@ +Supermount README +================= + +Running supermount +------------------ + +To run supermount, compile and install a kernel with the supermount +patches and select "Y" to the question + + Supermount removable media support (CONFIG_SUPERMOUNT) [Y/n/?] + +when you run "make config". You set up a supermount filesystem with +the normal mount command, using the syntax: + + mount -t supermount -o ,--, + +where + + are the options you want to pass to supermount + itself. These are described below. + + are the options you want supermount to pass to the + dismountable filesystem underneath. + + is the mount point where you want your removable media to be + mounted. + +Notice that you do not directly specify the block device you are going +to mount on the mount command line. This is because the supermount +filesystem is NOT connected to a block device; rather, supermount is +responsible for connecting a separate filesystem to the block device. +You specify the sub-filesystem and block device name by providing the + field, where the following options are currently +recognised: + +* fs= [default is "auto"] + + Specify the subfilesystem type. "msdos" and "iso9660" have + been tested. If you use auto, it will try the following + filesystems in order: + "udf" + "iso9660" + "vfat" + "msdos" + "ext2" + + +* dev= + + Specify the block device on which the subfs is to be mounted. + + + +* debug + + Enable debugging code in the supermount filesystem, if +the debug option was enabled at compile time. By default, debugging +code is compiled into the kernel but is disabled until a debug mount +option is seen. + +* '--' + + All options after the option string '--' will be passed +directly to the subfilesystem when it gets mounted. + + diff -Naurp linux-2.4.20-ck4/drivers/block/paride/pcd.c linux-2.4.20-ck5/drivers/block/paride/pcd.c --- linux-2.4.20-ck4/drivers/block/paride/pcd.c 2002-11-30 17:02:06.000000000 +1100 +++ linux-2.4.20-ck5/drivers/block/paride/pcd.c 2003-04-08 10:52:50.000000000 +1000 @@ -271,6 +271,7 @@ static struct block_device_operations pc release: cdrom_release, ioctl: cdrom_ioctl, check_media_change: cdrom_media_changed, + mediactl: cdrom_mediactl, }; static struct cdrom_device_ops pcd_dops = { diff -Naurp linux-2.4.20-ck4/drivers/cdrom/cdrom.c linux-2.4.20-ck5/drivers/cdrom/cdrom.c --- linux-2.4.20-ck4/drivers/cdrom/cdrom.c 2002-11-30 17:02:06.000000000 +1100 +++ linux-2.4.20-ck5/drivers/cdrom/cdrom.c 2003-04-08 10:52:50.000000000 +1000 @@ -908,6 +908,24 @@ void cdrom_count_tracks(struct cdrom_dev tracks->cdi, tracks->xa); } +int cdrom_mediactl (kdev_t dev, int op, int optarg) +{ + struct cdrom_device_info *cdi = cdrom_find_device(dev); + struct cdrom_device_ops *cdo = cdi->ops; + + switch (op) { + case MEDIA_LOCK: + case MEDIA_UNLOCK: + if (cdo->capability & ~cdi->mask & CDC_LOCK && + cdi->options & CDO_LOCK) + cdo->lock_door(cdi, (op == MEDIA_LOCK)); + break; + default: + return -ENOSYS; + } + return 0; +} + /* Requests to the low-level drivers will /always/ be done in the following format convention: @@ -1492,10 +1510,26 @@ int cdrom_ioctl(struct inode *ip, struct } case CDROMEJECT: { +#if defined(CONFIG_SUPERMOUNT) || defined(CONFIG_SUPERMOUNT_MODULE) + struct super_block *sb = get_super(dev); + int supermounted = sb ? sb->s_flags & MS_SUPERMOUNTED : 0; + int usagecount = cdi->use_count; + + if (sb) drop_super(sb); + if (supermounted && usagecount < 2) + BUG(); +#endif cdinfo(CD_DO_IOCTL, "entering CDROMEJECT\n"); if (!CDROM_CAN(CDC_OPEN_TRAY)) return -ENOSYS; - if (cdi->use_count != 1 || keeplocked) + if (keeplocked +#if defined(CONFIG_SUPERMOUNT) || defined(CONFIG_SUPERMOUNT_MODULE) + || (supermounted && usagecount != 2) + || (!supermounted && usagecount != 1) +#else + || (cdi->use_count != 1) +#endif + ) return -EBUSY; if (CDROM_CAN(CDC_LOCK)) if ((ret=cdo->lock_door(cdi, 0))) @@ -2364,7 +2398,7 @@ use_last_written: *next_writable += 7; return 0; } -} +} EXPORT_SYMBOL(cdrom_get_disc_info); EXPORT_SYMBOL(cdrom_get_track_info); @@ -2377,6 +2411,7 @@ EXPORT_SYMBOL(cdrom_open); EXPORT_SYMBOL(cdrom_release); EXPORT_SYMBOL(cdrom_ioctl); EXPORT_SYMBOL(cdrom_media_changed); +EXPORT_SYMBOL(cdrom_mediactl); EXPORT_SYMBOL(cdrom_number_of_slots); EXPORT_SYMBOL(cdrom_select_disc); EXPORT_SYMBOL(cdrom_mode_select); diff -Naurp linux-2.4.20-ck4/drivers/cdrom/cdu31a.c linux-2.4.20-ck5/drivers/cdrom/cdu31a.c --- linux-2.4.20-ck4/drivers/cdrom/cdu31a.c 2002-11-30 17:02:06.000000000 +1100 +++ linux-2.4.20-ck5/drivers/cdrom/cdu31a.c 2003-04-08 10:52:50.000000000 +1000 @@ -3188,6 +3188,7 @@ struct block_device_operations scd_bdops release: cdrom_release, ioctl: cdrom_ioctl, check_media_change: cdrom_media_changed, + mediactl: cdrom_mediactl, }; static struct cdrom_device_ops scd_dops = { diff -Naurp linux-2.4.20-ck4/drivers/cdrom/cm206.c linux-2.4.20-ck5/drivers/cdrom/cm206.c --- linux-2.4.20-ck4/drivers/cdrom/cm206.c 2001-10-26 06:58:35.000000000 +1000 +++ linux-2.4.20-ck5/drivers/cdrom/cm206.c 2003-04-08 10:52:50.000000000 +1000 @@ -772,6 +772,7 @@ struct block_device_operations cm206_bdo release: cdrom_release, ioctl: cdrom_ioctl, check_media_change: cdrom_media_changed, + mediactl: cdrom_mediactl, }; /* The new open. The real opening strategy is defined in cdrom.c. */ diff -Naurp linux-2.4.20-ck4/drivers/cdrom/mcd.c linux-2.4.20-ck5/drivers/cdrom/mcd.c --- linux-2.4.20-ck4/drivers/cdrom/mcd.c 2001-10-26 06:58:35.000000000 +1000 +++ linux-2.4.20-ck5/drivers/cdrom/mcd.c 2003-04-08 10:52:51.000000000 +1000 @@ -196,6 +196,7 @@ struct block_device_operations mcd_bdops release: cdrom_release, ioctl: cdrom_ioctl, check_media_change: cdrom_media_changed, + mediactl: cdrom_mediactl, }; static struct timer_list mcd_timer; diff -Naurp linux-2.4.20-ck4/drivers/cdrom/mcdx.c linux-2.4.20-ck5/drivers/cdrom/mcdx.c --- linux-2.4.20-ck4/drivers/cdrom/mcdx.c 2001-10-26 06:58:35.000000000 +1000 +++ linux-2.4.20-ck5/drivers/cdrom/mcdx.c 2003-04-08 10:52:51.000000000 +1000 @@ -226,6 +226,7 @@ struct block_device_operations mcdx_bdop release: cdrom_release, ioctl: cdrom_ioctl, check_media_change: cdrom_media_changed, + mediactl: cdrom_mediactl, }; diff -Naurp linux-2.4.20-ck4/drivers/cdrom/sbpcd.c linux-2.4.20-ck5/drivers/cdrom/sbpcd.c --- linux-2.4.20-ck4/drivers/cdrom/sbpcd.c 2001-10-26 06:58:35.000000000 +1000 +++ linux-2.4.20-ck5/drivers/cdrom/sbpcd.c 2003-04-08 10:52:51.000000000 +1000 @@ -5426,6 +5426,7 @@ static struct block_device_operations sb release: cdrom_release, ioctl: cdrom_ioctl, check_media_change: cdrom_media_changed, + mediactl: cdrom_mediactl, }; /*==========================================================================*/ /* diff -Naurp linux-2.4.20-ck4/drivers/ide/ide.c linux-2.4.20-ck5/drivers/ide/ide.c --- linux-2.4.20-ck4/drivers/ide/ide.c 2002-11-30 17:02:07.000000000 +1100 +++ linux-2.4.20-ck5/drivers/ide/ide.c 2003-04-08 10:52:51.000000000 +1000 @@ -2215,6 +2215,18 @@ jump_eight: #endif /* (CONFIG_AMIGA) || (CONFIG_MAC) */ } +static int ide_mediactl (kdev_t i_rdev, int op, int optarg) +{ + ide_drive_t *drive; + + if ((drive = get_info_ptr(i_rdev)) == NULL) + return -ENODEV; + + if (drive->driver != NULL && DRIVER(drive)->mediactl != NULL) + return DRIVER(drive)->mediactl(drive, op, optarg); + return 0; +} + void ide_unregister (unsigned int index) { struct gendisk *gd; @@ -3930,7 +3942,8 @@ struct block_device_operations ide_fops[ release: ide_release, ioctl: ide_ioctl, check_media_change: ide_check_media_change, - revalidate: ide_revalidate_disk + revalidate: ide_revalidate_disk, + mediactl: ide_mediactl }}; EXPORT_SYMBOL(ide_hwifs); diff -Naurp linux-2.4.20-ck4/drivers/ide/ide-cd.c linux-2.4.20-ck5/drivers/ide/ide-cd.c --- linux-2.4.20-ck4/drivers/ide/ide-cd.c 2002-11-30 17:02:07.000000000 +1100 +++ linux-2.4.20-ck5/drivers/ide/ide-cd.c 2003-04-08 10:52:51.000000000 +1000 @@ -2875,6 +2875,18 @@ int ide_cdrom_ioctl (ide_drive_t *drive, return cdrom_ioctl (inode, file, cmd, arg); } +int ide_cdrom_mediactl (ide_drive_t *drive, int op, int optarg) +{ + switch (op) { + case MEDIA_LOCK: + case MEDIA_UNLOCK: + return cdrom_lockdoor (drive, op == MEDIA_LOCK, NULL); + default: + return -ENOSYS; + } + return 0; +} + static int ide_cdrom_open (struct inode *ip, struct file *fp, ide_drive_t *drive) { @@ -2989,6 +3001,7 @@ static ide_driver_t ide_cdrom_driver = { special: NULL, proc: NULL, reinit: ide_cdrom_reinit, + mediactl: ide_cdrom_mediactl, ata_prebuilder: NULL, atapi_prebuilder: NULL, }; diff -Naurp linux-2.4.20-ck4/drivers/scsi/sd.c linux-2.4.20-ck5/drivers/scsi/sd.c --- linux-2.4.20-ck4/drivers/scsi/sd.c 2002-08-03 13:14:53.000000000 +1000 +++ linux-2.4.20-ck5/drivers/scsi/sd.c 2003-04-08 10:52:51.000000000 +1000 @@ -562,6 +562,32 @@ static int sd_release(struct inode *inod return 0; } +/* + * This function performs media control operations. Currently the + * only functions used are MEDIA_LOCK and MEDIA_UNLOCK, to lock and + * unlock the drive door. + */ + +static int sd_mediactl(kdev_t full_dev, int op, int optarg) { + int target; + + target = DEVICE_NR(full_dev); + + if (target >= sd_template.nr_dev) { + printk("CD-ROM request error: invalid device.\n"); + return -ENODEV; + }; + + switch (op) { + case MEDIA_LOCK: + return scsi_ioctl(rscsi_disks[target].device, SCSI_IOCTL_DOORLOCK, 0); + case MEDIA_UNLOCK: + return scsi_ioctl(rscsi_disks[target].device, SCSI_IOCTL_DOORUNLOCK, 0); + default: + return -ENOSYS; + } +} + static struct block_device_operations sd_fops = { owner: THIS_MODULE, @@ -569,7 +595,8 @@ static struct block_device_operations sd release: sd_release, ioctl: sd_ioctl, check_media_change: check_scsidisk_media_change, - revalidate: fop_revalidate_scsidisk + revalidate: fop_revalidate_scsidisk, + mediactl: sd_mediactl, }; /* diff -Naurp linux-2.4.20-ck4/drivers/scsi/sr.c linux-2.4.20-ck5/drivers/scsi/sr.c --- linux-2.4.20-ck4/drivers/scsi/sr.c 2002-11-30 17:02:09.000000000 +1100 +++ linux-2.4.20-ck5/drivers/scsi/sr.c 2003-04-08 10:52:51.000000000 +1000 @@ -512,6 +512,7 @@ struct block_device_operations sr_bdops release: cdrom_release, ioctl: cdrom_ioctl, check_media_change: cdrom_media_changed, + mediactl: cdrom_mediactl, }; static int sr_open(struct cdrom_device_info *cdi, int purpose) diff -Naurp linux-2.4.20-ck4/fs/block_dev.c linux-2.4.20-ck5/fs/block_dev.c --- linux-2.4.20-ck4/fs/block_dev.c 2002-08-03 13:14:57.000000000 +1000 +++ linux-2.4.20-ck5/fs/block_dev.c 2003-04-08 10:52:51.000000000 +1000 @@ -508,36 +508,80 @@ int unregister_blkdev(unsigned int major * People changing diskettes in the middle of an operation deserve * to lose :-) */ -int check_disk_change(kdev_t dev) +const struct block_device_operations *get_block_operations(kdev_t dev) { - int i; + int major = MAJOR(dev); const struct block_device_operations * bdops = NULL; - i = MAJOR(dev); - if (i < MAX_BLKDEV) - bdops = blkdevs[i].bdops; + if (major < MAX_BLKDEV) + bdops = blkdevs[major].bdops; if (bdops == NULL) { devfs_handle_t de; - de = devfs_find_handle (NULL, NULL, i, MINOR (dev), + de = devfs_find_handle (NULL, NULL, major, MINOR (dev), DEVFS_SPECIAL_BLK, 0); if (de) { bdops = devfs_get_ops (de); devfs_put_ops (de); /* We're running in owner module */ } } - if (bdops == NULL) - return 0; - if (bdops->check_media_change == NULL) - return 0; - if (!bdops->check_media_change(dev)) - return 0; - if (invalidate_device(dev, 0)) - printk("VFS: busy inodes on changed media.\n"); + return bdops; +} + +/* + * This routine checks whether a removable media has been changed, and + * (for check_disk_change only) invalidate all buffer-cache-entries in + * that case. This is a relatively slow routine, so we have to try to + * minimize using it. Thus it is called only upon a 'mount' or + * 'open'. This is the best way of combining speed and utility, I + * think. People changing diskettes in the middle of an operation + * deserve to loose :-) +*/ + +int query_disk_change(kdev_t dev) +{ + const struct block_device_operations * + bdops = get_block_operations(dev); + + if (bdops && bdops->check_media_change) + return (bdops->check_media_change(dev)); + return 0; +} + +/** + * invalidate_media + * destroy all the buffers of this device + */ - if (bdops->revalidate) +void invalidate_media(kdev_t dev) +{ + const struct block_device_operations * + bdops = get_block_operations(dev); + + if (destroy_device(dev)) + printk(KERN_INFO "VFS: busy inodes on changed media.\n"); + + if (bdops && bdops->revalidate) bdops->revalidate(dev); +} + +int check_disk_change(kdev_t dev) +{ + if (!query_disk_change(dev)) + return 0; + +#if defined(CONFIG_SUPERMOUNT) || defined(CONFIG_SUPERMOUNT_MODULE) + { + struct super_block *sb = get_super(dev); + if (sb) { + sb->s_media_changed = 1; + drop_super(sb); + } + } +#endif + + invalidate_media(dev); return 1; } diff -Naurp linux-2.4.20-ck4/fs/Config.in linux-2.4.20-ck5/fs/Config.in --- linux-2.4.20-ck4/fs/Config.in 2002-11-30 17:02:20.000000000 +1100 +++ linux-2.4.20-ck5/fs/Config.in 2003-04-08 10:52:51.000000000 +1000 @@ -12,6 +12,8 @@ tristate 'Reiserfs support' CONFIG_REISE dep_mbool ' Enable reiserfs debug mode' CONFIG_REISERFS_CHECK $CONFIG_REISERFS_FS dep_mbool ' Stats in /proc/fs/reiserfs' CONFIG_REISERFS_PROC_INFO $CONFIG_REISERFS_FS +tristate 'Supermount removable media support' CONFIG_SUPERMOUNT + dep_tristate 'ADFS file system support (EXPERIMENTAL)' CONFIG_ADFS_FS $CONFIG_EXPERIMENTAL dep_mbool ' ADFS write support (DANGEROUS)' CONFIG_ADFS_FS_RW $CONFIG_ADFS_FS $CONFIG_EXPERIMENTAL diff -Naurp linux-2.4.20-ck4/fs/ext2/super.c linux-2.4.20-ck5/fs/ext2/super.c --- linux-2.4.20-ck4/fs/ext2/super.c 2002-11-30 17:02:20.000000000 +1100 +++ linux-2.4.20-ck5/fs/ext2/super.c 2003-04-08 10:52:51.000000000 +1000 @@ -274,6 +274,13 @@ static int parse_options (char * options || !strcmp (this_char, "quota") || !strcmp (this_char, "usrquota")) /* Don't do anything ;-) */ ; +#if defined(CONFIG_SUPERMOUNT) || defined(CONFIG_SUPERMOUNT_MODULE) + /* Silently ignore NLS options */ + else if (!strcmp (this_char, "iocharset") + || !strcmp (this_char, "codepage") + || !strcmp (this_char, "mode")) + /* Don't do anything ;-) */ ; +#endif else { printk ("EXT2-fs: Unrecognized mount option %s\n", this_char); return 0; diff -Naurp linux-2.4.20-ck4/fs/fat/file.c linux-2.4.20-ck5/fs/fat/file.c --- linux-2.4.20-ck4/fs/fat/file.c 2001-08-13 03:56:56.000000000 +1000 +++ linux-2.4.20-ck5/fs/fat/file.c 2003-04-08 10:52:51.000000000 +1000 @@ -117,8 +117,17 @@ void fat_truncate(struct inode *inode) int cluster; /* Why no return value? Surely the disk could fail... */ +#if 0 + /* + * Why do we need this check? VFS layer should already have + * checked user access before we ever get here; and we can + * get here from iput on read-only file system, consider + * remounting file system read-only after changing some file. + * That is exactly what happens with supermount and breaks it + */ if (IS_RDONLY (inode)) return /* -EPERM */; +#endif if (IS_IMMUTABLE(inode)) return /* -EPERM */; cluster = 1 << sbi->cluster_bits; diff -Naurp linux-2.4.20-ck4/fs/inode.c linux-2.4.20-ck5/fs/inode.c --- linux-2.4.20-ck4/fs/inode.c 2003-04-08 10:50:36.000000000 +1000 +++ linux-2.4.20-ck5/fs/inode.c 2003-04-08 10:52:51.000000000 +1000 @@ -660,6 +660,22 @@ int invalidate_device(kdev_t dev, int do return res; } +/* + * The device is a removable device, the device is not there anymore, + * we need to remove all its buffers + */ + +int destroy_device(kdev_t dev) +{ + struct super_block *sb = get_super(dev); + int res = 0; + if (sb) { + res = invalidate_inodes(sb); + drop_super(sb); + } + destroy_buffers(dev); + return res; +} /* * This is called with the inode lock held. It searches diff -Naurp linux-2.4.20-ck4/fs/Makefile linux-2.4.20-ck5/fs/Makefile --- linux-2.4.20-ck4/fs/Makefile 2002-11-30 17:02:20.000000000 +1100 +++ linux-2.4.20-ck5/fs/Makefile 2003-04-08 10:52:51.000000000 +1000 @@ -60,6 +60,7 @@ subdir-$(CONFIG_AFFS_FS) += affs subdir-$(CONFIG_ROMFS_FS) += romfs subdir-$(CONFIG_QNX4FS_FS) += qnx4 subdir-$(CONFIG_UDF_FS) += udf +subdir-$(CONFIG_SUPERMOUNT) += supermount subdir-$(CONFIG_AUTOFS_FS) += autofs subdir-$(CONFIG_AUTOFS4_FS) += autofs4 subdir-$(CONFIG_ADFS_FS) += adfs diff -Naurp linux-2.4.20-ck4/fs/namespace.c linux-2.4.20-ck5/fs/namespace.c --- linux-2.4.20-ck4/fs/namespace.c 2002-11-30 17:02:21.000000000 +1100 +++ linux-2.4.20-ck5/fs/namespace.c 2003-04-08 10:52:51.000000000 +1000 @@ -21,8 +21,6 @@ #include #include -struct vfsmount *do_kern_mount(const char *type, int flags, char *name, void *data); -int do_remount_sb(struct super_block *sb, int flags, void * data); void kill_super(struct super_block *sb); static struct list_head *mount_hashtable; diff -Naurp linux-2.4.20-ck4/fs/read_write.c linux-2.4.20-ck5/fs/read_write.c --- linux-2.4.20-ck4/fs/read_write.c 2002-08-03 13:14:58.000000000 +1000 +++ linux-2.4.20-ck5/fs/read_write.c 2003-04-08 10:52:51.000000000 +1000 @@ -91,7 +91,7 @@ loff_t default_llseek(struct file *file, return retval; } -static inline loff_t llseek(struct file *file, loff_t offset, int origin) +loff_t vfs_llseek(struct file *file, loff_t offset, int origin) { loff_t (*fn)(struct file *, loff_t, int); loff_t retval; @@ -116,7 +116,7 @@ asmlinkage off_t sys_lseek(unsigned int goto bad; retval = -EINVAL; if (origin <= 2) { - loff_t res = llseek(file, offset, origin); + loff_t res = vfs_llseek(file, offset, origin); retval = res; if (res != (loff_t)retval) retval = -EOVERFLOW; /* LFS: should only happen on 32 bit platforms */ @@ -143,7 +143,7 @@ asmlinkage long sys_llseek(unsigned int if (origin > 2) goto out_putf; - offset = llseek(file, ((loff_t) offset_high << 32) | offset_low, + offset = vfs_llseek(file, ((loff_t) offset_high << 32) | offset_low, origin); retval = (int)offset; diff -Naurp linux-2.4.20-ck4/fs/stat.c linux-2.4.20-ck5/fs/stat.c --- linux-2.4.20-ck4/fs/stat.c 2001-09-14 09:04:43.000000000 +1000 +++ linux-2.4.20-ck5/fs/stat.c 2003-04-08 10:52:51.000000000 +1000 @@ -25,222 +25,248 @@ do_revalidate(struct dentry *dentry) return 0; } - -#if !defined(__alpha__) && !defined(__sparc__) && !defined(__ia64__) && !defined(CONFIG_ARCH_S390) && !defined(__hppa__) && !defined(__x86_64__) - -/* - * For backward compatibility? Maybe this should be moved - * into arch/i386 instead? - */ -static int cp_old_stat(struct inode * inode, struct __old_kernel_stat * statbuf) +int getattr_full(struct vfsmount *mnt, struct dentry *dentry, struct kstat *stat) { - static int warncount = 5; - struct __old_kernel_stat tmp; - - if (warncount > 0) { - warncount--; - printk(KERN_WARNING "VFS: Warning: %s using old stat() call. Recompile your binary.\n", - current->comm); - } else if (warncount < 0) { - /* it's laughable, but... */ - warncount = 0; - } - - tmp.st_dev = kdev_t_to_nr(inode->i_dev); - tmp.st_ino = inode->i_ino; - tmp.st_mode = inode->i_mode; - tmp.st_nlink = inode->i_nlink; - SET_OLDSTAT_UID(tmp, inode->i_uid); - SET_OLDSTAT_GID(tmp, inode->i_gid); - tmp.st_rdev = kdev_t_to_nr(inode->i_rdev); -#if BITS_PER_LONG == 32 - if (inode->i_size > MAX_NON_LFS) - return -EOVERFLOW; -#endif - tmp.st_size = inode->i_size; - tmp.st_atime = inode->i_atime; - tmp.st_mtime = inode->i_mtime; - tmp.st_ctime = inode->i_ctime; - return copy_to_user(statbuf,&tmp,sizeof(tmp)) ? -EFAULT : 0; + struct inode *inode = dentry->d_inode; + stat->dev = kdev_t_to_nr(inode->i_dev); + stat->ino = inode->i_ino; + stat->mode = inode->i_mode; + stat->nlink = inode->i_nlink; + stat->uid = inode->i_uid; + stat->gid = inode->i_gid; + stat->rdev = kdev_t_to_nr(inode->i_rdev); + stat->atime = inode->i_atime; + stat->mtime = inode->i_mtime; + stat->ctime = inode->i_ctime; + stat->size = inode->i_size; + stat->blocks = inode->i_blocks; + stat->blksize = inode->i_blksize; + return 0; } -#endif - -static int cp_new_stat(struct inode * inode, struct stat * statbuf) +int getattr_minix(struct vfsmount *mnt, struct dentry *dentry, struct kstat *stat) { - struct stat tmp; + struct inode *inode = dentry->d_inode; unsigned int blocks, indirect; - memset(&tmp, 0, sizeof(tmp)); - tmp.st_dev = kdev_t_to_nr(inode->i_dev); - tmp.st_ino = inode->i_ino; - tmp.st_mode = inode->i_mode; - tmp.st_nlink = inode->i_nlink; - SET_STAT_UID(tmp, inode->i_uid); - SET_STAT_GID(tmp, inode->i_gid); - tmp.st_rdev = kdev_t_to_nr(inode->i_rdev); -#if BITS_PER_LONG == 32 - if (inode->i_size > MAX_NON_LFS) - return -EOVERFLOW; -#endif - tmp.st_size = inode->i_size; - tmp.st_atime = inode->i_atime; - tmp.st_mtime = inode->i_mtime; - tmp.st_ctime = inode->i_ctime; -/* - * st_blocks and st_blksize are approximated with a simple algorithm if - * they aren't supported directly by the filesystem. The minix and msdos - * filesystems don't keep track of blocks, so they would either have to - * be counted explicitly (by delving into the file itself), or by using - * this simple algorithm to get a reasonable (although not 100% accurate) - * value. - */ - -/* - * Use minix fs values for the number of direct and indirect blocks. The - * count is now exact for the minix fs except that it counts zero blocks. - * Everything is in units of BLOCK_SIZE until the assignment to - * tmp.st_blksize. - */ + stat->dev = kdev_t_to_nr(inode->i_dev); + stat->ino = inode->i_ino; + stat->mode = inode->i_mode; + stat->nlink = inode->i_nlink; + stat->uid = inode->i_uid; + stat->gid = inode->i_gid; + stat->rdev = kdev_t_to_nr(inode->i_rdev); + stat->atime = inode->i_atime; + stat->mtime = inode->i_mtime; + stat->ctime = inode->i_ctime; + stat->size = inode->i_size; #define D_B 7 #define I_B (BLOCK_SIZE / sizeof(unsigned short)) - if (!inode->i_blksize) { - blocks = (tmp.st_size + BLOCK_SIZE - 1) / BLOCK_SIZE; - if (blocks > D_B) { - indirect = (blocks - D_B + I_B - 1) / I_B; + blocks = (stat->size + BLOCK_SIZE - 1) >> BLOCK_SIZE_BITS; + if (blocks > D_B) { + indirect = (blocks - D_B + I_B - 1) / I_B; + blocks += indirect; + if (indirect > 1) { + indirect = (indirect - 1 + I_B - 1) / I_B; blocks += indirect; - if (indirect > 1) { - indirect = (indirect - 1 + I_B - 1) / I_B; - blocks += indirect; - if (indirect > 1) - blocks++; - } + if (indirect > 1) + blocks++; } - tmp.st_blocks = (BLOCK_SIZE / 512) * blocks; - tmp.st_blksize = BLOCK_SIZE; - } else { - tmp.st_blocks = inode->i_blocks; - tmp.st_blksize = inode->i_blksize; } - return copy_to_user(statbuf,&tmp,sizeof(tmp)) ? -EFAULT : 0; + stat->blocks = (BLOCK_SIZE / 512) * blocks; + stat->blksize = BLOCK_SIZE; + return 0; } - -#if !defined(__alpha__) && !defined(__sparc__) && !defined(__ia64__) && !defined(CONFIG_ARCH_S390) && !defined(__hppa__) && !defined(__x86_64__) -/* - * For backward compatibility? Maybe this should be moved - * into arch/i386 instead? - */ -asmlinkage long sys_stat(char * filename, struct __old_kernel_stat * statbuf) +int vfs_lstat(char * filename, struct kstat *stat) { struct nameidata nd; int error; - error = user_path_walk(filename, &nd); + error = user_path_walk_link(filename, &nd); + if (error) + return error; + + error = do_revalidate(nd.dentry); if (!error) { - error = do_revalidate(nd.dentry); - if (!error) - error = cp_old_stat(nd.dentry->d_inode, statbuf); + struct inode *inode = nd.dentry->d_inode; + if (inode->i_op->getattr) + error = inode->i_op->getattr(nd.mnt, nd.dentry, stat); + else + error = getattr_full(nd.mnt, nd.dentry, stat); path_release(&nd); } return error; } -#endif -asmlinkage long sys_newstat(char * filename, struct stat * statbuf) +int vfs_stat(char * filename, struct kstat *stat) { struct nameidata nd; int error; error = user_path_walk(filename, &nd); + if (error) + return error; + + error = do_revalidate(nd.dentry); if (!error) { - error = do_revalidate(nd.dentry); - if (!error) - error = cp_new_stat(nd.dentry->d_inode, statbuf); + struct inode *inode = nd.dentry->d_inode; + if (inode->i_op->getattr) + error = inode->i_op->getattr(nd.mnt, nd.dentry, stat); + else + error = getattr_full(nd.mnt, nd.dentry, stat); path_release(&nd); } return error; } +int vfs_fstat(int fd, struct kstat *stat) +{ + struct file *file = fget(fd); + int error; + + if (!file) + return -EBADF; + + error = do_revalidate(file->f_dentry); + if (!error) { + struct inode *inode = file->f_dentry->d_inode; + if (inode->i_op->getattr) + error = inode->i_op->getattr(file->f_vfsmnt, file->f_dentry, stat); + else + error = getattr_full(file->f_vfsmnt, file->f_dentry, stat); + fput(file); + } + return error; +} + #if !defined(__alpha__) && !defined(__sparc__) && !defined(__ia64__) && !defined(CONFIG_ARCH_S390) && !defined(__hppa__) && !defined(__x86_64__) /* * For backward compatibility? Maybe this should be moved * into arch/i386 instead? */ -asmlinkage long sys_lstat(char * filename, struct __old_kernel_stat * statbuf) +static int cp_old_stat(struct kstat *stat, struct __old_kernel_stat * statbuf) { - struct nameidata nd; - int error; + static int warncount = 5; + struct __old_kernel_stat tmp; - error = user_path_walk_link(filename, &nd); - if (!error) { - error = do_revalidate(nd.dentry); - if (!error) - error = cp_old_stat(nd.dentry->d_inode, statbuf); - path_release(&nd); + if (warncount > 0) { + warncount--; + printk(KERN_WARNING "VFS: Warning: %s using old stat() call. Recompile your binary.\n", + current->comm); + } else if (warncount < 0) { + /* it's laughable, but... */ + warncount = 0; } - return error; -} -#endif + tmp.st_dev = stat->dev; + tmp.st_ino = stat->ino; + tmp.st_mode = stat->mode; + tmp.st_nlink = stat->nlink; + SET_OLDSTAT_UID(tmp, stat->uid); + SET_OLDSTAT_GID(tmp, stat->gid); + tmp.st_rdev = stat->rdev; +#if BITS_PER_LONG == 32 + if (stat->size > MAX_NON_LFS) + return -EOVERFLOW; +#endif + tmp.st_size = stat->size; + tmp.st_atime = stat->atime; + tmp.st_mtime = stat->mtime; + tmp.st_ctime = stat->ctime; + return copy_to_user(statbuf,&tmp,sizeof(tmp)) ? -EFAULT : 0; +} -asmlinkage long sys_newlstat(char * filename, struct stat * statbuf) +asmlinkage long sys_stat(char * filename, struct __old_kernel_stat * statbuf) { - struct nameidata nd; - int error; + struct kstat stat; + int error = vfs_stat(filename, &stat); - error = user_path_walk_link(filename, &nd); - if (!error) { - error = do_revalidate(nd.dentry); - if (!error) - error = cp_new_stat(nd.dentry->d_inode, statbuf); - path_release(&nd); - } - return error; + return error ? error : cp_old_stat(&stat, statbuf); } -#if !defined(__alpha__) && !defined(__sparc__) && !defined(__ia64__) && !defined(CONFIG_ARCH_S390) && !defined(__hppa__) && !defined(__x86_64__) +asmlinkage long sys_lstat(char * filename, struct __old_kernel_stat * statbuf) +{ + struct kstat stat; + int error = vfs_lstat(filename, &stat); + + return error ? error : cp_old_stat(&stat, statbuf); +} -/* - * For backward compatibility? Maybe this should be moved - * into arch/i386 instead? - */ asmlinkage long sys_fstat(unsigned int fd, struct __old_kernel_stat * statbuf) { - struct file * f; - int err = -EBADF; + struct kstat stat; + int error = vfs_fstat(fd, &stat); - f = fget(fd); - if (f) { - struct dentry * dentry = f->f_dentry; - - err = do_revalidate(dentry); - if (!err) - err = cp_old_stat(dentry->d_inode, statbuf); - fput(f); - } - return err; + return error ? error : cp_old_stat(&stat, statbuf); } #endif +static int cp_new_stat(struct kstat *stat, struct stat * statbuf) +{ + struct stat tmp; + + memset(&tmp, 0, sizeof(tmp)); + tmp.st_dev = stat->dev; + tmp.st_ino = stat->ino; + tmp.st_mode = stat->mode; + tmp.st_nlink = stat->nlink; + SET_STAT_UID(tmp, stat->uid); + SET_STAT_GID(tmp, stat->gid); + tmp.st_rdev = stat->rdev; +#if BITS_PER_LONG == 32 + if (stat->size > MAX_NON_LFS) + return -EOVERFLOW; +#endif + tmp.st_size = stat->size; + tmp.st_atime = stat->atime; + tmp.st_mtime = stat->mtime; + tmp.st_ctime = stat->ctime; + tmp.st_blocks = stat->blocks; + tmp.st_blksize = stat->blksize; + return copy_to_user(statbuf,&tmp,sizeof(tmp)) ? -EFAULT : 0; +} + +asmlinkage long sys_newstat(char * filename, struct stat * statbuf) +{ + struct kstat stat; + int error = vfs_stat(filename, &stat); + + return error ? error : cp_new_stat(&stat, statbuf); +} + +asmlinkage long sys_newlstat(char * filename, struct stat * statbuf) +{ + struct kstat stat; + int error = vfs_lstat(filename, &stat); + + return error ? error : cp_new_stat(&stat, statbuf); +} + asmlinkage long sys_newfstat(unsigned int fd, struct stat * statbuf) { - struct file * f; - int err = -EBADF; + struct kstat stat; + int error = vfs_fstat(fd, &stat); + + return error ? error : cp_new_stat(&stat, statbuf); +} +/* this function should be callled vfs_readlink & vfs_readlink + generic_readlink */ + +int vfs_readlink_real(struct dentry * dentry, char * buf, int bufsiz) +{ + struct inode * inode = dentry->d_inode; + int error = -EINVAL; - f = fget(fd); - if (f) { - struct dentry * dentry = f->f_dentry; - - err = do_revalidate(dentry); - if (!err) - err = cp_new_stat(dentry->d_inode, statbuf); - fput(f); + if (inode->i_op && inode->i_op->readlink && + !(error = do_revalidate(dentry))) { + UPDATE_ATIME(inode); + error = inode->i_op->readlink(dentry, buf, bufsiz); } - return err; + return error; } asmlinkage long sys_readlink(const char * path, char * buf, int bufsiz) @@ -253,127 +279,60 @@ asmlinkage long sys_readlink(const char error = user_path_walk_link(path, &nd); if (!error) { - struct inode * inode = nd.dentry->d_inode; - - error = -EINVAL; - if (inode->i_op && inode->i_op->readlink && - !(error = do_revalidate(nd.dentry))) { - UPDATE_ATIME(inode); - error = inode->i_op->readlink(nd.dentry, buf, bufsiz); - } + error = vfs_readlink_real(nd.dentry, buf, bufsiz); path_release(&nd); } return error; } - /* ---------- LFS-64 ----------- */ #if !defined(__alpha__) && !defined(__ia64__) && !defined(__mips64) && !defined(__x86_64__) && !defined(CONFIG_ARCH_S390X) -static long cp_new_stat64(struct inode * inode, struct stat64 * statbuf) +static long cp_new_stat64(struct kstat *stat, struct stat64 * statbuf) { struct stat64 tmp; - unsigned int blocks, indirect; - memset(&tmp, 0, sizeof(tmp)); - tmp.st_dev = kdev_t_to_nr(inode->i_dev); - tmp.st_ino = inode->i_ino; + tmp.st_dev = stat->dev; + tmp.st_ino = stat->ino; #ifdef STAT64_HAS_BROKEN_ST_INO - tmp.__st_ino = inode->i_ino; + tmp.__st_ino = stat->ino; #endif - tmp.st_mode = inode->i_mode; - tmp.st_nlink = inode->i_nlink; - tmp.st_uid = inode->i_uid; - tmp.st_gid = inode->i_gid; - tmp.st_rdev = kdev_t_to_nr(inode->i_rdev); - tmp.st_atime = inode->i_atime; - tmp.st_mtime = inode->i_mtime; - tmp.st_ctime = inode->i_ctime; - tmp.st_size = inode->i_size; -/* - * st_blocks and st_blksize are approximated with a simple algorithm if - * they aren't supported directly by the filesystem. The minix and msdos - * filesystems don't keep track of blocks, so they would either have to - * be counted explicitly (by delving into the file itself), or by using - * this simple algorithm to get a reasonable (although not 100% accurate) - * value. - */ - -/* - * Use minix fs values for the number of direct and indirect blocks. The - * count is now exact for the minix fs except that it counts zero blocks. - * Everything is in units of BLOCK_SIZE until the assignment to - * tmp.st_blksize. - */ -#define D_B 7 -#define I_B (BLOCK_SIZE / sizeof(unsigned short)) - - if (!inode->i_blksize) { - blocks = (tmp.st_size + BLOCK_SIZE - 1) >> BLOCK_SIZE_BITS; - if (blocks > D_B) { - indirect = (blocks - D_B + I_B - 1) / I_B; - blocks += indirect; - if (indirect > 1) { - indirect = (indirect - 1 + I_B - 1) / I_B; - blocks += indirect; - if (indirect > 1) - blocks++; - } - } - tmp.st_blocks = (BLOCK_SIZE / 512) * blocks; - tmp.st_blksize = BLOCK_SIZE; - } else { - tmp.st_blocks = inode->i_blocks; - tmp.st_blksize = inode->i_blksize; - } + tmp.st_mode = stat->mode; + tmp.st_nlink = stat->nlink; + tmp.st_uid = stat->uid; + tmp.st_gid = stat->gid; + tmp.st_rdev = stat->rdev; + tmp.st_atime = stat->atime; + tmp.st_mtime = stat->mtime; + tmp.st_ctime = stat->ctime; + tmp.st_size = stat->size; + tmp.st_blocks = stat->blocks; + tmp.st_blksize = stat->blksize; return copy_to_user(statbuf,&tmp,sizeof(tmp)) ? -EFAULT : 0; } asmlinkage long sys_stat64(char * filename, struct stat64 * statbuf, long flags) { - struct nameidata nd; - int error; + struct kstat stat; + int error = vfs_stat(filename, &stat); - error = user_path_walk(filename, &nd); - if (!error) { - error = do_revalidate(nd.dentry); - if (!error) - error = cp_new_stat64(nd.dentry->d_inode, statbuf); - path_release(&nd); - } - return error; + return error ? error : cp_new_stat64(&stat, statbuf); } asmlinkage long sys_lstat64(char * filename, struct stat64 * statbuf, long flags) { - struct nameidata nd; - int error; + struct kstat stat; + int error = vfs_lstat(filename, &stat); - error = user_path_walk_link(filename, &nd); - if (!error) { - error = do_revalidate(nd.dentry); - if (!error) - error = cp_new_stat64(nd.dentry->d_inode, statbuf); - path_release(&nd); - } - return error; + return error ? error : cp_new_stat64(&stat, statbuf); } asmlinkage long sys_fstat64(unsigned long fd, struct stat64 * statbuf, long flags) { - struct file * f; - int err = -EBADF; + struct kstat stat; + int error = vfs_fstat(fd, &stat); - f = fget(fd); - if (f) { - struct dentry * dentry = f->f_dentry; - - err = do_revalidate(dentry); - if (!err) - err = cp_new_stat64(dentry->d_inode, statbuf); - fput(f); - } - return err; + return error ? error : cp_new_stat64(&stat, statbuf); } #endif /* LFS-64 */ diff -Naurp linux-2.4.20-ck4/fs/super.c linux-2.4.20-ck5/fs/super.c --- linux-2.4.20-ck4/fs/super.c 2002-11-30 17:02:22.000000000 +1100 +++ linux-2.4.20-ck5/fs/super.c 2003-04-08 10:59:54.000000000 +1000 @@ -844,10 +844,14 @@ void kill_super(struct super_block *sb) } /* Forget any remaining inodes */ +#if defined(CONFIG_SUPERMOUNT) || defined(CONFIG_SUPERMOUNT_MODULE) + invalidate_inodes(sb); +#else if (invalidate_inodes(sb)) { printk(KERN_ERR "VFS: Busy inodes after unmount. " "Self-destruct in 5 seconds. Have a nice day...\n"); } +#endif unlock_kernel(); unlock_super(sb); diff -Naurp linux-2.4.20-ck4/fs/supermount/changelog linux-2.4.20-ck5/fs/supermount/changelog --- linux-2.4.20-ck4/fs/supermount/changelog 1970-01-01 10:00:00.000000000 +1000 +++ linux-2.4.20-ck5/fs/supermount/changelog 2003-04-08 10:52:51.000000000 +1000 @@ -0,0 +1 @@ + diff -Naurp linux-2.4.20-ck4/fs/supermount/dentry_operations.c linux-2.4.20-ck5/fs/supermount/dentry_operations.c --- linux-2.4.20-ck4/fs/supermount/dentry_operations.c 1970-01-01 10:00:00.000000000 +1000 +++ linux-2.4.20-ck5/fs/supermount/dentry_operations.c 2003-04-08 10:52:51.000000000 +1000 @@ -0,0 +1,54 @@ +/* + * linux/fs/supermount/revalidate.c + * + * Original version: + * Copyright (C) 1995 + * Stephen Tweedie (sct@dcs.ed.ac.uk) + * + * Rewriten for kernel 2.2 & 2.4. (C) 1999, 2000 Alexis Mikhailov + * (alexis@abc.cap.ru) + * + */ + +#include +#include +#include +#include "supermount_i.h" + +static int +supermount_dentry_revalidate(struct dentry *dentry, int flags) +{ + struct dentry *subd; + int rc = 1; + + if (subfs_go_online(dentry->d_sb)) + goto bad_dentry; + spin_lock(&dcache_lock); + if (dentry->d_inode && is_inode_obsolete(dentry->d_inode)) { + spin_unlock(&dcache_lock); + goto bad_dentry; + } + spin_unlock(&dcache_lock); + subd = get_subfs_dentry(dentry); + if (IS_ERR(subd)) + goto bad_dentry; + + if (subd->d_op && subd->d_op->d_revalidate) + rc = subd->d_op->d_revalidate(subd, flags); + + dput(subd); +out: + subfs_go_inactive(dentry->d_sb); + return rc; +bad_dentry: + rc = 0; + goto out; +} + +struct dentry_operations supermount_dir_dops = { + .d_revalidate = supermount_dentry_revalidate, +}; + +struct dentry_operations supermount_file_dops = { + .d_revalidate = supermount_dentry_revalidate, +}; diff -Naurp linux-2.4.20-ck4/fs/supermount/file_operations.c linux-2.4.20-ck5/fs/supermount/file_operations.c --- linux-2.4.20-ck4/fs/supermount/file_operations.c 1970-01-01 10:00:00.000000000 +1000 +++ linux-2.4.20-ck5/fs/supermount/file_operations.c 2003-04-08 10:52:51.000000000 +1000 @@ -0,0 +1,289 @@ +/* + * linux/fs/supermount/file_operations.c + * + * Original version: + * Copyright (C) 1995, 1997 + * Stephen Tweedie (sct@dcs.ed.ac.uk) + * + * from + * + * linux/fs/minix/dir.c + * Copyright (C) 1991, 1992 Linus Torvalds + * + * and + * + * linux/fs/ext2/dir.c + * Copyright (C) 1992, 1993, 1994, 1995 Remy Card + * + * Rewriten for kernel 2.2 & 2.4. (C) 1999, 2000 Alexis Mikhailov + * (alexis@abc.cap.ru) + * Rewriten for kernel 2.4. (C) 2001 MandrakeSoft Inc. + * Juan Quintela (quintela@mandrakesoft.com) + * + */ + +#include +#include +#include +#include +#include +#include + +#include "supermount_i.h" + +#define DEFAULT_POLLMASK (POLLIN | POLLOUT | POLLRDNORM | POLLWRNORM) + +static loff_t +supermount_llseek(struct file *file, loff_t offset, int origin) +{ + struct file *subfile = get_subfile(file); + loff_t retval; + + retval = vfs_llseek(subfile, offset, origin); + file->f_pos = subfile->f_pos; + file->f_reada = subfile->f_reada; + file->f_version = subfile->f_version; + return retval; +} + +static ssize_t +supermount_read(struct file *file, char *buf, size_t count, loff_t * ppos) +{ + struct file *subfile = get_subfile(file); + int rc; + + rc = -EINVAL; + if (subfile->f_op && subfile->f_op->read) + rc = subfile->f_op->read(subfile, buf, count, ppos); + if (rc > 0) + file->f_pos = subfile->f_pos = *ppos; + return rc; +} + +static ssize_t +supermount_write(struct file *file, const char *buf, + size_t count, loff_t * ppos) +{ + struct file *subfile = get_subfile(file); + int rc = 0; + + if (subfile->f_op && subfile->f_op->write) + rc = subfile->f_op->write(subfile, buf, count, ppos); + if (rc > 0) { + struct inode *subinode = subfile->f_dentry->d_inode; + + file->f_pos = subfile->f_pos = *ppos; + file->f_mode = subfile->f_mode; + file->f_dentry->d_inode->i_size = subinode->i_size; + file->f_dentry->d_inode->i_blocks = subinode->i_blocks; + file->f_dentry->d_inode->i_mode = subinode->i_mode; + } + return rc; +} + +int +supermount_readdir(struct file *file, void *buf, filldir_t fill_fn) +{ + struct file *subfile = get_subfile(file); + int rc; + + rc = vfs_readdir(subfile, fill_fn, buf); + file->f_pos = subfile->f_pos; /* update position, nfs needs it */ + UPDATE_ATIME(file->f_dentry->d_inode); + return rc; +} + +static unsigned int +supermount_poll(struct file *file, struct poll_table_struct *table) +{ + struct file *subfile = get_subfile(file); + int rc = DEFAULT_POLLMASK; + + if (subfile->f_op && subfile->f_op->poll) + rc = subfile->f_op->poll(subfile, table); + + return rc; +} + +static int +supermount_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + struct file *subfile = get_subfile(file); + struct inode *subinode; + int rc = -ENOTTY; + + subinode = subfile->f_dentry->d_inode; + if (subfile->f_op && subfile->f_op->ioctl) + rc = subfile->f_op->ioctl(subinode, subfile, cmd, arg); + return rc; +} + +int +supermount_open(struct inode *inode, struct file *file) +{ + struct dentry *subdent; + struct file *subfile; + struct supermount_sb_info *sbi = supermount_sbi(inode->i_sb); + struct vfsmount *mnt; + int rc; + + rc = -EIO; + if (subfs_go_online(inode->i_sb)) + goto out; + + subdent = get_subfs_dentry(file->f_dentry); + rc = PTR_ERR(subdent); + if (IS_ERR(subdent)) + goto out; + + if (!subdent->d_inode) + BUG(); + + subfs_get_read_access(inode->i_sb); + + if (file->f_mode & FMODE_WRITE) { + rc = subfs_get_write_access(inode->i_sb); + if (rc) + goto dput_ret; + } + + mnt = mntget(sbi->s_undermount); + subfile = dentry_open(subdent, mnt, file->f_flags); + rc = PTR_ERR(subfile); + if (IS_ERR(subfile)) + goto close_ret; + + put_subfile(file, subfile); + subfile->f_mode = file->f_mode; + + rc = 0; +out: + subfs_go_inactive(inode->i_sb); + return rc; + +close_ret: + subfs_put_write_access(inode->i_sb); +dput_ret: + subfs_put_read_access(inode->i_sb); + dput(subdent); + goto out; +} + +static int +supermount_flush(struct file *file) +{ + struct file *subfile = get_subfile(file); + int rc = 0; + + if (subfile->f_op && subfile->f_op->flush) + rc = subfile->f_op->flush(subfile); + + return rc; +} + +/* we want to maintain the numbers right indeed if we have an error + */ + +static int +supermount_release(struct inode *inode, struct file *file) +{ + struct file *subfile = get_subfile(file); + struct inode *subinode; + int write_on = 0; + int rc; + + rc = -EIO; + if (subfs_go_online(inode->i_sb)) + goto out; + + rc = 0; + subinode = subfile->f_dentry->d_inode; + if (!(file->f_mode & FMODE_WRITE) && subinode->i_nlink == 0) { + if (!subfs_get_write_access(inode->i_sb)) + write_on = 1; + + } + + filp_close(subfile, current->files); + if ((file->f_mode & FMODE_WRITE) || write_on) { + mark_subfs_dirty(inode->i_sb); + subfs_put_write_access(inode->i_sb); + } +out: + subfs_put_read_access(inode->i_sb); + subfs_go_inactive(inode->i_sb); + return rc; + +} + +static int +supermount_fsync(struct file *file, struct dentry *dentry, int datasync) +{ + struct file *subfile = get_subfile(file); + int rc = -EINVAL; + + if (subfile->f_op && subfile->f_op->fsync) + rc = subfile->f_op->fsync(subfile, subfile->f_dentry, datasync); + + return rc; +} + +static int +supermount_fasync(int fd, struct file *file, int on) +{ + struct file *subfile = get_subfile(file); + int rc = -EINVAL; + + if (subfile->f_op && subfile->f_op->fasync) + rc = subfile->f_op->fasync(fd, subfile, on); + + return rc; +} + +static int +supermount_lock(struct file *file, int cmd, struct file_lock *fl) +{ + struct file *subfile = get_subfile(file); + int rc = 0; + + if (subfile->f_op && subfile->f_op->lock) + rc = subfile->f_op->lock(subfile, cmd, fl); + else if (cmd == F_GETLK) + posix_test_lock(file, fl); + return rc; +} + +/* Fixme: + * readv: easy, export churnk from vfs + * writev: easy, export churnk from vfs + * sendpage: only used for networking, not needed + * get_unmmapped_area: only used for devices, not needed + */ + +struct file_operations supermount_dir_operations = { + .llseek = supermount_llseek, + .read = supermount_read, + .readdir = supermount_readdir, + .ioctl = supermount_ioctl, + .open = supermount_open, + .flush = supermount_flush, + .release = supermount_release, + .fsync = supermount_fsync, + .fasync = supermount_fasync, +}; + +struct file_operations supermount_file_operations = { + .llseek = supermount_llseek, + .read = supermount_read, + .write = supermount_write, + .poll = supermount_poll, + .ioctl = supermount_ioctl, + .mmap = generic_file_mmap, + .open = supermount_open, + .flush = supermount_flush, + .release = supermount_release, + .fsync = supermount_fsync, + .fasync = supermount_fasync, + .lock = supermount_lock, +}; diff -Naurp linux-2.4.20-ck4/fs/supermount/init.c linux-2.4.20-ck5/fs/supermount/init.c --- linux-2.4.20-ck4/fs/supermount/init.c 1970-01-01 10:00:00.000000000 +1000 +++ linux-2.4.20-ck5/fs/supermount/init.c 2003-04-08 10:52:51.000000000 +1000 @@ -0,0 +1,36 @@ +/* + * linux/fs/supermount/init.c + * + * (C) Copyright 2001-2002 Juan Quintela + * Released unde GPL v2. + * + */ + +#include +#include +#include + +#include "supermount_i.h" + +static DECLARE_FSTYPE(supermount_fs_type, "supermount", + supermount_read_super, 0); + +static int __init +init_supermount_fs(void) +{ + return register_filesystem(&supermount_fs_type); +} + +static void __exit +exit_supermount_fs(void) +{ + unregister_filesystem(&supermount_fs_type); +} + +EXPORT_NO_SYMBOLS; + +MODULE_AUTHOR("Stephen Tweedie, Alexis Mikhailov, Juan Quintela and others"); +MODULE_DESCRIPTION("Supermount"); +MODULE_LICENSE("GPL"); +module_init(init_supermount_fs); +module_exit(exit_supermount_fs); diff -Naurp linux-2.4.20-ck4/fs/supermount/inode_operations.c linux-2.4.20-ck5/fs/supermount/inode_operations.c --- linux-2.4.20-ck4/fs/supermount/inode_operations.c 1970-01-01 10:00:00.000000000 +1000 +++ linux-2.4.20-ck5/fs/supermount/inode_operations.c 2003-04-08 10:52:51.000000000 +1000 @@ -0,0 +1,893 @@ +/* + * linux/fs/supermount/namei.c + * + * Original version: + * Copyright (C) 1995 + * Stephen Tweedie (sct@dcs.ed.ac.uk) + * + * from + * + * linux/fs/minix/namei.c + * Copyright (C) 1991, 1992 Linus Torvalds + * + * and + * + * linux/fs/ext2/namei.c + * Copyright (C) 1992, 1993, 1994, 1995 Remy Card + * + * Rewriten for kernel 2.2 & 2.4. (C) 1999, 2000 Alexis Mikhailov + * (alexis@abc.cap.ru) + * + */ + +/* This file handles almost all of the normal filesystem running of + supermount. We don't have to deal to much with the subfs + interface, since we just pass subinodes out to the application on + demand. */ + +#include +#include +#include + +#include "supermount_i.h" + +static struct dentry * +get_subdent(struct dentry *subfs_dir, struct dentry *dentry) +{ + struct dentry *subdent = get_subfs_dentry(dentry); + if (IS_ERR(subdent)) { + subdent = d_alloc(subfs_dir, &dentry->d_name); + } + if (!subdent) + subdent = ERR_PTR(-ENOMEM); + + return subdent; +} + +static void +supermount_update_super_inode(struct inode *superi, struct inode *subi) +{ + superi->i_mode = subi->i_mode; + superi->i_uid = subi->i_uid; + superi->i_gid = subi->i_gid; + superi->i_nlink = subi->i_nlink; + superi->i_size = subi->i_size; + superi->i_atime = subi->i_atime; + superi->i_ctime = subi->i_ctime; + superi->i_mtime = subi->i_mtime; + superi->i_blksize = subi->i_blksize; + superi->i_blocks = subi->i_blocks; + superi->i_rdev = subi->i_rdev; + superi->i_version++; + + if (S_ISDIR(superi->i_mode)) { + superi->i_op = &supermount_dir_iops; + superi->i_fop = &supermount_dir_operations; + } else if (S_ISLNK(superi->i_mode)) { + superi->i_op = &supermount_symlink_iops; + superi->i_mapping = subi->i_mapping; + } else { + superi->i_op = &supermount_file_iops; + superi->i_fop = &supermount_file_operations; + superi->i_mapping = subi->i_mapping; + } +} + +/* + * Attach superinode to subinode and instantiate dentry with this inode + */ + +static void +attach_and_prepare(struct super_block *sb, + struct dentry *dentry, struct dentry *subd, int create) +{ + struct inode *superi, *subi = subd->d_inode; + + if (dentry->d_inode) + BUG(); + + superi = supermount_create_inode(sb); + supermount_update_super_inode(superi, subd->d_inode); + + if (create) + d_instantiate(dentry, superi); + else + d_add(dentry, superi); + supermount_i(superi)->i_dentry = dget(subd); + + if (S_ISDIR(subi->i_mode)) + dentry->d_op = &supermount_dir_dops; + else + dentry->d_op = &supermount_file_dops; + +} + +static inline void +update_nlink(struct inode *superi, struct inode *subi) +{ + superi->i_nlink = subi->i_nlink; +} + +static inline void +update_ctime(struct inode *superi, struct inode *subi) +{ + superi->i_ctime = subi->i_ctime; +} + +static inline void +update_mtime(struct inode *superi, struct inode *subi) +{ + superi->i_mtime = subi->i_mtime; +} + +/* inode methods */ + +static int +supermount_create(struct inode *dir, struct dentry *dentry, int mode) +{ + struct dentry *subdent, *subfs_dir; + int rc; + + if (dentry->d_inode) + BUG(); + rc = -EIO; + if (subfs_go_online(dir->i_sb)) + goto out; + + subfs_dir = get_subfs_dentry_by_inode(dir); + rc = PTR_ERR(subfs_dir); + if (IS_ERR(subfs_dir)) + goto out; + + rc = -ENOENT; + if (!subfs_dir->d_inode) + goto dirput_ret; + + subdent = get_subdent(subfs_dir, dentry); + rc = PTR_ERR(subdent); + if (IS_ERR(subdent)) + goto dirput_ret; + + rc = -EINVAL; + if (subdent->d_inode) + goto attach; + + rc = subfs_get_write_access(dir->i_sb); + if (rc) + goto dput_ret; + + rc = vfs_create(subfs_dir->d_inode, subdent, mode); + + mark_subfs_dirty(dir->i_sb); + subfs_put_write_access(dir->i_sb); + + if (rc) + goto dput_ret; + + if (!subdent->d_inode) { + d_instantiate(dentry, NULL); + goto dput_ret; + } + + /* It worked, so now create a shadow supermount inode for the + result... */ +attach: + attach_and_prepare(dir->i_sb, dentry, subdent, 1); +dput_ret: + dput(subdent); +dirput_ret: + dput(subfs_dir); +out: + subfs_go_inactive(dir->i_sb); + return rc; +} + +static struct dentry * +supermount_lookup(struct inode *dir, struct dentry *dentry) +{ + struct dentry *err, *subdent, *subfs_dir; + + if (dentry->d_inode) + BUG(); + err = ERR_PTR(-EIO); + if (subfs_go_online(dir->i_sb)) + goto out; + subfs_dir = get_subfs_dentry_by_inode(dir); + err = subfs_dir; + if (IS_ERR(subfs_dir)) + goto out; + err = ERR_PTR(-ENOENT); + if (!subfs_dir->d_inode) + goto dirput_ret; + subdent = lookup_one_len(dentry->d_name.name, subfs_dir, + dentry->d_name.len); + err = subdent; + if (IS_ERR(subdent)) + goto dirput_ret; + + err = NULL; + if (!subdent->d_inode) { + /* not found */ + d_add(dentry, NULL); + goto dput_ret; + } + + /* It worked, so now create a shadow supermount inode for the + result... */ + attach_and_prepare(dir->i_sb, dentry, subdent, 0); +dput_ret: + dput(subdent); +dirput_ret: + dput(subfs_dir); +out: + subfs_go_inactive(dir->i_sb); + return err; +} + +static int +supermount_link(struct dentry *old_dentry, struct inode *dir, + struct dentry *new_dentry) +{ + struct dentry *old_subdent, *subfs_dir, *new_subdent; + int rc; + + rc = -EIO; + if (subfs_go_online(dir->i_sb)) + goto out; + + subfs_dir = get_subfs_dentry_by_inode(dir); + rc = -EIO; + if (IS_ERR(subfs_dir)) + goto out; + + rc = -ENOENT; + if (!subfs_dir->d_inode) + goto dirput_ret; + + old_subdent = get_subdent(subfs_dir, old_dentry); + rc = PTR_ERR(old_subdent); + if (IS_ERR(old_subdent)) + goto dirput_ret; + + rc = 0; + if (old_subdent->d_inode && !old_dentry->d_inode) + attach_and_prepare(dir->i_sb, old_dentry, + old_subdent, 0); + + rc = subfs_get_write_access(dir->i_sb); + if (rc) + goto dput_ret; + new_subdent = lookup_one_len(new_dentry->d_name.name, subfs_dir, + new_dentry->d_name.len); + + if (IS_ERR(new_subdent)) { + rc = PTR_ERR(new_subdent); + goto out_write; + } + + rc = vfs_link(old_subdent, subfs_dir->d_inode, new_subdent); + /* Now we have subinode, attached to subdent. */ + + mark_subfs_dirty(dir->i_sb); + subfs_put_write_access(dir->i_sb); + + if (rc) + goto dput_ret; + + if (!old_subdent->d_inode) { + d_instantiate(old_dentry, NULL); + goto dput_ret; + } + + /* It worked, so now create a shadow supermount inode for the + result... */ + attach_and_prepare(dir->i_sb, new_dentry, new_subdent, 1); +dput_ret: + dput(old_subdent); +dirput_ret: + dput(subfs_dir); +out: + subfs_go_inactive(dir->i_sb); + return rc; +out_write: + subfs_put_write_access(dir->i_sb); + goto dput_ret; +} + +static int +supermount_unlink(struct inode *dir, struct dentry *dentry) +{ + struct dentry *subfs_dir, *subdent; + int rc; + + rc = -EIO; + if (subfs_go_online(dir->i_sb)) + goto out; + + subfs_dir = get_subfs_dentry_by_inode(dir); + rc = -ENOENT; + if (IS_ERR(subfs_dir)) + goto out; + + if (!subfs_dir->d_inode) + goto dirput_ret; + + subdent = get_subfs_dentry(dentry); + rc = -ENOENT; + if (IS_ERR(subdent)) + goto dirput_ret; + + rc = subfs_get_write_access(dir->i_sb); + if (rc) + goto dput_ret; + + rc = vfs_unlink(subfs_dir->d_inode, subdent); + + update_nlink(dir, subfs_dir->d_inode); + update_nlink(dentry->d_inode, subdent->d_inode); + update_ctime(dir, subfs_dir->d_inode); + update_mtime(dir, subfs_dir->d_inode); + update_ctime(dentry->d_inode, subdent->d_inode); + + /* If subdent->d_count == 1 (which means that this function is the + * only holder of dentry then d_delete called a little earlier will + * dentry_iput subdent, so if its inode has no other links inode_delete + * will be called at this point, otherwise inode_delete will be + * called when other link(s) to this inode is deleted. So we + * can safely put_write_access now. + */ + /* Then, if subdent->d_count != 1 then last iput will be called on last + * dput (if inode has no other links) so we will have to check for + * ->i_nlink == 0 in supermount_release + */ + + mark_subfs_dirty(dir->i_sb); + subfs_put_write_access(dir->i_sb); + +dput_ret: + dput(subdent); +dirput_ret: + dput(subfs_dir); +out: + subfs_go_inactive(dir->i_sb); + return rc; +} + +static int +supermount_symlink(struct inode *dir, struct dentry *dentry, + const char *symname) +{ + struct dentry *subdent, *subfs_dir; + int rc; + + rc = -EIO; + if (subfs_go_online(dir->i_sb)) + goto out; + + subfs_dir = get_subfs_dentry_by_inode(dir); + rc = PTR_ERR(subfs_dir); + if (IS_ERR(subfs_dir)) + goto out; + + rc = -ENOENT; + if (!subfs_dir->d_inode) + goto dirput_ret; + + subdent = get_subdent(subfs_dir, dentry); + rc = PTR_ERR(subdent); + if (IS_ERR(subdent)) + goto dirput_ret; + + rc = 0; + if (subdent->d_inode && !dentry->d_inode) + goto attach; + + rc = subfs_get_write_access(dir->i_sb); + if (rc) + goto dput_ret; + + rc = vfs_symlink(subfs_dir->d_inode, subdent, symname); + /* Now we have subinode, attached to subdent. */ + + mark_subfs_dirty(dir->i_sb); + subfs_put_write_access(dir->i_sb); + + if (rc) + goto dput_ret; + + if (!subdent->d_inode) { + d_instantiate(dentry, NULL); + goto dput_ret; + } + + /* It worked, so now create a shadow supermount inode for the + result... */ +attach: + attach_and_prepare(dir->i_sb, dentry, subdent, 1); +dput_ret: + dput(subdent); +dirput_ret: + dput(subfs_dir); +out: + subfs_go_inactive(dir->i_sb); + return rc; +} + +static int +supermount_mkdir(struct inode *dir, struct dentry *dentry, int mode) +{ + struct dentry *subdent, *subfs_dir; + int rc; + + rc = -EIO; + if (subfs_go_online(dir->i_sb)) + goto out; + + subfs_dir = get_subfs_dentry_by_inode(dir); + rc = -ENOENT; + if (IS_ERR(subfs_dir)) + goto out; + + if (!subfs_dir->d_inode) + goto dirput_ret; + + subdent = get_subdent(subfs_dir, dentry); + rc = PTR_ERR(subdent); + if (IS_ERR(subdent)) + goto dirput_ret; + + rc = 0; + if (subdent->d_inode && !dentry->d_inode) + goto attach; + + rc = subfs_get_write_access(dir->i_sb); + if (rc) + goto dput_ret; + + rc = vfs_mkdir(subfs_dir->d_inode, subdent, mode); + + update_nlink(dir, subfs_dir->d_inode); + + mark_subfs_dirty(dir->i_sb); + subfs_put_write_access(dir->i_sb); + + if (rc) + goto dput_ret; + + if (!subdent->d_inode) { + d_instantiate(dentry, NULL); + goto dput_ret; + } + + /* It worked, so now create a shadow supermount inode for the + result... */ +attach: + attach_and_prepare(dir->i_sb, dentry, subdent, 1); +dput_ret: + dput(subdent); +dirput_ret: + dput(subfs_dir); /* We don't need it anymore */ +out: + subfs_go_inactive(dir->i_sb); + return rc; +} + +static int +supermount_rmdir(struct inode *dir, struct dentry *dentry) +{ + struct dentry *subdent, *subfs_dir; + int rc = -EIO; + + if (subfs_go_online(dir->i_sb)) + goto out; + + rc = -ENOENT; + subfs_dir = get_subfs_dentry_by_inode(dir); + if (IS_ERR(subfs_dir)) + goto out; + + if (!subfs_dir->d_inode) + goto dput_subdir; + + subdent = get_subfs_dentry(dentry); + if (IS_ERR(subdent)) + goto dput_subdir; + + rc = subfs_get_write_access(dir->i_sb); + if (rc) + goto dput_subdent; + + rc = vfs_rmdir(subfs_dir->d_inode, subdent); + if (rc) + goto put_write_access; + + update_nlink(dir, subfs_dir->d_inode); + update_nlink(dentry->d_inode, subdent->d_inode); + update_ctime(dir, subfs_dir->d_inode); + update_mtime(dir, subfs_dir->d_inode); + update_ctime(dentry->d_inode, subdent->d_inode); + + mark_subfs_dirty(dir->i_sb); +put_write_access: + subfs_put_write_access(dir->i_sb); +dput_subdent: + dput(subdent); +dput_subdir: + dput(subfs_dir); +out: + subfs_go_inactive(dir->i_sb); + return rc; +} + +static int +supermount_mknod(struct inode *dir, struct dentry *dentry, int + mode, int dev) +{ + struct dentry *subdent, *subfs_dir; + int rc = -EIO; + + if (subfs_go_online(dir->i_sb)) + goto out; + + rc = -ENOENT; + subfs_dir = get_subfs_dentry_by_inode(dir); + if (IS_ERR(subfs_dir)) + goto out; + + if (!subfs_dir->d_inode) + goto dirput_ret; + + subdent = get_subdent(subfs_dir, dentry); + rc = PTR_ERR(subdent); + if (IS_ERR(subdent)) + goto dirput_ret; + + rc = 0; + if (subdent->d_inode && !dentry->d_inode) + goto attach; + + rc = subfs_get_write_access(dir->i_sb); + if (rc) + goto dput_ret; + + rc = vfs_mknod(subfs_dir->d_inode, subdent, mode, dev); + /* Now we have subinode, attached to subdent. */ + + mark_subfs_dirty(dir->i_sb); + subfs_put_write_access(dir->i_sb); + + if (rc) + goto dput_ret; + + if (!subdent->d_inode) { + d_instantiate(dentry, NULL); + goto dput_ret; + } + + /* It worked, so now create a shadow supermount inode for the + result... */ +attach: + attach_and_prepare(dir->i_sb, dentry, subdent, 1); +dput_ret: + dput(subdent); +dirput_ret: + dput(subfs_dir); +out: + subfs_go_inactive(dir->i_sb); + return rc; +} + +static int +supermount_rename(struct inode *olddir, struct dentry *olddentry, + struct inode *newdir, struct dentry *newdentry) +{ + struct dentry *oldsubdent, *newsubdent, *oldsubfs_dir, *newsubfs_dir; + int rc = -EIO; + + if (subfs_go_online(olddir->i_sb)) + goto out; + + rc = -ENOENT; + oldsubdent = get_subfs_dentry(olddentry); + if (IS_ERR(oldsubdent)) + goto out; + + oldsubfs_dir = get_subfs_dentry_by_inode(olddir); + if (IS_ERR(oldsubfs_dir)) + goto olddput_ret; + + if (!oldsubfs_dir->d_inode) + goto olddirput_ret; + + newsubfs_dir = get_subfs_dentry_by_inode(newdir); + if (IS_ERR(newsubfs_dir)) + goto olddirput_ret; + + newsubdent = get_subfs_dentry(newdentry); + if (IS_ERR(newsubdent)) { + rc = PTR_ERR(newsubdent); + goto newdirput_ret; + } + + rc = subfs_get_write_access(olddir->i_sb); + if (rc) + goto newdput_ret; + + rc = vfs_rename(oldsubfs_dir->d_inode, oldsubdent, + newsubfs_dir->d_inode, newsubdent); + update_nlink(olddir, oldsubfs_dir->d_inode); + update_nlink(newdir, newsubfs_dir->d_inode); + update_nlink(olddentry->d_inode, oldsubdent->d_inode); + update_ctime(olddir, oldsubfs_dir->d_inode); + update_mtime(olddir, oldsubfs_dir->d_inode); + update_ctime(olddentry->d_inode, oldsubdent->d_inode); + + subfs_put_write_access(olddir->i_sb); +newdput_ret: + dput(newsubdent); +newdirput_ret: + dput(newsubfs_dir); +olddirput_ret: + dput(oldsubfs_dir); +olddput_ret: + dput(oldsubdent); +out: + subfs_go_inactive(olddir->i_sb); + return rc; +} + +static int +supermount_readlink(struct dentry *dentry, char *buffer , int buflen) +{ + struct dentry *subdent; + int rc = -EIO; + + if (subfs_go_online(dentry->d_sb)) + goto out; + + subdent = get_subfs_dentry(dentry); + if (IS_ERR(subdent)) + goto out; + + rc = -ENOENT; + if (!subdent->d_inode) + goto dput_ret; + + rc = vfs_readlink_real(subdent, buffer, buflen); +dput_ret: + dput(subdent); +out: + subfs_go_inactive(dentry->d_sb); + return rc; +} + +static int +supermount_follow_link(struct dentry *dentry, struct nameidata *nd) +{ + struct dentry *subdent = get_subfs_dentry(dentry); + int rc = PTR_ERR(subdent); + + if (IS_ERR(subdent)) + goto out; + + rc = -ENOENT; + if (!subdent->d_inode) + goto fail; + + if (subdent->d_inode && subdent->d_inode->i_op + && subdent->d_inode->i_op->follow_link) + rc = subdent->d_inode->i_op->follow_link(subdent, nd); + else { + rc = -ENOENT; + goto fail; + } + + dput(subdent); + return rc; +fail: + dput(subdent); +out: + return vfs_follow_link(nd, ERR_PTR(rc)); +} + +static int +supermount_permission(struct inode *inode, int mask) +{ + struct inode *subi; + struct dentry *subdent; + int rc = -EIO; + int need_write_access; + + if (subfs_go_online(inode->i_sb)) + goto out; + + subdent = get_subfs_dentry_by_inode(inode); + if (IS_ERR(subdent)) { + rc = PTR_ERR(subdent); + goto out; + } + subi = subdent->d_inode; + if (!subi) + goto dput_ret; + need_write_access = (mask & MAY_WRITE) && IS_RDONLY(subi); + if (need_write_access) { + rc = subfs_get_write_access(inode->i_sb); + if (rc) + goto dput_ret; + } + rc = permission(subi, mask); + if (need_write_access) + subfs_put_write_access(inode->i_sb); +dput_ret: + dput(subdent); +out: + subfs_go_inactive(inode->i_sb); + return rc; +} + +static int +supermount_setattr(struct dentry *dentry, struct iattr *attr) +{ + struct inode *subi, *inode = dentry->d_inode; + struct dentry *subdent; + int rc = -ENOENT; + + if (!inode) + return -EINVAL; + + rc = -EIO; + if (subfs_go_online(dentry->d_sb)) + goto out; + + subdent = get_subfs_dentry(dentry); + if (IS_ERR(subdent)) { + rc = PTR_ERR(subdent); + goto out; + } + subi = subdent->d_inode; + if (!subi) + goto dput_ret; + + rc = subfs_get_write_access(inode->i_sb); + if (rc) + goto dput_ret; + + rc = notify_change(subdent, attr); + + if (!rc) { + /* + * If it worked, then we need to mark the modification + * to the subfs, and we also need to propogate the + * change up to the shadowing inode. + */ + mark_subfs_dirty(inode->i_sb); + attr->ia_mode = subi->i_mode; + attr->ia_uid = subi->i_uid; + attr->ia_gid = subi->i_gid; + attr->ia_size = subi->i_size; + attr->ia_atime = subi->i_atime; + attr->ia_mtime = subi->i_mtime; + attr->ia_ctime = subi->i_ctime; + attr->ia_valid = + ATTR_UID | ATTR_GID | ATTR_MODE | ATTR_SIZE | + ATTR_ATIME | ATTR_MTIME | ATTR_CTIME; + inode_setattr(inode, attr); + } + + subfs_put_write_access(inode->i_sb); +dput_ret: + dput(subdent); +out: + subfs_go_inactive(inode->i_sb); + return rc; +} + +static int +supermount_getattr(struct vfsmount *mnt, struct dentry *dentry, + struct kstat *stat) +{ + struct inode *subi, *inode = dentry->d_inode; + struct dentry *subdent; + int rc; + + if (!inode) + return -EINVAL; + + rc = -EIO; + if (subfs_go_online(dentry->d_sb)) + goto out; + + rc = -ENOENT; + subdent = get_subfs_dentry(dentry); + if (IS_ERR(subdent)) { + rc = PTR_ERR(subdent); + goto out; + } + subi = subdent->d_inode; + if (!subi) + goto dput_ret; + + getattr_full(mnt, dentry, stat); + stat->ino = subi->i_ino; + rc = 0; +dput_ret: + dput(subdent); +out: + subfs_go_inactive(inode->i_sb); + return rc; +} + +static int +supermount_root_getattr(struct vfsmount *mnt, struct dentry *dentry, + struct kstat *stat) +{ + struct supermount_sb_info *sbi; + struct inode *inode = dentry->d_inode; + int retval; + + if (!inode) + return -EINVAL; + + sbi = supermount_sbi(dentry->d_sb); + if (subfs_is_mounted(sbi)) + retval = supermount_getattr(mnt, dentry, stat); + else + retval = -EIO; + + if (retval == -EIO) + /* there are problems for mounting */ + return getattr_full(mnt, dentry, stat); + + return retval; +} + +/* + * directories can handle most operations... supermount/namei.c just + * passes them through to the underlying subfs, except for lookup(). + */ + +/* truncate: is not necesary, handled with setattr + * revalidate: only needed by nfs + * FixMe: implement accl functions + */ +struct inode_operations supermount_root_iops = { + .create = supermount_create, + .lookup = supermount_lookup, + .link = supermount_link, + .unlink = supermount_unlink, + .symlink = supermount_symlink, + .mkdir = supermount_mkdir, + .rmdir = supermount_rmdir, + .mknod = supermount_mknod, + .rename = supermount_rename, + .permission = supermount_permission, + .setattr = supermount_setattr, + .getattr = supermount_root_getattr, +}; + +struct inode_operations supermount_dir_iops = { + .create = supermount_create, + .lookup = supermount_lookup, + .link = supermount_link, + .unlink = supermount_unlink, + .symlink = supermount_symlink, + .mkdir = supermount_mkdir, + .rmdir = supermount_rmdir, + .mknod = supermount_mknod, + .rename = supermount_rename, + .permission = supermount_permission, + .setattr = supermount_setattr, + .getattr = supermount_getattr, +}; + +struct inode_operations supermount_symlink_iops = { + .readlink = supermount_readlink, + .follow_link = supermount_follow_link, + .setattr = supermount_setattr, + .getattr = supermount_getattr, +}; + +struct inode_operations supermount_file_iops = { + .setattr = supermount_setattr, + .getattr = supermount_getattr, +}; diff -Naurp linux-2.4.20-ck4/fs/supermount/Makefile linux-2.4.20-ck5/fs/supermount/Makefile --- linux-2.4.20-ck4/fs/supermount/Makefile 1970-01-01 10:00:00.000000000 +1000 +++ linux-2.4.20-ck5/fs/supermount/Makefile 2003-04-08 10:52:51.000000000 +1000 @@ -0,0 +1,13 @@ +# +# Makefile for the linux supermounting routines. +# + +O_TARGET := supermount.o + +obj-y := super.o init.o mediactl.o translation.o super_operations.o \ + file_operations.o inode_operations.o \ + dentry_operations.o + +obj-m := $(O_TARGET) + +include $(TOPDIR)/Rules.make diff -Naurp linux-2.4.20-ck4/fs/supermount/mediactl.c linux-2.4.20-ck5/fs/supermount/mediactl.c --- linux-2.4.20-ck4/fs/supermount/mediactl.c 1970-01-01 10:00:00.000000000 +1000 +++ linux-2.4.20-ck5/fs/supermount/mediactl.c 2003-04-08 10:52:51.000000000 +1000 @@ -0,0 +1,40 @@ +/* + * linux/fs/supermount/mediactl.c + * + * Original version: + * Copyright (C) 1995, 1997 + * Stephen Tweedie (sct@dcs.ed.ac.uk) + * + * Rewriten for kernel 2.2, 2.4. (C) 1999, 2000 Alexis Mikhailov + * (alexis@abc.cap.ru) + * + */ + +#include +#include +#include +#include + +#include "supermount_i.h" + +/* + * Try to lock the drive door. This is not guaranteed to work on all + * hardware, but we try any tricks we know of anyway. + */ +void +supermount_handle_door(struct super_block *sb, int operation) +{ + kdev_t dev; + int major; + const struct block_device_operations *fops; + struct supermount_sb_info *sbi = supermount_sbi(sb); + struct super_block *subsb = supermount_subfs_sb(sbi); + + dev = subsb->s_dev; + major = MAJOR(dev); + if (major >= MAX_BLKDEV || !(fops = get_blkfops(major))) + return; + if (!fops->mediactl) + return; + fops->mediactl(dev, operation, 0); +} diff -Naurp linux-2.4.20-ck4/fs/supermount/super.c linux-2.4.20-ck5/fs/supermount/super.c --- linux-2.4.20-ck4/fs/supermount/super.c 1970-01-01 10:00:00.000000000 +1000 +++ linux-2.4.20-ck5/fs/supermount/super.c 2003-04-08 10:52:51.000000000 +1000 @@ -0,0 +1,452 @@ +/* + * linux/fs/supermount/super.c + * + * Original version: + * Copyright (C) 1995, 1997 + * Stephen Tweedie (sct@dcs.ed.ac.uk) + * + * from + * + * linux/fs/minix/inode.c + * Copyright (C) 1991, 1992 Linus Torvalds + * + * and + * + * linux/fs/ext2/super.c + * Copyright (C) 1992, 1993, 1994, 1995 Remy Card + * + * Rewriten for kernel 2.2, 2.4. (C) 1999, 2000 Alexis Mikhailov + * (alexis@abc.cap.ru) + * + */ + +#include +#include +#include +#include + +#include "supermount_i.h" + +struct inode * +supermount_create_inode(struct super_block *s) +{ + struct inode *inode = new_inode(s); + struct supermount_sb_info *sbi = supermount_sbi(s); + + if (!inode) + return NULL; + + inode->i_mode = sbi->s_default_mode; + if (s->s_root) { + inode->i_uid = s->s_root->d_inode->i_uid; + inode->i_gid = s->s_root->d_inode->i_gid; + } else { + inode->i_uid = 0; + inode->i_gid = 0; + } + inode->i_blksize = inode->i_sb->s_blocksize; + inode->i_rdev = 0; + inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME; + supermount_i(inode)->i_sb_version = sbi->s_version; + list_add(&(supermount_i(inode)->i_fs), &sbi->s_inodes); + + return inode; +} + +static void +supermount_clean_inodes(struct super_block *sb) +{ + struct supermount_sb_info *sbi = supermount_sbi(sb); + struct list_head *ptr; + struct supermount_inode_info *info; + + list_for_each(ptr, &sbi->s_inodes) { + info = list_entry(ptr, struct supermount_inode_info, i_fs); + if (!info) + BUG(); + if (!info->i_dentry) + continue; + dput(info->i_dentry); + info->i_dentry = NULL; + } +} + +/* + * Cut off busy dentries unless mounted over. They go away when + * their owners die + * We run under mutex or sb lock so nobody can change directory structure + * d_drop holds dcache lock for us + * FIXME: it probably should unmount submounts + */ +static void +supermount_drop_dentries(struct super_block *sb) +{ + struct dentry *d; + struct list_head *ptr; + + if (!sb->s_root) + return; + + list_for_each(ptr, &sb->s_root->d_subdirs) { + d = list_entry(ptr, struct dentry, d_child); + + if (!have_submounts(d)) + d_drop(d); + } +} + +void +subfs_umount(struct super_block *sb) +{ + struct supermount_sb_info *sbi = supermount_sbi(sb); + struct vfsmount *mnt = sbi->s_undermount; + struct inode *root_inode; + + if (sb->s_root) { + root_inode = sb->s_root->d_inode; + if (!supermount_i(root_inode)->i_dentry) + BUG(); + dput(supermount_i(root_inode)->i_dentry); + supermount_i(root_inode)->i_dentry = NULL; + } + shrink_dcache_sb(sb); + supermount_clean_inodes(sb); + supermount_drop_dentries(sb); + BUG_ON(!mnt); + mntput(mnt); + sbi->s_state = SUPERMOUNT_UNMOUNTED; + sbi->s_undermount = NULL; + sbi->s_version++; + sb->s_flags = sbi->s_mflags; +} + +static int +subfs_remount(struct super_block *sb, int flags) +{ + struct supermount_sb_info *sbi = supermount_sbi(sb); + struct super_block *subsb = supermount_subfs_sb(sbi); + int real_flags = (sb->s_flags | flags) & MS_RMT_MASK; + int retval; + + retval = do_remount_sb(subsb, real_flags, NULL); + if (flags & MS_RDONLY) + fsync_dev(subsb->s_dev); + return retval; +} + +static inline int +subfs_remount_ro(struct super_block *sb) +{ + int retval = subfs_remount(sb, MS_RDONLY); + + if (retval) + return retval; + unmark_subfs_rw(sb); + return 0; +} + +static inline int +subfs_remount_rw(struct super_block *sb) +{ + int retval = subfs_remount(sb, 0); + + if (retval) + return retval; + mark_subfs_rw(sb); + return 0; +} + +static int +subfs_real_mount(struct super_block *sb, char *type) +{ + struct supermount_sb_info *sbi = supermount_sbi(sb); + struct vfsmount *mnt; + int retval; + + if (sbi->s_data_copy) + strcpy(sbi->s_data, sbi->s_data_copy); + + mnt = do_kern_mount(type, + sb->s_flags | MS_MGC_VAL | MS_SUPERMOUNTED, + sbi->s_devname, sbi->s_data); + + if (IS_ERR(mnt)) { + retval = PTR_ERR(mnt); + } else { + retval = 0; + mnt->mnt_sb->s_media_changed = 0; + sbi->s_undermount = mnt; + } + return retval; +} + +static int +subfs_real_mount2(struct super_block *sb, char *type) +{ + struct supermount_sb_info *sbi = supermount_sbi(sb); + struct inode *root_inode; + int retval = 0; + + sb->s_flags = sbi->s_mflags; + + retval = subfs_real_mount(sb, type); + + if (retval == -EROFS) { + sb->s_flags |= MS_RDONLY; + retval = subfs_real_mount(sb, type); + if (!retval) + goto assign_root_inode; + } + if (retval) + goto out; + + if (!(sb->s_flags & MS_RDONLY)) { + retval = subfs_remount_ro(sb); + if (retval) + goto out; + } +assign_root_inode: + root_inode = sb->s_root->d_inode; + if (supermount_i(root_inode)->i_dentry) + BUG(); + supermount_i(root_inode)->i_dentry = dget(sbi->s_undermount->mnt_root); +out: + return retval; +} + +static char *types_in_order[] = { + "udf", + "iso9660", + "vfat", + "msdos", + "ext2", + NULL +}; + +/* + * -ENXIO + * + */ + +static int +subfs_mount(struct super_block *sb) +{ + struct supermount_sb_info *sbi = supermount_sbi(sb); + int retval = -ENODEV; + char **type = types_in_order; + + if (sbi->s_type && strcmp(sbi->s_type, "auto")) + return subfs_real_mount2(sb, sbi->s_type); + + while (*type && retval + && retval != -ENXIO + && retval != -ENOMEDIUM + && retval != -ENOENT) { + retval = subfs_real_mount2(sb, *type); + type++; + } + + return retval; +} + +/* We used not to check the medium if we was already mounted, this + * provoked problems in floppies & things that don't have a working + * locking mechanism, now we _always_ check if the medium has changed. + */ + +/* Return 0 (OK) if medium is valid. */ +int +subfs_go_online(struct super_block *sb) +{ + int retval = 0; + struct supermount_sb_info *sbi = supermount_sbi(sb); + + down(&sbi->sem); + + if (subfs_is_mounted(sbi)) { + struct super_block *subsb = supermount_subfs_sb(sbi); + kdev_t dev = subsb->s_dev; + + /* + * check for disk change or disk not present + * + * FIXME there is a race here; check must be protected + * with mutex; currently it is possible that media changed + * flag is stolen + */ + + if (!(subsb->s_media_changed || query_disk_change(dev))) + goto mounted; + + /* We have a disk change! Unmount the subfs */ + subfs_umount(sb); + /* The call to disk_changed may clear the media- + changed flag on the device, so we need to force a + media invalidation. We don't want to do this + before unmounting the subfs, naturally! */ + invalidate_media(dev); + } + /* OK, we're unmounted now --- can we remount? Please? */ + if (sbi->s_undermount) + BUG(); + + retval = subfs_mount(sb); + if (retval) { + retval = 1; + goto out; + } + sbi->s_version++; + supermount_i(sb->s_root->d_inode)->i_sb_version = sbi->s_version; + unmark_subfs_rw(sb); + sbi->s_state = SUPERMOUNT_SUSPENDED; +mounted: + switch (sbi->s_state) { + case SUPERMOUNT_UNMOUNTED: + BUG(); + case SUPERMOUNT_SUSPENDED: + supermount_lock_door(sb); + sbi->s_state = SUPERMOUNT_ONLINE; + break; + case SUPERMOUNT_ONLINE: + /* No need to do anything */ ; + } +out: + atomic_inc(&sbi->s_opencount); + up(&sbi->sem); + return retval; +} + +/* + * Check the state of the supermount filesystem after it has been + * released by some inode. If it is no longer active, do the necessary + * cleanup: fsync the disk, remount it readonly if appropriate, and + * unlock the drive door if possible. + */ + +void +subfs_go_inactive(struct super_block *sb) +{ + struct supermount_sb_info *sbi = supermount_sbi(sb); + + down(&sbi->sem); + + /* there was a problem mounting */ + if (!subfs_is_mounted(sbi)) + goto out; + + if ((atomic_read(&sbi->s_opencount) > 1)) + goto out; + + /* + * Do an initial fsync_dev. We do this before locking the + * superblock to minimise the duration of the lock. + */ + if (subfs_is_dirty(sbi)) { + fsync_dev(sb->s_dev); + mark_subfs_clean(sb); + } + + /* + * First thing is, has the filesystem been made + * read-write since it became active? If so, now is the + * time to retire it back to read-only state. + */ + + if (subfs_is_rw(sbi)) { + int retval = subfs_remount_ro(sb); + BUG_ON(retval); + } + supermount_unlock_door(sb); + sbi->s_state = SUPERMOUNT_SUSPENDED; +out: + atomic_dec(&sbi->s_opencount); + up(&sbi->sem); +} + +/* + * Try to put the filesystem into writable mode for the duration of + * an open(O_RDWR) or an inode operation requiring write access. + */ + +int +subfs_get_write_access(struct super_block *sb) +{ + int retval; + struct supermount_sb_info *sbi = supermount_sbi(sb); + + down(&sbi->sem); + if (!subfs_is_online(sbi)) + BUG(); + + retval = 0; + if (atomic_read(&sbi->s_writecount) == 0) { + /* + * We may already have write access to the filesystem + */ + if (!subfs_is_rw(sbi)) { + if (sb->s_flags & MS_RDONLY) + retval = -EROFS; + else + retval = subfs_remount_rw(sb); + } + } + + if (!retval) + atomic_inc(&sbi->s_writecount); + up(&sbi->sem); + + return retval; +} + +/* + * Once we're done, release write access. + */ +void +subfs_put_write_access(struct super_block *sb) +{ + struct supermount_sb_info *sbi = supermount_sbi(sb); + + down(&sbi->sem); + if (!subfs_is_rw(sbi)) + BUG(); + + BUG_ON(atomic_read(&sbi->s_writecount) == 0); + atomic_dec(&sbi->s_writecount); + up(&sbi->sem); +} + +/* Supermount open/close inode. These functions maintain the + superblock reference counts of active inodes; the subfs is + suspended when that count reaches zero. + + Files are opened on any significant reference, and are not closed + until they become fully dereferenced (during the last iput). + Directories, on the other hand, are always closed unless they are + active and opened; a directory referenced as CWD is not open. +*/ + +void +subfs_get_read_access(struct super_block *sb) +{ + struct supermount_sb_info *sbi = supermount_sbi(sb); + + down(&sbi->sem); + BUG_ON(!subfs_is_online(sbi)); + atomic_inc(&sbi->s_opencount); + up(&sbi->sem); +} + +/* we can't check here for subfs_is_online, because in the case of one + * error, system will call ->release() on inodes, where subfs is not + * online. + */ + +void +subfs_put_read_access(struct super_block *sb) +{ + struct supermount_sb_info *sbi = supermount_sbi(sb); + + down(&sbi->sem); + atomic_dec(&sbi->s_opencount); + up(&sbi->sem); +} diff -Naurp linux-2.4.20-ck4/fs/supermount/supermount_i.h linux-2.4.20-ck5/fs/supermount/supermount_i.h --- linux-2.4.20-ck4/fs/supermount/supermount_i.h 1970-01-01 10:00:00.000000000 +1000 +++ linux-2.4.20-ck5/fs/supermount/supermount_i.h 2003-04-08 10:52:51.000000000 +1000 @@ -0,0 +1,215 @@ +#ifndef _SUPERMOUNT_I_H +#define _SUPERMOUNT_I_H + +#define SUPERMOUNT_DEBUG + +/* + * The supermount superblock magic number + */ + +#define SUPERMOUNT_SUPER_MAGIC 0x9fa1 + +static inline int +is_inode_supermounted(struct inode *inode) +{ + return (inode->i_sb && + (inode->i_sb->s_magic == SUPERMOUNT_SUPER_MAGIC)); +} + +static inline int +is_dentry_supermounted(struct dentry *dentry) +{ + return (dentry->d_inode && is_inode_supermounted(dentry->d_inode)); +} + +#define SUPERMOUNT_VERSION "0.7" + +/* + * The difference between SUSPENDED and ONLINE is that in SUSPENDED + * the door is unlocked, and in ONLINE it is locked. + */ + +typedef enum { + SUPERMOUNT_UNMOUNTED, /* No media mounted */ + SUPERMOUNT_SUSPENDED, /* Mounted but suspended because + no files open */ + SUPERMOUNT_ONLINE, /* Mounted and active */ +} sm_state_t; + +/* + * supermount super-block data in memory + */ +struct supermount_sb_info { + sm_state_t s_state; + int s_version; /* Used to indicate obsolete inodes */ + mode_t s_default_mode; /* Default mode for supermount root */ + + char *s_type; /* Type of fs to be sub-mounted */ + char *s_devname; /* Where to mount the subfs from */ + /* unused */ + int s_mflags; /* Flags to pass when mounting subfs */ + char *s_data; /* Data to pass when mounting subfs */ + char *s_data_copy; /* copy of previous data. FS + destroy that data, and we + need to mount several + times. We could allocate + the memory in + supermount_mount, but then + we have more possible + errors */ + struct vfsmount *s_undermount; /* Mount point for subfs */ + atomic_t s_opencount; /* Refcount of opened inodes + (an inode in use as cwd + does not count as open) */ + atomic_t s_writecount; /* Refcount of write accesses + on the filesystem */ + /* Flags */ + int s_dirty:1; /* Do we need to fsync() the subfs? */ + int s_rw:1; /* Is the subfs mounted read/write? */ + struct list_head s_inodes; + struct semaphore sem; +}; + +static inline struct supermount_inode_info * +supermount_i(struct inode *inode) +{ + return (&inode->u.supermount_i); +} + +static inline struct supermount_sb_info * +supermount_sbi(struct super_block *sb) +{ + if (!sb) + BUG(); + return (struct supermount_sb_info *) (sb->u.generic_sbp); +} + +static inline struct super_block * +supermount_subfs_sb(struct supermount_sb_info *sbi) +{ + return sbi->s_undermount->mnt_sb; +} + +/* + * Function prototypes + */ + +static inline struct file * +get_subfile(struct file *file) +{ + struct file *subfile = (struct file *) file->private_data; + if (!subfile) + BUG(); + return subfile; +} + +static inline void +put_subfile(struct file *file, struct file *subfile) +{ + file->private_data = (void *) subfile; +} + +/* How to test if an inode is obsolete: */ +static inline int +is_inode_obsolete(struct inode *inode) +{ + struct supermount_sb_info *sbi = supermount_sbi(inode->i_sb); + + return (supermount_i(inode)->i_sb_version != sbi->s_version); +} + +/* Manage the subfs dirty flag */ +static inline void +mark_subfs_dirty(struct super_block *sb) +{ + struct supermount_sb_info *sbi = supermount_sbi(sb); + + sbi->s_dirty = 1; +} +static inline void +mark_subfs_clean(struct super_block *sb) +{ + struct supermount_sb_info *sbi = supermount_sbi(sb); + + sbi->s_dirty = 0; +} +static inline int +subfs_is_dirty(struct supermount_sb_info *sbi) +{ + return (sbi->s_dirty); +} + +/* Manage the subfs rw flag */ +static inline void +mark_subfs_rw(struct super_block *sb) +{ + struct supermount_sb_info *sbi = supermount_sbi(sb); + + sbi->s_rw = 1; +} +static inline void +unmark_subfs_rw(struct super_block *sb) +{ + struct supermount_sb_info *sbi = supermount_sbi(sb); + + sbi->s_rw = 0; +} +static inline int +subfs_is_rw(struct supermount_sb_info *sbi) +{ + return (sbi->s_rw); +} + +static inline int +subfs_is_online(struct supermount_sb_info *sbi) +{ + return (sbi->s_state == SUPERMOUNT_ONLINE); +} + +static inline int +subfs_is_mounted(struct supermount_sb_info *sbi) +{ + return (sbi->s_state != SUPERMOUNT_UNMOUNTED); +} + +/* super.c */ +extern struct inode *supermount_create_inode(struct super_block *s); +extern void subfs_umount(struct super_block *sb); +extern int subfs_go_online(struct super_block *); +extern void subfs_go_inactive(struct super_block *); +extern int subfs_get_write_access(struct super_block *); +extern void subfs_put_write_access(struct super_block *); +extern void subfs_get_read_access(struct super_block *); +extern void subfs_put_read_access(struct super_block *); +/* translate.c */ +extern struct dentry *get_subfs_dentry(struct dentry *dentry); +extern struct dentry *get_subfs_dentry_by_inode(struct inode *inode); +/* mediactl.c */ +extern void supermount_handle_door(struct super_block *, int); +static inline void +supermount_lock_door(struct super_block *sb) +{ + supermount_handle_door(sb, MEDIA_LOCK); +} +static inline void +supermount_unlock_door(struct super_block *sb) +{ + supermount_handle_door(sb, MEDIA_UNLOCK); +} + +/* inode_operations.c */ +extern struct inode_operations supermount_root_iops; +extern struct inode_operations supermount_dir_iops; +extern struct inode_operations supermount_file_iops; +extern struct inode_operations supermount_symlink_iops; +/* super_operations.c */ +extern struct super_block *supermount_read_super(struct super_block *, + void *, int); +/* file_operations.c */ +extern struct file_operations supermount_dir_operations; +extern struct file_operations supermount_file_operations; +/* dentry_operations.c */ +extern struct dentry_operations supermount_file_dops; +extern struct dentry_operations supermount_dir_dops; + +#endif /* _SUPERMOUNT_I_H */ diff -Naurp linux-2.4.20-ck4/fs/supermount/super_operations.c linux-2.4.20-ck5/fs/supermount/super_operations.c --- linux-2.4.20-ck4/fs/supermount/super_operations.c 1970-01-01 10:00:00.000000000 +1000 +++ linux-2.4.20-ck5/fs/supermount/super_operations.c 2003-04-08 10:52:51.000000000 +1000 @@ -0,0 +1,301 @@ +/* + * linux/fs/supermount/super_operations.c + * + * Original version: + * Copyright (C) 1995, 1997 + * Stephen Tweedie (sct@dcs.ed.ac.uk) + * + * from + * + * linux/fs/minix/inode.c + * Copyright (C) 1991, 1992 Linus Torvalds + * + * and + * + * linux/fs/ext2/super.c + * Copyright (C) 1992, 1993, 1994, 1995 Remy Card + * + * Rewriten for kernel 2.2, 2.4. (C) 1999, 2000 Alexis Mikhailov + * (alexis@abc.cap.ru) + * + */ + +#include +#include +#include +#include + +#include "supermount_i.h" + +static struct super_operations supermount_sops; + +static void +supermount_write_inode(struct inode *inode, int sync) +{ + struct supermount_sb_info *sbi = supermount_sbi(inode->i_sb); + struct super_block *subsb; + struct dentry *dent; + struct inode *subi; + int rc = -EIO; + + if (subfs_go_online(inode->i_sb)) + goto out; + + rc = 0; + dent = get_subfs_dentry_by_inode(inode); + if (IS_ERR(dent)) + goto out; + subi = dent->d_inode; + if (!subi) + goto dput_ret; + subsb = supermount_subfs_sb(sbi); + if (subi && subsb && subsb->s_op && subsb->s_op->write_inode) { + subi->i_mode = inode->i_mode; + subi->i_uid = inode->i_uid; + subi->i_gid = inode->i_gid; + subi->i_size = inode->i_size; + subi->i_atime = inode->i_atime; + subi->i_mtime = inode->i_mtime; + subi->i_ctime = inode->i_ctime; + rc = subfs_get_write_access(inode->i_sb); + if (rc) + goto dput_ret;; + subi->i_state |= I_LOCK; + subsb->s_op->write_inode(subi, sync); + subi->i_state &= ~I_LOCK; + mark_subfs_dirty(inode->i_sb); + subfs_put_write_access(inode->i_sb); + } +dput_ret: + dput(dent); +out: + subfs_go_inactive(inode->i_sb); +} + +static void +supermount_clear_inode(struct inode *inode) +{ + dput(supermount_i(inode)->i_dentry); + list_del(&supermount_i(inode)->i_fs); +} + +static struct supermount_sb_info * +create_sbi(struct super_block *sb) +{ + struct supermount_sb_info *sbi = kmalloc(sizeof (*sbi), GFP_KERNEL); + + if (!sbi) + return NULL; + + memset(sbi, 0, sizeof (*sbi)); + + sbi->s_state = SUPERMOUNT_UNMOUNTED; + sbi->s_default_mode = 0777 | S_IFDIR; + atomic_set(&sbi->s_opencount, 0); + atomic_set(&sbi->s_writecount, 0); + sbi->s_mflags = sb->s_flags & MS_RMT_MASK; + INIT_LIST_HEAD(&sbi->s_inodes); + sema_init(&sbi->sem, 1); + return sbi; +} + +static void +free_sbi(struct supermount_sb_info *sbi) +{ + if (sbi->s_type) + kfree(sbi->s_type); + if (sbi->s_devname) + kfree(sbi->s_devname); + if (sbi->s_data) + kfree(sbi->s_data); + if (sbi->s_data_copy) + kfree(sbi->s_data_copy); + kfree(sbi); + +} + +/* Release the superblock and any resources it has reserved + * it is called with the sb locked + */ + +static void +supermount_put_super(struct super_block *sb) +{ + struct supermount_sb_info *sbi = supermount_sbi(sb); + + if (subfs_is_mounted(sbi)) + subfs_umount(sb); + + sb->u.generic_sbp = 0; + free_sbi(sbi); + sb->s_dev = 0; +} + +static int +copy_option(char **option, const char *val) +{ + char *tmp; + if (!val || !*val) + return -EINVAL; + tmp = kmalloc(1 + strlen(val), GFP_KERNEL); + if (!tmp) + return -ENOMEM; + strcpy(tmp, val); + *option = tmp; + return 0; +} + +static int +parse_options(char *options, struct supermount_sb_info *sbi) +{ + char *this_char; + char *value; + int rc; + + if (!options) + return 0; + while ((this_char = options)) { + if (!strncmp(this_char, "--", 2)) + this_char += 2; + if (!*this_char) + break; + if (*this_char == ',') { + /* An empty option, or the option "--", + introduces options to be passed through to + the subfs */ + return copy_option(&sbi->s_data, ++this_char); + } + if ((options = strchr(this_char, ','))) + *options++ = 0; + + if ((value = strchr(this_char, '='))) + *value++ = 0; + + if (!strcmp(this_char, "fs")) { + rc = copy_option(&sbi->s_type, value); + if (rc) + return rc; + } else if (!strcmp(this_char, "dev")) { + rc = copy_option(&sbi->s_devname, value); + if (rc) + return rc; + } else { + printk(KERN_DEBUG "Unrecognized mount option %s", + this_char); + return -EINVAL; + } + } + return 0; +} + +static struct inode * +supermount_root_inode(struct super_block *s) +{ + struct inode *inode = supermount_create_inode(s); + + if (!inode) + return NULL; + + inode->i_version = 0; + inode->i_op = &supermount_root_iops; + inode->i_fop = &supermount_dir_operations; + + return inode; +} + +/* read_super: the main mount() entry point into the VFS layer. */ +struct super_block * +supermount_read_super(struct super_block *s, void *data, int silent) +{ + struct inode *root_inode; + struct dentry *root; + struct supermount_sb_info *sbi = create_sbi(s); + + if (!sbi) + goto fail_no_memory; + + if (parse_options((char *) data, sbi)) + goto fail_free_sbi; + + if (!sbi->s_devname) { + printk(KERN_ERR "%s: no dev= option", __FUNCTION__); + goto fail_free_sbi; + } + if (sbi->s_data) { + sbi->s_data_copy = kmalloc(1 + strlen(sbi->s_data), GFP_KERNEL); + if (!sbi->s_data_copy) { + printk(KERN_ERR "%s: not enough memory", __FUNCTION__); + goto fail_free_sbi; + } + strcpy(sbi->s_data_copy, sbi->s_data); + } else + sbi->s_data_copy = NULL; + + s->s_blocksize = 1024; + s->s_blocksize_bits = 10; + s->s_magic = SUPERMOUNT_SUPER_MAGIC; + s->s_op = &supermount_sops; + s->u.generic_sbp = sbi; + + root_inode = supermount_root_inode(s); + if (!root_inode) + goto fail_free_sbi; + + root = d_alloc_root(root_inode); + if (!root) + goto fail_free_root_inode; + + s->s_root = root; + s->s_root->d_op = &supermount_dir_dops; + dget(root); + + return s; + +fail_free_root_inode: + iput(root_inode); +fail_free_sbi: + s->s_dev = 0; + free_sbi(sbi); +fail_no_memory: + return NULL; +} + +/* + * called with lock_super held + */ + +static void +supermount_write_super(struct super_block *sb) +{ + struct supermount_sb_info *sbi = supermount_sbi(sb); + + if (subfs_is_online(sbi) && subfs_is_rw(sbi)) { + struct super_block *subsb = supermount_subfs_sb(sbi); + if (subsb && subsb->s_op && subsb->s_op->write_super) + subsb->s_op->write_super(subsb); + mark_subfs_clean(sb); + } +} + +static int +supermount_statfs(struct super_block *sb, struct statfs *buf) +{ + int rc = 0; + struct supermount_sb_info *sbi = supermount_sbi(sb); + + if (subfs_is_online(sbi)) { + struct super_block *subsb = supermount_subfs_sb(sbi); + if (subsb->s_op && subsb->s_op->statfs) + rc = subsb->s_op->statfs(subsb, buf); + } + buf->f_type = SUPERMOUNT_SUPER_MAGIC; + return rc; +} + +static struct super_operations supermount_sops = { + .write_inode = supermount_write_inode, + .clear_inode = supermount_clear_inode, + .put_super = supermount_put_super, + .write_super = supermount_write_super, + .statfs = supermount_statfs, +}; diff -Naurp linux-2.4.20-ck4/fs/supermount/translation.c linux-2.4.20-ck5/fs/supermount/translation.c --- linux-2.4.20-ck4/fs/supermount/translation.c 1970-01-01 10:00:00.000000000 +1000 +++ linux-2.4.20-ck5/fs/supermount/translation.c 2003-04-08 10:52:51.000000000 +1000 @@ -0,0 +1,155 @@ +/* + * linux/fs/supermount/translate.c + * + * Original version: + * Copyright (C) 1995, 1997 + * Stephen Tweedie (sct@dcs.ed.ac.uk) + * + * from + * + * linux/fs/minix/dir.c + * Copyright (C) 1991, 1992 Linus Torvalds + * + * and + * + * linux/fs/ext2/dir.c + * Copyright (C) 1992, 1993, 1994, 1995 Remy Card + * + * Rewriten for kernel 2.2 & 2.4. (C) 1999, 2000 Alexis Mikhailov + * (alexis@abc.cap.ru) + * Rewriten for kernel 2.4. (C) 2001 MandrakeSoft Inc. + * Juan Quintela (quintela@mandrakesoft.com) + * + */ + +#include +#include +#include +#include + +#include "supermount_i.h" + +static int +unwalk_dentry(struct dentry *dentry, char *name) +{ + char *tmp; + char *path; + int number = 0; + + path = name + PATH_MAX; + *path = 0; + + spin_lock(&dcache_lock); + for (;;) { + if (dentry->d_parent == dentry) + break; + path -= dentry->d_name.len + 1; + if (path < name) + goto err_out; + memcpy(path + 1, dentry->d_name.name, dentry->d_name.len); + *path = 0; + number++; + dentry = dentry->d_parent; + } + spin_unlock(&dcache_lock); + if (number) + /* we need to removing leading '\0' */ + path++; + + tmp = name; + while (path <= name + PATH_MAX) + *tmp++ = *path++; + + return number; + +err_out: + spin_unlock(&dcache_lock); + return -EINVAL; +} + +static struct dentry * +walk_path(struct dentry *root, char *path, int number) +{ + struct dentry *base = root; + + while (number-- && !IS_ERR(base)) { + struct dentry *subdent; + int len = strlen(path); + subdent = lookup_one_len(path, base, len); + path += len + 1; + dput(base); + base = subdent; + } + return base; +} + +static struct dentry * +__get_subfs_dentry(struct dentry *dentry) +{ + struct supermount_sb_info *sbi; + struct dentry *subdent = ERR_PTR(-EINVAL); + struct dentry *root; + int number; + char *name; + struct vfsmount *mnt; + + name = __getname(); + if (!name) + return ERR_PTR(-ENOMEM); + + sbi = supermount_sbi(dentry->d_sb); + number = unwalk_dentry(dentry, name); + if (number < 0) { + subdent = ERR_PTR(number); + goto out; + } + mnt = mntget(sbi->s_undermount); + if (!mnt) + goto out; + + root = dget(mnt->mnt_sb->s_root); + subdent = walk_path(root, name, number); + mntput(mnt); +out: + putname(name); + return subdent; +} + +struct dentry * +get_subfs_dentry_by_inode(struct inode *inode) +{ + struct dentry *dentry; + struct supermount_sb_info *sbi; + + BUG_ON(!inode); + BUG_ON(!is_inode_supermounted(inode)); + + if (is_inode_obsolete(inode)) + return ERR_PTR(-ESTALE); + + sbi = supermount_sbi(inode->i_sb); + if (!subfs_is_mounted(sbi)) + return ERR_PTR(-EINVAL); + + dentry = dget(supermount_i(inode)->i_dentry); + if (!dentry) + BUG(); + + return dentry; +} + +struct dentry * +get_subfs_dentry(struct dentry *dentry) +{ + struct dentry *subdent; + + BUG_ON(!dentry); + BUG_ON(dentry->d_inode && !is_dentry_supermounted(dentry)); + + if (dentry->d_inode && !is_inode_obsolete(dentry->d_inode)) + subdent = get_subfs_dentry_by_inode(dentry->d_inode); + else + subdent = __get_subfs_dentry(dentry); + + return subdent; +} diff -Naurp linux-2.4.20-ck4/fs/udf/super.c linux-2.4.20-ck5/fs/udf/super.c --- linux-2.4.20-ck4/fs/udf/super.c 2002-08-03 13:14:59.000000000 +1000 +++ linux-2.4.20-ck5/fs/udf/super.c 2003-04-08 10:52:51.000000000 +1000 @@ -268,6 +268,11 @@ udf_parse_options(char *options, struct uopt->nls_map = load_nls(val); uopt->flags |= (1 << UDF_FLAG_NLS_MAP); } +#if defined(CONFIG_SUPERMOUNT) || defined(CONFIG_SUPERMOUNT_MODULE) + /* Silently ignore NLS option */ + else if (!strcmp (opt, "codepage")) + /* Don't do anything ;-) */ ; +#endif #endif else if (!strcmp(opt, "utf8") && !val) uopt->flags |= (1 << UDF_FLAG_UTF8); diff -Naurp linux-2.4.20-ck4/include/linux/cdrom.h linux-2.4.20-ck5/include/linux/cdrom.h --- linux-2.4.20-ck4/include/linux/cdrom.h 2001-11-23 06:47:04.000000000 +1100 +++ linux-2.4.20-ck5/include/linux/cdrom.h 2003-04-08 10:52:51.000000000 +1000 @@ -781,6 +781,7 @@ extern int cdrom_open(struct inode *, st extern int cdrom_release(struct inode *, struct file *); extern int cdrom_ioctl(struct inode *, struct file *, unsigned, unsigned long); extern int cdrom_media_changed(kdev_t); +extern int cdrom_mediactl (kdev_t dev, int op, int optarg); extern int register_cdrom(struct cdrom_device_info *cdi); extern int unregister_cdrom(struct cdrom_device_info *cdi); diff -Naurp linux-2.4.20-ck4/include/linux/fs.h linux-2.4.20-ck5/include/linux/fs.h --- linux/include/linux/fs.h.orig Mon Apr 14 09:03:15 2003 +++ linux/include/linux/fs.h Sat Apr 19 11:11:59 2003 @@ -112,6 +112,7 @@ #define MS_REC 16384 #define MS_VERBOSE 32768 #define MS_POSIXACL 65536 /* VFS does not apply the umask */ +#define MS_SUPERMOUNTED (1<<17) #define MS_ACTIVE (1<<30) #define MS_NOUSER (1<<31) @@ -296,6 +297,7 @@ #include +#include #include #include #include @@ -519,6 +521,7 @@ struct usbdev_inode_info usbdev_i; struct jffs2_inode_info jffs2_i; struct squashfs_inode_info squashfs_i; + struct supermount_inode_info supermount_i; void *generic_ip; } u; }; @@ -811,6 +814,9 @@ * non-directories) are allowed, but not unconnected diretories. */ struct semaphore s_nfsd_free_path_sem; +#if defined(CONFIG_SUPERMOUNT) || defined(CONFIG_SUPERMOUNT_MODULE) + int s_media_changed; +#endif }; /* @@ -825,6 +831,23 @@ extern int vfs_unlink(struct inode *, struct dentry *); extern int vfs_rename(struct inode *, struct dentry *, struct inode *, struct dentry *); +struct kstat { + ino_t ino; + atomic_t count; + dev_t dev; + umode_t mode; + nlink_t nlink; + uid_t uid; + gid_t gid; + dev_t rdev; + loff_t size; + time_t atime; + time_t mtime; + time_t ctime; + unsigned long blksize; + unsigned long blocks; +}; + /* * File types */ @@ -853,6 +876,16 @@ int (*check_media_change) (kdev_t); int (*revalidate) (kdev_t); struct module *owner; + int (*mediactl) (kdev_t dev, int op, int optarg); +}; + +/* + * These are the "op" operation codes for mediactl() media control + * calls for device special files + */ +enum { + MEDIA_LOCK = 1234, /* Lock the drive door */ + MEDIA_UNLOCK /* Unlock the drive door */ }; /* @@ -898,7 +931,7 @@ int (*permission) (struct inode *, int); int (*revalidate) (struct dentry *); int (*setattr) (struct dentry *, struct iattr *); - int (*getattr) (struct dentry *, struct iattr *); + int (*getattr) (struct vfsmount *, struct dentry *, struct kstat *); int (*setxattr) (struct dentry *, const char *, const void *, size_t, int); ssize_t (*getxattr) (struct dentry *, const char *, void *, size_t); ssize_t (*listxattr) (struct dentry *, char *, size_t); @@ -1030,6 +1063,8 @@ extern struct vfsmount *kern_mount(struct file_system_type *); extern int may_umount(struct vfsmount *); extern long do_mount(char *, char *, char *, unsigned long, void *); +extern struct vfsmount *do_kern_mount(const char *type, int flags, char *name, void *data); +extern int do_remount_sb(struct super_block *sb, int flags, void * data); #define kern_umount mntput @@ -1254,8 +1289,11 @@ extern void set_buffer_flushtime(struct buffer_head *); extern void balance_dirty(void); extern int check_disk_change(kdev_t); +extern int query_disk_change(kdev_t dev); +extern void invalidate_media(kdev_t dev); extern int invalidate_inodes(struct super_block *); extern int invalidate_device(kdev_t, int); +extern int destroy_device(kdev_t); extern void invalidate_inode_pages(struct inode *); extern void invalidate_inode_pages2(struct address_space *); extern void invalidate_inode_buffers(struct inode *); @@ -1516,12 +1554,14 @@ extern void do_generic_file_read(struct file *, loff_t *, read_descriptor_t *, read_actor_t); extern loff_t no_llseek(struct file *file, loff_t offset, int origin); extern loff_t generic_file_llseek(struct file *file, loff_t offset, int origin); +extern loff_t vfs_llseek(struct file *file, loff_t loffset, int origin); extern ssize_t generic_read_dir(struct file *, char *, size_t, loff_t *); extern int generic_file_open(struct inode * inode, struct file * filp); extern struct file_operations generic_ro_fops; extern int vfs_readlink(struct dentry *, char *, int, const char *); +extern int vfs_readlink_real(struct dentry *, char *, int); extern int vfs_follow_link(struct nameidata *, const char *); extern int page_readlink(struct dentry *, char *, int); extern int page_follow_link(struct dentry *, struct nameidata *); @@ -1535,6 +1575,13 @@ extern int dcache_readdir(struct file *, void *, filldir_t); extern struct file_operations dcache_dir_ops; +int getattr_full(struct vfsmount *mnt, struct dentry *dentry, struct kstat *stat); +int getattr_minix(struct vfsmount *mnt, struct dentry *dentry, struct kstat *stat); + +extern int vfs_stat(char *, struct kstat *); +extern int vfs_lstat(char *, struct kstat *); +extern int vfs_fstat(int, struct kstat *); + extern struct file_system_type *get_fs_type(const char *name); extern struct super_block *get_super(kdev_t); extern void drop_super(struct super_block *sb); diff -Naurp linux-2.4.20-ck4/include/linux/ide.h linux-2.4.20-ck5/include/linux/ide.h --- linux-2.4.20-ck4/include/linux/ide.h 2002-11-30 17:02:23.000000000 +1100 +++ linux-2.4.20-ck5/include/linux/ide.h 2003-04-08 10:52:51.000000000 +1000 @@ -726,6 +726,7 @@ typedef struct ide_driver_s { ide_special_proc *special; ide_proc_entry_t *proc; ide_reinit_proc *reinit; + int (*mediactl)(ide_drive_t *, int, int); ata_prebuilder_proc *ata_prebuilder; atapi_prebuilder_proc *atapi_prebuilder; } ide_driver_t; @@ -1062,6 +1063,7 @@ int idedisk_init (void); #ifdef CONFIG_BLK_DEV_IDECD int ide_cdrom_reinit (ide_drive_t *drive); int ide_cdrom_init (void); +int ide_cdrom_mediactl (ide_drive_t *, int, int); #endif /* CONFIG_BLK_DEV_IDECD */ #ifdef CONFIG_BLK_DEV_IDETAPE int idetape_reinit (ide_drive_t *drive); diff -Naurp linux-2.4.20-ck4/include/linux/supermount_fs_i.h linux-2.4.20-ck5/include/linux/supermount_fs_i.h --- linux-2.4.20-ck4/include/linux/supermount_fs_i.h 1970-01-01 10:00:00.000000000 +1000 +++ linux-2.4.20-ck5/include/linux/supermount_fs_i.h 2003-04-08 10:52:51.000000000 +1000 @@ -0,0 +1,10 @@ +#ifndef _LINUX_SUPERMOUNT_FS_I_H +#define _LINUX_SUPERMOUNT_FS_I_H + +struct supermount_inode_info { + int i_sb_version; + struct dentry * i_dentry; + struct list_head i_fs; +}; + +#endif diff -Naurp linux-2.4.20-ck4/kernel/ksyms.c linux-2.4.20-ck5/kernel/ksyms.c --- linux-2.4.20-ck4/kernel/ksyms.c 2003-04-08 10:50:37.000000000 +1000 +++ linux-2.4.20-ck5/kernel/ksyms.c 2003-04-08 10:52:51.000000000 +1000 @@ -177,10 +177,13 @@ EXPORT_SYMBOL(filp_close); EXPORT_SYMBOL(put_filp); EXPORT_SYMBOL(files_lock); EXPORT_SYMBOL(check_disk_change); +EXPORT_SYMBOL(query_disk_change); +EXPORT_SYMBOL(invalidate_media); EXPORT_SYMBOL(__invalidate_buffers); EXPORT_SYMBOL(invalidate_bdev); EXPORT_SYMBOL(invalidate_inodes); EXPORT_SYMBOL(invalidate_device); +EXPORT_SYMBOL(destroy_device); EXPORT_SYMBOL(invalidate_inode_pages); EXPORT_SYMBOL(truncate_inode_pages); EXPORT_SYMBOL(fsync_dev); @@ -254,9 +257,12 @@ EXPORT_SYMBOL(vfs_rmdir); EXPORT_SYMBOL(vfs_unlink); EXPORT_SYMBOL(vfs_rename); EXPORT_SYMBOL(vfs_statfs); +EXPORT_SYMBOL(getattr_full); +EXPORT_SYMBOL(getattr_minix); EXPORT_SYMBOL(generic_read_dir); EXPORT_SYMBOL(generic_file_llseek); EXPORT_SYMBOL(no_llseek); +EXPORT_SYMBOL(vfs_llseek); EXPORT_SYMBOL(__pollwait); EXPORT_SYMBOL(poll_freewait); EXPORT_SYMBOL(ROOT_DEV); @@ -267,6 +273,7 @@ EXPORT_SYMBOL(grab_cache_page_nowait); EXPORT_SYMBOL(read_cache_page); EXPORT_SYMBOL(set_page_dirty); EXPORT_SYMBOL(vfs_readlink); +EXPORT_SYMBOL(vfs_readlink_real); EXPORT_SYMBOL(vfs_follow_link); EXPORT_SYMBOL(page_readlink); EXPORT_SYMBOL(page_follow_link); @@ -300,6 +307,7 @@ EXPORT_SYMBOL(register_chrdev); EXPORT_SYMBOL(unregister_chrdev); EXPORT_SYMBOL(register_blkdev); EXPORT_SYMBOL(unregister_blkdev); +EXPORT_SYMBOL(get_blkfops); EXPORT_SYMBOL(tty_register_driver); EXPORT_SYMBOL(tty_unregister_driver); EXPORT_SYMBOL(tty_std_termios); @@ -341,6 +349,8 @@ EXPORT_SYMBOL(unregister_filesystem); EXPORT_SYMBOL(kern_mount); EXPORT_SYMBOL(__mntput); EXPORT_SYMBOL(may_umount); +EXPORT_SYMBOL(do_kern_mount); +EXPORT_SYMBOL(do_remount_sb); /* executable format registration */ EXPORT_SYMBOL(register_binfmt);