diff options
author | Ralf Baechle <ralf@linux-mips.org> | 2000-02-24 00:12:35 +0000 |
---|---|---|
committer | Ralf Baechle <ralf@linux-mips.org> | 2000-02-24 00:12:35 +0000 |
commit | 482368b1a8e45430672c58c9a42e7d2004367126 (patch) | |
tree | ce2a1a567d4d62dee7c2e71a46a99cf72cf1d606 /fs | |
parent | e4d0251c6f56ab2e191afb70f80f382793e23f74 (diff) |
Merge with 2.3.47. Guys, this is buggy as shit. You've been warned.
Diffstat (limited to 'fs')
67 files changed, 5633 insertions, 388 deletions
diff --git a/fs/Config.in b/fs/Config.in index 934a7a70a..eb4a5f78a 100644 --- a/fs/Config.in +++ b/fs/Config.in @@ -1,32 +1,32 @@ # -# Filesystem configuration +# File system configuration # mainmenu_option next_comment -comment 'Filesystems' +comment 'File systems' bool 'Quota support' CONFIG_QUOTA tristate 'Kernel automounter support' CONFIG_AUTOFS_FS tristate 'Kernel automounter version 4 support (also supports v3)' CONFIG_AUTOFS4_FS -dep_tristate 'ADFS filesystem support (read only) (EXPERIMENTAL)' CONFIG_ADFS_FS $CONFIG_EXPERIMENTAL +dep_tristate 'ADFS file system support (read only) (EXPERIMENTAL)' CONFIG_ADFS_FS $CONFIG_EXPERIMENTAL -tristate 'Amiga FFS filesystem support' CONFIG_AFFS_FS +tristate 'Amiga FFS file system support' CONFIG_AFFS_FS -dep_tristate 'Apple Macintosh filesystem support (EXPERIMENTAL)' CONFIG_HFS_FS $CONFIG_EXPERIMENTAL +dep_tristate 'Apple Macintosh file system support (EXPERIMENTAL)' CONFIG_HFS_FS $CONFIG_EXPERIMENTAL -dep_tristate 'BFS filesystem support (EXPERIMENTAL)' CONFIG_BFS_FS $CONFIG_EXPERIMENTAL +dep_tristate 'BFS file system support (EXPERIMENTAL)' CONFIG_BFS_FS $CONFIG_EXPERIMENTAL -# msdos filesystems +# msdos file systems tristate 'DOS FAT fs support' CONFIG_FAT_FS dep_tristate ' MSDOS fs support' CONFIG_MSDOS_FS $CONFIG_FAT_FS -dep_tristate ' UMSDOS: Unix-like filesystem on top of standard MSDOS fs' CONFIG_UMSDOS_FS $CONFIG_MSDOS_FS +dep_tristate ' UMSDOS: Unix-like file system on top of standard MSDOS fs' CONFIG_UMSDOS_FS $CONFIG_MSDOS_FS dep_tristate ' VFAT (Windows-95) fs support' CONFIG_VFAT_FS $CONFIG_FAT_FS -dep_tristate 'EFS filesystem support (read only) (EXPERIMENTAL)' CONFIG_EFS_FS $CONFIG_EXPERIMENTAL +dep_tristate 'EFS file system support (read only) (EXPERIMENTAL)' CONFIG_EFS_FS $CONFIG_EXPERIMENTAL -tristate 'Compressed ROM filessytem support' CONFIG_CRAMFS +tristate 'Compressed ROM file sytem support' CONFIG_CRAMFS -tristate 'ISO 9660 CDROM filesystem support' CONFIG_ISO9660_FS +tristate 'ISO 9660 CDROM file system support' CONFIG_ISO9660_FS if [ "$CONFIG_ISO9660_FS" != "n" ]; then bool ' Microsoft Joliet CDROM extensions' CONFIG_JOLIET else @@ -36,35 +36,47 @@ fi tristate 'Minix fs support' CONFIG_MINIX_FS -tristate 'NTFS filesystem support (read only)' CONFIG_NTFS_FS -dep_bool ' NTFS write support (DANGEROUS)' CONFIG_NTFS_RW $CONFIG_NTFS_FS $CONFIG_EXPERIMENTAL +tristate 'NTFS file system support (read only)' CONFIG_NTFS_FS +if [ "$CONFIG_NTFS_FS" != "n" -a "$CONFIG_EXPERIMENTAL" = "y" ]; then + bool ' NTFS write support (DANGEROUS)' CONFIG_NTFS_RW +fi + +tristate 'OS/2 HPFS file system support' CONFIG_HPFS_FS -tristate 'OS/2 HPFS filesystem support' CONFIG_HPFS_FS +bool '/proc file system support' CONFIG_PROC_FS -bool '/proc filesystem support' CONFIG_PROC_FS +dep_bool '/dev file system support (EXPERIMENTAL)' CONFIG_DEVFS_FS $CONFIG_EXPERIMENTAL +dep_bool ' Debug devfs' CONFIG_DEVFS_DEBUG $CONFIG_DEVFS_FS # It compiles as a module for testing only. It should not be used # as a module in general. If we make this "tristate", a bunch of people # who don't know what they are doing turn it on and complain when it # breaks. -dep_bool '/dev/pts filesystem for Unix98 PTYs' CONFIG_DEVPTS_FS $CONFIG_UNIX98_PTYS +dep_bool '/dev/pts file system for Unix98 PTYs' CONFIG_DEVPTS_FS $CONFIG_UNIX98_PTYS -dep_tristate 'QNX4 filesystem support (read only) (EXPERIMENTAL)' CONFIG_QNX4FS_FS $CONFIG_EXPERIMENTAL -dep_bool ' QNX4FS write support (DANGEROUS)' CONFIG_QNX4FS_RW $CONFIG_QNX4FS_FS +dep_tristate 'QNX4 file system support (read only) (EXPERIMENTAL)' CONFIG_QNX4FS_FS $CONFIG_EXPERIMENTAL +if [ "$CONFIG_QNX4FS_FS" != "n" -a "$CONFIG_EXPERIMENTAL" = "y" ]; then + bool ' QNX4FS write support (DANGEROUS)' CONFIG_QNX4FS_RW +fi -tristate 'ROM filesystem support' CONFIG_ROMFS_FS +tristate 'ROM file system support' CONFIG_ROMFS_FS tristate 'Second extended fs support' CONFIG_EXT2_FS -tristate 'System V and Coherent filesystem support' CONFIG_SYSV_FS -dep_bool ' SYSV filesystem write support (DANGEROUS)' CONFIG_SYSV_FS_WRITE $CONFIG_SYSV_FS $CONFIG_EXPERIMENTAL - -tristate 'UDF filesystem support (read only)' CONFIG_UDF_FS -dep_bool ' UDF write support (DANGEROUS)' CONFIG_UDF_RW $CONFIG_UDF_FS $CONFIG_EXPERIMENTAL +tristate 'System V and Coherent file system support (read only)' CONFIG_SYSV_FS +if [ "$CONFIG_SYSV_FS" != "n" -a "$CONFIG_EXPERIMENTAL" = "y" ]; then + bool ' SYSV file system write support (DANGEROUS)' CONFIG_SYSV_FS_WRITE +fi -tristate 'UFS filesystem support (read only)' CONFIG_UFS_FS -dep_bool ' UFS filesystem write support (DANGEROUS)' CONFIG_UFS_FS_WRITE $CONFIG_UFS_FS $CONFIG_EXPERIMENTAL +tristate 'UDF file system support (read only)' CONFIG_UDF_FS +if [ "$CONFIG_UDF_FS" != "n" -a "$CONFIG_EXPERIMENTAL" = "y" ]; then + bool ' UDF write support (DANGEROUS)' CONFIG_UDF_RW +fi +tristate 'UFS file system support (read only)' CONFIG_UFS_FS +if [ "$CONFIG_UFS_FS" != "n" -a "$CONFIG_EXPERIMENTAL" = "y" ]; then + bool ' UFS file system write support (DANGEROUS)' CONFIG_UFS_FS_WRITE +fi if [ "$CONFIG_NET" = "y" ]; then @@ -72,13 +84,15 @@ mainmenu_option next_comment comment 'Network File Systems' if [ "$CONFIG_INET" = "y" ]; then - tristate 'Coda filesystem support (advanced network fs)' CONFIG_CODA_FS + tristate 'Coda file system support (advanced network fs)' CONFIG_CODA_FS - tristate 'NFS filesystem support' CONFIG_NFS_FS + tristate 'NFS file system support' CONFIG_NFS_FS dep_bool ' Root file system on NFS' CONFIG_ROOT_NFS $CONFIG_NFS_FS $CONFIG_IP_PNP tristate 'NFS server support' CONFIG_NFSD - dep_bool ' Provide NFSv3 server support (EXPERIMENTAL)' CONFIG_NFSD_V3 $CONFIG_NFSD $CONFIG_EXPERIMENTAL + if [ "$CONFIG_NFSD" != "n" ]; then + bool ' Provide NFSv3 server support (EXPERIMENTAL)' CONFIG_NFSD_V3 + fi if [ "$CONFIG_NFS_FS" = "y" -o "$CONFIG_NFSD" = "y" ]; then define_tristate CONFIG_SUNRPC y @@ -92,10 +106,13 @@ if [ "$CONFIG_INET" = "y" ]; then define_tristate CONFIG_LOCKD n fi fi - tristate 'SMB filesystem support (to mount WfW shares etc.)' CONFIG_SMB_FS + if [ "$CONFIG_NFSD_V3" = "y" ]; then + define_bool CONFIG_LOCKD_V4 y + fi + tristate 'SMB file system support (to mount Windows shares etc.)' CONFIG_SMB_FS fi if [ "$CONFIG_IPX" != "n" -o "$CONFIG_INET" != "n" ]; then - tristate 'NCP filesystem support (to mount NetWare volumes)' CONFIG_NCP_FS + tristate 'NCP file system support (to mount NetWare volumes)' CONFIG_NCP_FS if [ "$CONFIG_NCP_FS" != "n" ]; then source fs/ncpfs/Config.in fi diff --git a/fs/Makefile b/fs/Makefile index e79d69c1b..74fc394ed 100644 --- a/fs/Makefile +++ b/fs/Makefile @@ -18,8 +18,8 @@ O_OBJS = open.o read_write.o devices.o file_table.o buffer.o \ MOD_LIST_NAME := FS_MODULES ALL_SUB_DIRS = coda minix ext2 fat msdos vfat proc isofs nfs umsdos ntfs \ hpfs sysv smbfs ncpfs ufs efs affs romfs autofs hfs lockd \ - nfsd nls devpts adfs partitions qnx4 udf bfs cramfs openpromfs \ - autofs4 + nfsd nls devpts devfs adfs partitions qnx4 udf bfs cramfs \ + openpromfs autofs4 SUB_DIRS := partitions @@ -105,6 +105,10 @@ else endif endif +ifdef CONFIG_DEVFS_FS +SUB_DIRS += devfs +endif + ifeq ($(CONFIG_HFS_FS),y) SUB_DIRS += hfs else diff --git a/fs/adfs/file.c b/fs/adfs/file.c index a2f105b06..0d5f4346e 100644 --- a/fs/adfs/file.c +++ b/fs/adfs/file.c @@ -44,4 +44,8 @@ static struct file_operations adfs_file_operations = { struct inode_operations adfs_file_inode_operations = { &adfs_file_operations, /* default file operations */ +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0) + readpage: generic_readpage, + bmap: adfs_bmap, +#endif }; diff --git a/fs/adfs/inode.c b/fs/adfs/inode.c index 30398d62f..7a759c050 100644 --- a/fs/adfs/inode.c +++ b/fs/adfs/inode.c @@ -48,30 +48,36 @@ abort_negative: abort_toobig: return 0; } + static int adfs_writepage(struct dentry *dentry, struct page *page) { - return block_write_full_page(page,adfs_get_block); + return block_write_full_page(page, adfs_get_block); } + static int adfs_readpage(struct dentry *dentry, struct page *page) { - return block_read_full_page(page,adfs_get_block); + return block_read_full_page(page, adfs_get_block); } -static int adfs_prepare_write(struct page *page, unsigned from, unsigned to) + +static int adfs_prepare_write(struct page *page, unsigned int from, unsigned int to) { - return cont_prepare_write(page,from,to,adfs_get_block, - &((struct inode*)page->mapping->host)->u.adfs_i.mmu_private); + return cont_prepare_write(page, from, to, adfs_get_block, + &((struct inode *)page->mapping->host)->u.adfs_i.mmu_private); } + static int _adfs_bmap(struct address_space *mapping, long block) { - return generic_block_bmap(mapping,block,adfs_get_block); + return generic_block_bmap(mapping, block, adfs_get_block); } -struct address_space_operations adfs_aops = { - readpage: adfs_readpage, - writepage: adfs_writepage, - prepare_write: adfs_prepare_write, - commit_write: generic_commit_write, - bmap: _adfs_bmap + +static struct address_space_operations adfs_aops = { + readpage: adfs_readpage, + writepage: adfs_writepage, + prepare_write: adfs_prepare_write, + commit_write: generic_commit_write, + bmap: _adfs_bmap }; + #else int adfs_bmap(struct inode *inode, int block) { @@ -280,7 +286,7 @@ adfs_iget(struct super_block *sb, struct object_info *obj) if (S_ISDIR(inode->i_mode)) inode->i_op = &adfs_dir_inode_operations; else if (S_ISREG(inode->i_mode)) { - inode->i_op = &adfs_file_inode_operations; + inode->i_op = &adfs_file_inode_operations; inode->i_mapping->a_ops = &adfs_aops; inode->u.adfs_i.mmu_private = inode->i_size; } @@ -329,7 +335,7 @@ adfs_notify_change(struct dentry *dentry, struct iattr *attr) goto out; if (ia_valid & ATTR_SIZE) - inode->i_size = attr->ia_size; + vmtruncate(inode, attr->ia_size); if (ia_valid & ATTR_MTIME) { inode->i_mtime = attr->ia_mtime; adfs_unix2adfs_time(inode, attr->ia_mtime); diff --git a/fs/adfs/map.c b/fs/adfs/map.c index 0fde11f5d..0c6507411 100644 --- a/fs/adfs/map.c +++ b/fs/adfs/map.c @@ -44,7 +44,6 @@ lookup_zone(const struct adfs_discmap *dm, const unsigned int idlen, /* * get fragment id */ - //asm("@ get fragment id start"); { unsigned long v2; unsigned int tmp; @@ -60,14 +59,12 @@ lookup_zone(const struct adfs_discmap *dm, const unsigned int idlen, frag &= idmask; } - //asm("@ get fragment id end"); mapptr = start + idlen; /* * find end of fragment */ - //asm("@ find end of fragment start"); { unsigned long v2; @@ -79,7 +76,6 @@ lookup_zone(const struct adfs_discmap *dm, const unsigned int idlen, mapptr += 1 + ffz(~v2); } - //asm("@ find end of fragment end"); if (frag == frag_id) goto found; @@ -122,7 +118,6 @@ scan_free_map(struct adfs_sb_info *asb, struct adfs_discmap *dm) /* * get fragment id */ - //asm("@ get fragment id start"); { unsigned long v2; unsigned int tmp; @@ -138,7 +133,6 @@ scan_free_map(struct adfs_sb_info *asb, struct adfs_discmap *dm) frag &= idmask; } - //asm("@ get fragment id end"); /* * If the freelink is null, then no free fragments @@ -153,7 +147,6 @@ scan_free_map(struct adfs_sb_info *asb, struct adfs_discmap *dm) /* * get fragment id */ - //asm("@ get fragment id start"); { unsigned long v2; unsigned int tmp; @@ -169,14 +162,12 @@ scan_free_map(struct adfs_sb_info *asb, struct adfs_discmap *dm) frag &= idmask; } - //asm("@ get fragment id end"); mapptr = start + idlen; /* * find end of fragment */ - //asm("@ find end of fragment start"); { unsigned long v2; @@ -188,7 +179,6 @@ scan_free_map(struct adfs_sb_info *asb, struct adfs_discmap *dm) mapptr += 1 + ffz(~v2); } - //asm("@ find end of fragment end"); total += mapptr - start; } while (frag >= idlen + 1); diff --git a/fs/affs/inode.c b/fs/affs/inode.c index ed4993e36..e0d411861 100644 --- a/fs/affs/inode.c +++ b/fs/affs/inode.c @@ -249,9 +249,8 @@ affs_notify_change(struct dentry *dentry, struct iattr *attr) if (attr->ia_valid & ATTR_MODE) inode->u.affs_i.i_protect = mode_to_prot(attr->ia_mode); - inode_setattr(inode, attr); - mark_inode_dirty(inode); error = 0; + inode_setattr(inode, attr); out: return error; } @@ -63,7 +63,7 @@ void inode_setattr(struct inode * inode, struct iattr * attr) if (ia_valid & ATTR_GID) inode->i_gid = attr->ia_gid; if (ia_valid & ATTR_SIZE) - inode->i_size = attr->ia_size; + vmtruncate(inode, attr->ia_size); if (ia_valid & ATTR_ATIME) inode->i_atime = attr->ia_atime; if (ia_valid & ATTR_MTIME) diff --git a/fs/bfs/file.c b/fs/bfs/file.c index c3d5a8905..fe90391f1 100644 --- a/fs/bfs/file.c +++ b/fs/bfs/file.c @@ -19,9 +19,9 @@ #endif static struct file_operations bfs_file_operations = { - read: generic_file_read, - write: generic_file_write, - mmap: generic_file_mmap, + read: generic_file_read, + write: generic_file_write, + mmap: generic_file_mmap, }; static int bfs_move_block(unsigned long from, unsigned long to, kdev_t dev) @@ -77,7 +77,8 @@ static int bfs_get_block(struct inode * inode, long block, /* if the file is not empty and the requested block is within the range of blocks allocated for this file, we can grant it */ if (inode->i_size && phys <= inode->iu_eblock) { - dprintf("c=%d, b=%08lx, phys=%08lx (interim block granted)\n", create, block, phys); + dprintf("c=%d, b=%08lx, phys=%08lx (interim block granted)\n", + create, block, phys); bh_result->b_dev = inode->i_dev; bh_result->b_blocknr = phys; bh_result->b_state |= (1UL << BH_Mapped); @@ -90,7 +91,8 @@ static int bfs_get_block(struct inode * inode, long block, /* if the last data block for this file is the last allocated block, we can extend the file trivially, without moving it anywhere */ if (inode->iu_eblock == s->su_lf_eblk) { - dprintf("c=%d, b=%08lx, phys=%08lx (simple extension)\n", create, block, phys); + dprintf("c=%d, b=%08lx, phys=%08lx (simple extension)\n", + create, block, phys); bh_result->b_dev = inode->i_dev; bh_result->b_blocknr = phys; bh_result->b_state |= (1UL << BH_Mapped); @@ -104,9 +106,10 @@ static int bfs_get_block(struct inode * inode, long block, /* Ok, we have to move this entire file to the next free block */ next_free_block = s->su_lf_eblk + 1; if (inode->iu_sblock) { /* if data starts on block 0 then there is no data */ - err = bfs_move_blocks(inode->i_dev, inode->iu_sblock, inode->iu_eblock, next_free_block); + err = bfs_move_blocks(inode->i_dev, inode->iu_sblock, + inode->iu_eblock, next_free_block); if (err) { - dprintf("failed to move ino=%08lx -> possible fs corruption\n", inode->i_ino); + dprintf("failed to move ino=%08lx -> fs corruption\n", inode->i_ino); goto out; } } else @@ -126,26 +129,30 @@ out: static int bfs_writepage(struct dentry *dentry, struct page *page) { - return block_write_full_page(page,bfs_get_block); + return block_write_full_page(page, bfs_get_block); } + static int bfs_readpage(struct dentry *dentry, struct page *page) { - return block_read_full_page(page,bfs_get_block); + return block_read_full_page(page, bfs_get_block); } + static int bfs_prepare_write(struct page *page, unsigned from, unsigned to) { - return block_prepare_write(page,from,to,bfs_get_block); + return block_prepare_write(page, from, to, bfs_get_block); } + static int bfs_bmap(struct address_space *mapping, long block) { - return generic_block_bmap(mapping,block,bfs_get_block); + return generic_block_bmap(mapping, block, bfs_get_block); } + struct address_space_operations bfs_aops = { - readpage: bfs_readpage, - writepage: bfs_writepage, - prepare_write: bfs_prepare_write, - commit_write: generic_commit_write, - bmap: bfs_bmap + readpage: bfs_readpage, + writepage: bfs_writepage, + prepare_write: bfs_prepare_write, + commit_write: generic_commit_write, + bmap: bfs_bmap }; struct inode_operations bfs_file_inops = { diff --git a/fs/binfmt_elf.c b/fs/binfmt_elf.c index f4937eb91..743375bc9 100644 --- a/fs/binfmt_elf.c +++ b/fs/binfmt_elf.c @@ -269,14 +269,8 @@ static unsigned long load_elf_interp(struct elfhdr * interp_elf_ex, if (eppnt->p_flags & PF_W) elf_prot |= PROT_WRITE; if (eppnt->p_flags & PF_X) elf_prot |= PROT_EXEC; vaddr = eppnt->p_vaddr; - if (interp_elf_ex->e_type == ET_EXEC || load_addr_set) { + if (interp_elf_ex->e_type == ET_EXEC || load_addr_set) elf_type |= MAP_FIXED; -#ifdef __sparc__ - } else { - load_addr = get_unmapped_area(0, eppnt->p_filesz + - ELF_PAGEOFFSET(vaddr)); -#endif - } map_addr = do_mmap(file, load_addr + ELF_PAGESTART(vaddr), diff --git a/fs/binfmt_script.c b/fs/binfmt_script.c index 3dad604e3..be8695e2b 100644 --- a/fs/binfmt_script.c +++ b/fs/binfmt_script.c @@ -14,7 +14,7 @@ static int do_load_script(struct linux_binprm *bprm,struct pt_regs *regs) { - char *cp, *i_name, *i_name_start, *i_arg; + char *cp, *i_name, *i_arg; struct dentry * dentry; char interp[128]; int retval; @@ -44,17 +44,15 @@ static int do_load_script(struct linux_binprm *bprm,struct pt_regs *regs) for (cp = bprm->buf+2; (*cp == ' ') || (*cp == '\t'); cp++); if (*cp == '\0') return -ENOEXEC; /* No interpreter name found */ - i_name_start = i_name = cp; + i_name = cp; i_arg = 0; - for ( ; *cp && (*cp != ' ') && (*cp != '\t'); cp++) { - if (*cp == '/') - i_name = cp+1; - } + for ( ; *cp && (*cp != ' ') && (*cp != '\t'); cp++) + /* nothing */ ; while ((*cp == ' ') || (*cp == '\t')) *cp++ = '\0'; if (*cp) i_arg = cp; - strcpy (interp, i_name_start); + strcpy (interp, i_name); /* * OK, we've parsed out the interpreter name and * (optional) argument. diff --git a/fs/block_dev.c b/fs/block_dev.c index b451332ed..b5d665c29 100644 --- a/fs/block_dev.c +++ b/fs/block_dev.c @@ -10,6 +10,7 @@ #include <linux/fcntl.h> #include <linux/malloc.h> #include <linux/kmod.h> +#include <linux/devfs_fs_kernel.h> #include <asm/uaccess.h> @@ -454,7 +455,7 @@ int get_blkdev_list(char * p) Return the function table of a device. Load the driver if needed. */ -static const struct block_device_operations * get_blkfops(unsigned int major) +const struct block_device_operations * get_blkfops(unsigned int major) { const struct block_device_operations *ret = NULL; @@ -518,11 +519,20 @@ int unregister_blkdev(unsigned int major, const char * name) int check_disk_change(kdev_t dev) { int i; - const struct block_device_operations * bdops; + const struct block_device_operations * bdops = NULL; struct super_block * sb; i = MAJOR(dev); - if (i >= MAX_BLKDEV || (bdops = blkdevs[i].bdops) == NULL) + if (i < MAX_BLKDEV) + bdops = blkdevs[i].bdops; + if (bdops == NULL) { + devfs_handle_t de; + + de = devfs_find_handle (NULL, NULL, 0, i, MINOR (dev), + DEVFS_SPECIAL_BLK, 0); + if (de) bdops = devfs_get_ops (de); + } + if (bdops == NULL) return 0; if (bdops->check_media_change == NULL) return 0; diff --git a/fs/buffer.c b/fs/buffer.c index 9113c07cc..7da594637 100644 --- a/fs/buffer.c +++ b/fs/buffer.c @@ -1692,8 +1692,12 @@ int block_prepare_write(struct page *page, unsigned from, unsigned to, int generic_commit_write(struct file *file, struct page *page, unsigned from, unsigned to) { - __block_commit_write((struct inode*)page->mapping->host,page,from,to); + struct inode *inode = (struct inode*)page->mapping->host; + loff_t pos = ((loff_t)page->index << PAGE_CACHE_SHIFT) + to; + __block_commit_write(inode,page,from,to); kunmap(page); + if (pos > inode->i_size) + inode->i_size = pos; return 0; } @@ -2010,7 +2014,6 @@ int block_symlink(struct inode *inode, const char *symname, int len) kaddr = (char*)page_address(page); memcpy(kaddr, symname, len-1); mapping->a_ops->commit_write(NULL, page, 0, len-1); - inode->i_size = len-1; /* * Notice that we are _not_ going to block here - end of page is * unmapped, so this will only try to map the rest of page, see @@ -2026,7 +2029,6 @@ int block_symlink(struct inode *inode, const char *symname, int len) mark_inode_dirty(inode); return 0; fail_map: - inode->i_size = len-1; UnlockPage(page); page_cache_release(page); fail: diff --git a/fs/coda/psdev.c b/fs/coda/psdev.c index 07ba22335..c00a6945c 100644 --- a/fs/coda/psdev.c +++ b/fs/coda/psdev.c @@ -29,6 +29,7 @@ #include <linux/delay.h> #include <linux/skbuff.h> #include <linux/proc_fs.h> +#include <linux/devfs_fs_kernel.h> #include <linux/vmalloc.h> #include <linux/fs.h> #include <linux/poll.h> @@ -59,7 +60,7 @@ unsigned long coda_timeout = 30; /* .. secs, then signals will dequeue */ struct coda_sb_info coda_super_info; struct venus_comm coda_upc_comm; - + /* * Device operations */ @@ -358,13 +359,21 @@ int __init init_coda(void) return status; } +static devfs_handle_t devfs_handle = NULL; + int init_coda_psdev(void) { - if(register_chrdev(CODA_PSDEV_MAJOR,"coda_psdev", &coda_psdev_fops)) { + if(devfs_register_chrdev(CODA_PSDEV_MAJOR,"coda_psdev", + &coda_psdev_fops)) { printk(KERN_ERR "coda_psdev: unable to get major %d\n", CODA_PSDEV_MAJOR); return -EIO; } + devfs_handle = devfs_mk_dir (NULL, "coda", 4, NULL); + devfs_register_series (devfs_handle, "%u", MAX_CODADEVS, DEVFS_FL_NONE, + CODA_PSDEV_MAJOR, 0, + S_IFCHR | S_IRUSR | S_IWUSR, 0, 0, + &coda_psdev_fops, NULL); memset(&coda_upc_comm, 0, sizeof(coda_upc_comm)); memset(&coda_super_info, 0, sizeof(coda_super_info)); init_waitqueue_head(&coda_upc_comm.vc_waitq); @@ -407,7 +416,8 @@ void cleanup_module(void) if ( (err = unregister_filesystem(&coda_fs_type)) != 0 ) { printk("coda: failed to unregister filesystem\n"); } - unregister_chrdev(CODA_PSDEV_MAJOR,"coda_psdev"); + devfs_unregister (devfs_handle); + devfs_unregister_chrdev(CODA_PSDEV_MAJOR,"coda_psdev"); coda_sysctl_clean(); } diff --git a/fs/devfs/.cvsignore b/fs/devfs/.cvsignore new file mode 100644 index 000000000..857dd22e9 --- /dev/null +++ b/fs/devfs/.cvsignore @@ -0,0 +1,2 @@ +.depend +.*.flags diff --git a/fs/devfs/Makefile b/fs/devfs/Makefile new file mode 100644 index 000000000..2b301b37a --- /dev/null +++ b/fs/devfs/Makefile @@ -0,0 +1,39 @@ +# +# Makefile for the linux devfs-filesystem routines. +# +# Note! Dependencies are done automagically by 'make dep', which also +# removes any old dependencies. DON'T put your own dependencies here +# unless it's something special (ie not a .c file). +# +# Note 2! The CFLAGS definitions are now in the main makefile... + +O_TARGET := devfs.o +OX_OBJS := base.o util.o + +# Special case to support building documentation +ifndef TOPDIR +TOPDIR = ../.. +endif + +include $(TOPDIR)/Rules.make + + +# Rule to build documentation +doc: base.c util.c + @echo '$$PACKAGE devfs' > devfs.doc + @echo '$$NAME Linux Kernel' >> devfs.doc + @echo '$$SUMMARY devfs (Device FileSystem) functions' >> devfs.doc + @echo '$$SYNOPSIS "#include <linux/devfs_fs.h>"' >> devfs.doc + @echo '$$END' >> devfs.doc + c2doc base.c util.c >> devfs.doc + karma_doc2man -section 9 devfs.doc . + rm devfs.doc + gzip --best *.9 + mv *.9.gz /usr/man/man9 + + +# Rule for test compiling +test: + gcc -o /tmp/base.o -D__KERNEL__ -I../../include -Wall \ + -Wstrict-prototypes -O2 -fomit-frame-pointer -pipe \ + -fno-strength-reduce -DCPU=686 -DEXPORT_SYMTAB -c base.c diff --git a/fs/devfs/base.c b/fs/devfs/base.c new file mode 100644 index 000000000..ed9e04d69 --- /dev/null +++ b/fs/devfs/base.c @@ -0,0 +1,3467 @@ +/* devfs (Device FileSystem) driver. + + Copyright (C) 1998-2000 Richard Gooch + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free + Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + Richard Gooch may be reached by email at rgooch@atnf.csiro.au + The postal address is: + Richard Gooch, c/o ATNF, P. O. Box 76, Epping, N.S.W., 2121, Australia. + + ChangeLog + + 19980110 Richard Gooch <rgooch@atnf.csiro.au> + Original version. + v0.1 + 19980111 Richard Gooch <rgooch@atnf.csiro.au> + Created per-fs inode table rather than using inode->u.generic_ip + v0.2 + 19980111 Richard Gooch <rgooch@atnf.csiro.au> + Created .epoch inode which has a ctime of 0. + Fixed loss of named pipes when dentries lost. + Fixed loss of inode data when devfs_register() follows mknod(). + v0.3 + 19980111 Richard Gooch <rgooch@atnf.csiro.au> + Fix for when compiling with CONFIG_KERNELD. + 19980112 Richard Gooch <rgooch@atnf.csiro.au> + Fix for readdir() which sometimes didn't show entries. + Added <<tolerant>> option to <devfs_register>. + v0.4 + 19980113 Richard Gooch <rgooch@atnf.csiro.au> + Created <devfs_fill_file> function. + v0.5 + 19980115 Richard Gooch <rgooch@atnf.csiro.au> + Added subdirectory support. Major restructuring. + 19980116 Richard Gooch <rgooch@atnf.csiro.au> + Fixed <find_by_dev> to not search major=0,minor=0. + Added symlink support. + v0.6 + 19980120 Richard Gooch <rgooch@atnf.csiro.au> + Created <devfs_mk_dir> function and support directory unregister + 19980120 Richard Gooch <rgooch@atnf.csiro.au> + Auto-ownership uses real uid/gid rather than effective uid/gid. + v0.7 + 19980121 Richard Gooch <rgooch@atnf.csiro.au> + Supported creation of sockets. + v0.8 + 19980122 Richard Gooch <rgooch@atnf.csiro.au> + Added DEVFS_FL_HIDE_UNREG flag. + Interface change to <devfs_mk_symlink>. + Created <devfs_symlink> to support symlink(2). + v0.9 + 19980123 Richard Gooch <rgooch@atnf.csiro.au> + Added check to <devfs_fill_file> to check inode is in devfs. + Added optional traversal of symlinks. + v0.10 + 19980124 Richard Gooch <rgooch@atnf.csiro.au> + Created <devfs_get_flags> and <devfs_set_flags>. + v0.11 + 19980125 C. Scott Ananian <cananian@alumni.princeton.edu> + Created <devfs_find_handle>. + 19980125 Richard Gooch <rgooch@atnf.csiro.au> + Allow removal of symlinks. + v0.12 + 19980125 Richard Gooch <rgooch@atnf.csiro.au> + Created <devfs_set_symlink_destination>. + 19980126 Richard Gooch <rgooch@atnf.csiro.au> + Moved DEVFS_SUPER_MAGIC into header file. + Added DEVFS_FL_HIDE flag. + Created <devfs_get_maj_min>. + Created <devfs_get_handle_from_inode>. + Fixed minor bug in <find_by_dev>. + 19980127 Richard Gooch <rgooch@atnf.csiro.au> + Changed interface to <find_by_dev>, <find_entry>, + <devfs_unregister>, <devfs_fill_file> and <devfs_find_handle>. + Fixed inode times when symlink created with symlink(2). + v0.13 + 19980129 C. Scott Ananian <cananian@alumni.princeton.edu> + Exported <devfs_set_symlink_destination>, <devfs_get_maj_min> + and <devfs_get_handle_from_inode>. + 19980129 Richard Gooch <rgooch@atnf.csiro.au> + Created <devfs_unlink> to support unlink(2). + v0.14 + 19980129 Richard Gooch <rgooch@atnf.csiro.au> + Fixed kerneld support for entries in devfs subdirectories. + 19980130 Richard Gooch <rgooch@atnf.csiro.au> + Bugfixes in <call_kerneld>. + v0.15 + 19980207 Richard Gooch <rgooch@atnf.csiro.au> + Call kerneld when looking up unregistered entries. + v0.16 + 19980326 Richard Gooch <rgooch@atnf.csiro.au> + Modified interface to <devfs_find_handle> for symlink traversal. + v0.17 + 19980331 Richard Gooch <rgooch@atnf.csiro.au> + Fixed persistence bug with device numbers for manually created + device files. + Fixed problem with recreating symlinks with different content. + v0.18 + 19980401 Richard Gooch <rgooch@atnf.csiro.au> + Changed to CONFIG_KMOD. + Hide entries which are manually unlinked. + Always invalidate devfs dentry cache when registering entries. + Created <devfs_rmdir> to support rmdir(2). + Ensure directories created by <devfs_mk_dir> are visible. + v0.19 + 19980402 Richard Gooch <rgooch@atnf.csiro.au> + Invalidate devfs dentry cache when making directories. + Invalidate devfs dentry cache when removing entries. + Fixed persistence bug with fifos. + v0.20 + 19980421 Richard Gooch <rgooch@atnf.csiro.au> + Print process command when debugging kerneld/kmod. + Added debugging for register/unregister/change operations. + 19980422 Richard Gooch <rgooch@atnf.csiro.au> + Added "devfs=" boot options. + v0.21 + 19980426 Richard Gooch <rgooch@atnf.csiro.au> + No longer lock/unlock superblock in <devfs_put_super>. + Drop negative dentries when they are released. + Manage dcache more efficiently. + v0.22 + 19980427 Richard Gooch <rgooch@atnf.csiro.au> + Added DEVFS_FL_AUTO_DEVNUM flag. + v0.23 + 19980430 Richard Gooch <rgooch@atnf.csiro.au> + No longer set unnecessary methods. + v0.24 + 19980504 Richard Gooch <rgooch@atnf.csiro.au> + Added PID display to <call_kerneld> debugging message. + Added "after" debugging message to <call_kerneld>. + 19980519 Richard Gooch <rgooch@atnf.csiro.au> + Added "diread" and "diwrite" boot options. + 19980520 Richard Gooch <rgooch@atnf.csiro.au> + Fixed persistence problem with permissions. + v0.25 + 19980602 Richard Gooch <rgooch@atnf.csiro.au> + Support legacy device nodes. + Fixed bug where recreated inodes were hidden. + v0.26 + 19980602 Richard Gooch <rgooch@atnf.csiro.au> + Improved debugging in <get_vfs_inode>. + 19980607 Richard Gooch <rgooch@atnf.csiro.au> + No longer free old dentries in <devfs_mk_dir>. + Free all dentries for a given entry when deleting inodes. + v0.27 + 19980627 Richard Gooch <rgooch@atnf.csiro.au> + Limit auto-device numbering to majors 128 to 239. + v0.28 + 19980629 Richard Gooch <rgooch@atnf.csiro.au> + Fixed inode times persistence problem. + v0.29 + 19980704 Richard Gooch <rgooch@atnf.csiro.au> + Fixed spelling in <devfs_readlink> debug. + Fixed bug in <devfs_setup> parsing "dilookup". + v0.30 + 19980705 Richard Gooch <rgooch@atnf.csiro.au> + Fixed devfs inode leak when manually recreating inodes. + Fixed permission persistence problem when recreating inodes. + v0.31 + 19980727 Richard Gooch <rgooch@atnf.csiro.au> + Removed harmless "unused variable" compiler warning. + Fixed modes for manually recreated device nodes. + v0.32 + 19980728 Richard Gooch <rgooch@atnf.csiro.au> + Added NULL devfs inode warning in <devfs_read_inode>. + Force all inode nlink values to 1. + v0.33 + 19980730 Richard Gooch <rgooch@atnf.csiro.au> + Added "dimknod" boot option. + Set inode nlink to 0 when freeing dentries. + Fixed modes for manually recreated symlinks. + v0.34 + 19980802 Richard Gooch <rgooch@atnf.csiro.au> + Fixed bugs in recreated directories and symlinks. + v0.35 + 19980806 Richard Gooch <rgooch@atnf.csiro.au> + Fixed bugs in recreated device nodes. + 19980807 Richard Gooch <rgooch@atnf.csiro.au> + Fixed bug in currently unused <devfs_get_handle_from_inode>. + Defined new <devfs_handle_t> type. + Improved debugging when getting entries. + Fixed bug where directories could be emptied. + v0.36 + 19980809 Richard Gooch <rgooch@atnf.csiro.au> + Replaced dummy .epoch inode with .devfsd character device. + 19980810 Richard Gooch <rgooch@atnf.csiro.au> + Implemented devfsd protocol revision 0. + v0.37 + 19980819 Richard Gooch <rgooch@atnf.csiro.au> + Added soothing message to warning in <devfs_d_iput>. + v0.38 + 19980829 Richard Gooch <rgooch@atnf.csiro.au> + Use GCC extensions for structure initialisations. + Implemented async open notification. + Incremented devfsd protocol revision to 1. + v0.39 + 19980908 Richard Gooch <rgooch@atnf.csiro.au> + Moved async open notification to end of <devfs_open>. + v0.40 + 19980910 Richard Gooch <rgooch@atnf.csiro.au> + Prepended "/dev/" to module load request. + Renamed <call_kerneld> to <call_kmod>. + v0.41 + 19980910 Richard Gooch <rgooch@atnf.csiro.au> + Fixed typo "AYSNC" -> "ASYNC". + v0.42 + 19980910 Richard Gooch <rgooch@atnf.csiro.au> + Added open flag for files. + v0.43 + 19980927 Richard Gooch <rgooch@atnf.csiro.au> + Set i_blocks=0 and i_blksize=1024 in <devfs_read_inode>. + v0.44 + 19981005 Richard Gooch <rgooch@atnf.csiro.au> + Added test for empty <<name>> in <devfs_find_handle>. + Renamed <generate_path> to <devfs_generate_path> and published. + v0.45 + 19981006 Richard Gooch <rgooch@atnf.csiro.au> + Created <devfs_get_fops>. + v0.46 + 19981007 Richard Gooch <rgooch@atnf.csiro.au> + Limit auto-device numbering to majors 144 to 239. + v0.47 + 19981010 Richard Gooch <rgooch@atnf.csiro.au> + Updated <devfs_follow_link> for VFS change in 2.1.125. + v0.48 + 19981022 Richard Gooch <rgooch@atnf.csiro.au> + Created DEVFS_ FL_COMPAT flag. + v0.49 + 19981023 Richard Gooch <rgooch@atnf.csiro.au> + Created "nocompat" boot option. + v0.50 + 19981025 Richard Gooch <rgooch@atnf.csiro.au> + Replaced "mount" boot option with "nomount". + v0.51 + 19981110 Richard Gooch <rgooch@atnf.csiro.au> + Created "only" boot option. + v0.52 + 19981112 Richard Gooch <rgooch@atnf.csiro.au> + Added DEVFS_FL_REMOVABLE flag. + v0.53 + 19981114 Richard Gooch <rgooch@atnf.csiro.au> + Only call <scan_dir_for_removable> on first call to + <devfs_readdir>. + v0.54 + 19981205 Richard Gooch <rgooch@atnf.csiro.au> + Updated <devfs_rmdir> for VFS change in 2.1.131. + v0.55 + 19981218 Richard Gooch <rgooch@atnf.csiro.au> + Created <devfs_mk_compat>. + 19981220 Richard Gooch <rgooch@atnf.csiro.au> + Check for partitions on removable media in <devfs_lookup>. + v0.56 + 19990118 Richard Gooch <rgooch@atnf.csiro.au> + Added support for registering regular files. + Created <devfs_set_file_size>. + Update devfs inodes from entries if not changed through FS. + v0.57 + 19990124 Richard Gooch <rgooch@atnf.csiro.au> + Fixed <devfs_fill_file> to only initialise temporary inodes. + Trap for NULL fops in <devfs_register>. + Return -ENODEV in <devfs_fill_file> for non-driver inodes. + v0.58 + 19990126 Richard Gooch <rgooch@atnf.csiro.au> + Switched from PATH_MAX to DEVFS_PATHLEN. + v0.59 + 19990127 Richard Gooch <rgooch@atnf.csiro.au> + Created "nottycompat" boot option. + v0.60 + 19990318 Richard Gooch <rgooch@atnf.csiro.au> + Fixed <devfsd_read> to not overrun event buffer. + v0.61 + 19990329 Richard Gooch <rgooch@atnf.csiro.au> + Created <devfs_auto_unregister>. + v0.62 + 19990330 Richard Gooch <rgooch@atnf.csiro.au> + Don't return unregistred entries in <devfs_find_handle>. + Panic in <devfs_unregister> if entry unregistered. + 19990401 Richard Gooch <rgooch@atnf.csiro.au> + Don't panic in <devfs_auto_unregister> for duplicates. + v0.63 + 19990402 Richard Gooch <rgooch@atnf.csiro.au> + Don't unregister already unregistered entries in <unregister>. + v0.64 + 19990510 Richard Gooch <rgooch@atnf.csiro.au> + Disable warning messages when unable to read partition table for + removable media. + v0.65 + 19990512 Richard Gooch <rgooch@atnf.csiro.au> + Updated <devfs_lookup> for VFS change in 2.3.1-pre1. + Created "oops-on-panic" boot option. + Improved debugging in <devfs_register> and <devfs_unregister>. + v0.66 + 19990519 Richard Gooch <rgooch@atnf.csiro.au> + Added documentation for some functions. + 19990525 Richard Gooch <rgooch@atnf.csiro.au> + Removed "oops-on-panic" boot option: now always Oops. + v0.67 + 19990531 Richard Gooch <rgooch@atnf.csiro.au> + Improved debugging in <devfs_register>. + v0.68 + 19990604 Richard Gooch <rgooch@atnf.csiro.au> + Added "diunlink" and "nokmod" boot options. + Removed superfluous warning message in <devfs_d_iput>. + v0.69 + 19990611 Richard Gooch <rgooch@atnf.csiro.au> + Took account of change to <d_alloc_root>. + v0.70 + 19990614 Richard Gooch <rgooch@atnf.csiro.au> + Created separate event queue for each mounted devfs. + Removed <devfs_invalidate_dcache>. + Created new ioctl()s. + Incremented devfsd protocol revision to 3. + Fixed bug when re-creating directories: contents were lost. + Block access to inodes until devfsd updates permissions. + 19990615 Richard Gooch <rgooch@atnf.csiro.au> + Support 2.2.x kernels. + v0.71 + 19990623 Richard Gooch <rgooch@atnf.csiro.au> + Switched to sending process uid/gid to devfsd. + Renamed <call_kmod> to <try_modload>. + Added DEVFSD_NOTIFY_LOOKUP event. + 19990624 Richard Gooch <rgooch@atnf.csiro.au> + Added DEVFSD_NOTIFY_CHANGE event. + Incremented devfsd protocol revision to 4. + v0.72 + 19990713 Richard Gooch <rgooch@atnf.csiro.au> + Return EISDIR rather than EINVAL for read(2) on directories. + v0.73 + 19990809 Richard Gooch <rgooch@atnf.csiro.au> + Changed <devfs_setup> to new __init scheme. + v0.74 + 19990901 Richard Gooch <rgooch@atnf.csiro.au> + Changed remaining function declarations to new __init scheme. + v0.75 + 19991013 Richard Gooch <rgooch@atnf.csiro.au> + Created <devfs_get_info>, <devfs_set_info>, + <devfs_get_first_child> and <devfs_get_next_sibling>. + Added <<dir>> parameter to <devfs_register>, <devfs_mk_compat>, + <devfs_mk_dir> and <devfs_find_handle>. + Work sponsored by SGI. + v0.76 + 19991017 Richard Gooch <rgooch@atnf.csiro.au> + Allow multiple unregistrations. + Work sponsored by SGI. + v0.77 + 19991026 Richard Gooch <rgooch@atnf.csiro.au> + Added major and minor number to devfsd protocol. + Incremented devfsd protocol revision to 5. + Work sponsored by SGI. + v0.78 + 19991030 Richard Gooch <rgooch@atnf.csiro.au> + Support info pointer for all devfs entry types. + Added <<info>> parameter to <devfs_mk_dir> and + <devfs_mk_symlink>. + Work sponsored by SGI. + v0.79 + 19991031 Richard Gooch <rgooch@atnf.csiro.au> + Support "../" when searching devfs namespace. + Work sponsored by SGI. + v0.80 + 19991101 Richard Gooch <rgooch@atnf.csiro.au> + Created <devfs_get_unregister_slave>. + Work sponsored by SGI. + v0.81 + 19991103 Richard Gooch <rgooch@atnf.csiro.au> + Exported <devfs_get_parent>. + Work sponsored by SGI. + v0.82 + 19991104 Richard Gooch <rgooch@atnf.csiro.au> + Removed unused <devfs_set_symlink_destination>. + 19991105 Richard Gooch <rgooch@atnf.csiro.au> + Do not hide entries from devfsd or children. + Removed DEVFS_ FL_TTY_COMPAT flag. + Removed "nottycompat" boot option. + Removed <devfs_mk_compat>. + Work sponsored by SGI. + v0.83 + 19991107 Richard Gooch <rgooch@atnf.csiro.au> + Added DEVFS_ FL_WAIT flag. + Work sponsored by SGI. + v0.84 + 19991107 Richard Gooch <rgooch@atnf.csiro.au> + Support new "disc" naming scheme in <get_removable_partition>. + Allow NULL fops in <devfs_register>. + Work sponsored by SGI. + v0.85 + 19991110 Richard Gooch <rgooch@atnf.csiro.au> + Fall back to major table if NULL fops given to <devfs_register>. + Work sponsored by SGI. + v0.86 + 19991204 Richard Gooch <rgooch@atnf.csiro.au> + Support fifos when unregistering. + Work sponsored by SGI. + v0.87 + 19991209 Richard Gooch <rgooch@atnf.csiro.au> + Removed obsolete DEVFS_ FL_COMPAT and DEVFS_ FL_TOLERANT flags. + Work sponsored by SGI. + v0.88 + 19991214 Richard Gooch <rgooch@atnf.csiro.au> + Removed kmod support. + Work sponsored by SGI. + v0.89 + 19991216 Richard Gooch <rgooch@atnf.csiro.au> + Improved debugging in <get_vfs_inode>. + Ensure dentries created by devfsd will be cleaned up. + Work sponsored by SGI. + v0.90 + 19991223 Richard Gooch <rgooch@atnf.csiro.au> + Created <devfs_get_name>. + Work sponsored by SGI. + v0.91 + 20000203 Richard Gooch <rgooch@atnf.csiro.au> + Ported to kernel 2.3.42. + Removed <devfs_fill_file>. + Work sponsored by SGI. + v0.92 +*/ +#include <linux/types.h> +#include <linux/errno.h> +#include <linux/sched.h> +#include <linux/tty.h> +#include <linux/timer.h> +#include <linux/config.h> +#include <linux/kernel.h> +#include <linux/wait.h> +#include <linux/string.h> +#include <linux/malloc.h> +#include <linux/ioport.h> +#include <linux/delay.h> +#include <linux/ctype.h> +#include <linux/mm.h> +#include <linux/module.h> +#include <linux/init.h> +#include <linux/locks.h> +#include <linux/kdev_t.h> +#include <linux/devfs_fs.h> +#include <linux/devfs_fs_kernel.h> +#include <linux/smp_lock.h> +#include <linux/smp.h> +#include <linux/version.h> + +#include <asm/uaccess.h> +#include <asm/io.h> +#include <asm/processor.h> +#include <asm/system.h> +#include <asm/pgtable.h> +#include <asm/segment.h> +#include <asm/bitops.h> +#include <asm/atomic.h> + +#define DEVFS_VERSION "0.92 (20000203)" + +#ifndef DEVFS_NAME +# define DEVFS_NAME "devfs" +#endif + +/* Compatibility for 2.2.x kernel series */ +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,3,1)) +# define init_waitqueue_head(p) init_waitqueue(p) +# define DECLARE_WAITQUEUE(wait, p) struct wait_queue wait = {p, NULL} +typedef struct wait_queue *wait_queue_head_t; +#endif +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,3,6)) +# define D_ALLOC_ROOT(inode) d_alloc_root (inode, NULL) +#else +# define D_ALLOC_ROOT(inode) d_alloc_root (inode) +#endif +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,3,13)) +# define SETUP_STATIC +# define __setup(a,b) +#else +# define SETUP_STATIC static +#endif + +#define INODE_TABLE_INC 250 +#define FIRST_INODE 1 + +#define STRING_LENGTH 256 + +#define MIN_DEVNUM 36864 /* Use major numbers 144 */ +#define MAX_DEVNUM 61439 /* through 239, inclusive */ + +#ifndef TRUE +# define TRUE 1 +# define FALSE 0 +#endif + +#define IS_HIDDEN(de) (( ((de)->hide && !is_devfsd_or_child(fs_info)) || (!(de)->registered&& !(de)->show_unreg))) + +#define DEBUG_NONE 0x00000 +#define DEBUG_MODULE_LOAD 0x00001 +#define DEBUG_REGISTER 0x00002 +#define DEBUG_UNREGISTER 0x00004 +#define DEBUG_SET_FLAGS 0x00008 +#define DEBUG_S_PUT 0x00010 +#define DEBUG_I_LOOKUP 0x00020 +#define DEBUG_I_CREATE 0x00040 +#define DEBUG_I_READ 0x00080 +#define DEBUG_I_WRITE 0x00100 +#define DEBUG_I_UNLINK 0x00200 +#define DEBUG_I_RLINK 0x00400 +#define DEBUG_I_FLINK 0x00800 +#define DEBUG_I_MKNOD 0x01000 +#define DEBUG_F_READDIR 0x02000 +#define DEBUG_D_DELETE 0x04000 +#define DEBUG_D_RELEASE 0x08000 +#define DEBUG_D_IPUT 0x10000 +#define DEBUG_ALL (DEBUG_MODULE_LOAD | DEBUG_REGISTER | \ + DEBUG_SET_FLAGS | DEBUG_I_LOOKUP | \ + DEBUG_I_UNLINK | DEBUG_I_MKNOD | \ + DEBUG_D_RELEASE | DEBUG_D_IPUT) +#define DEBUG_DISABLED DEBUG_NONE + +#define OPTION_NONE 0x00 +#define OPTION_SHOW 0x01 +#define OPTION_NOMOUNT 0x02 +#define OPTION_ONLY 0x04 + +#define OOPS(format, args...) {printk (format, ## args); \ + printk ("Forcing Oops\n"); \ + *(int *) 0 = 0;} + +struct directory_type +{ + struct devfs_entry *first; + struct devfs_entry *last; + unsigned int num_removable; +}; + +struct file_type +{ + unsigned long size; +}; + +struct device_type +{ + unsigned short major; + unsigned short minor; +}; + +struct fcb_type /* File, char, block type */ +{ + uid_t default_uid; + gid_t default_gid; + void *ops; + union + { + struct file_type file; + struct device_type device; + } + u; + unsigned char auto_owner:1; + unsigned char aopen_notify:1; + unsigned char removable:1; /* Belongs in device_type, but save space */ + unsigned char open:1; /* Not entirely correct */ +}; + +struct symlink_type +{ + unsigned int length; /* Not including the NULL-termimator */ + char *linkname; /* This is NULL-terminated */ +}; + +struct fifo_type +{ + uid_t uid; + gid_t gid; +}; + +struct devfs_entry +{ + void *info; + union + { + struct directory_type dir; + struct fcb_type fcb; + struct symlink_type symlink; + struct fifo_type fifo; + } + u; + struct devfs_entry *prev; /* Previous entry in the parent directory */ + struct devfs_entry *next; /* Next entry in the parent directory */ + struct devfs_entry *parent; /* The parent directory */ + struct devfs_entry *slave; /* Another entry to unregister */ + struct devfs_inode *first_inode; + struct devfs_inode *last_inode; + umode_t mode; + unsigned short namelen; /* I think 64k+ filenames are a way off... */ + unsigned char registered:1; + unsigned char show_unreg:1; + unsigned char hide:1; + char name[1]; /* This is just a dummy: the allocated array is + bigger. This is NULL-terminated */ +}; + +/* The root of the device tree */ +static struct devfs_entry *root_entry = NULL; + +struct devfs_inode /* This structure is for "persistent" inode storage */ +{ + time_t atime; + time_t mtime; + time_t ctime; + unsigned int ino; /* Inode number as seen in the VFS */ + struct devfs_entry *de; + struct fs_info *fs_info; + struct devfs_inode *prev; /* This pair are used to associate a list of */ + struct devfs_inode *next; /* inodes (one per FS) for a devfs entry */ + struct dentry *dentry; +#ifdef CONFIG_DEVFS_TUNNEL + struct dentry *covered; +#endif + umode_t mode; + uid_t uid; + gid_t gid; + nlink_t nlink; +}; + +struct devfsd_buf_entry +{ + void *data; + unsigned int type; + umode_t mode; + uid_t uid; + gid_t gid; +}; + +struct fs_info /* This structure is for each mounted devfs */ +{ + unsigned int num_inodes; /* Number of inodes created */ + unsigned int table_size; /* Size of the inode pointer table */ + struct devfs_inode **table; + struct super_block *sb; + volatile struct devfsd_buf_entry *devfsd_buffer; + volatile unsigned int devfsd_buf_in; + volatile unsigned int devfsd_buf_out; + volatile int devfsd_sleeping; + volatile int devfsd_buffer_in_use; + volatile struct task_struct *devfsd_task; + volatile struct file *devfsd_file; + volatile unsigned long devfsd_event_mask; + atomic_t devfsd_overrun_count; + wait_queue_head_t devfsd_wait_queue; + wait_queue_head_t revalidate_wait_queue; + struct fs_info *prev; + struct fs_info *next; + unsigned char require_explicit:1; +}; + +static struct fs_info *first_fs = NULL; +static struct fs_info *last_fs = NULL; +static unsigned int next_devnum_char = MIN_DEVNUM; +static unsigned int next_devnum_block = MIN_DEVNUM; +static const int devfsd_buf_size = PAGE_SIZE / sizeof(struct devfsd_buf_entry); +#ifdef CONFIG_DEVFS_DEBUG +# ifdef MODULE +unsigned int devfs_debug = DEBUG_NONE; +# else +static unsigned int devfs_debug_init __initdata = DEBUG_NONE; +static unsigned int devfs_debug = DEBUG_NONE; +# endif +#endif +static unsigned int boot_options = OPTION_NONE; + +/* Forward function declarations */ +static struct devfs_entry *search_for_entry (struct devfs_entry *dir, + const char *name, + unsigned int namelen, int mkdir, + int mkfile, int *is_new, + int traverse_symlink); +static ssize_t devfsd_read (struct file *file, char *buf, size_t len, + loff_t *ppos); +static int devfsd_ioctl (struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg); +static int devfsd_close (struct inode *inode, struct file *file); + + +/* Devfs daemon file operations */ +static struct file_operations devfsd_fops = +{ + read: devfsd_read, + ioctl: devfsd_ioctl, + release: devfsd_close, +}; + + +/* Support functions follow */ + +static struct devfs_entry *search_for_entry_in_dir (struct devfs_entry *parent, + const char *name, + unsigned int namelen, + int traverse_symlink) +/* [SUMMARY] Search for a devfs entry inside another devfs entry. + <parent> The parent devfs entry. + <name> The name of the entry. + <namelen> The number of characters in <<name>>. + <traverse_symlink> If TRUE then the entry is traversed if it is a symlink. + [RETURNS] A pointer to the entry on success, else NULL. +*/ +{ + struct devfs_entry *curr; + + if ( !S_ISDIR (parent->mode) ) + { + printk ("%s: entry is not a directory\n", DEVFS_NAME); + return NULL; + } + for (curr = parent->u.dir.first; curr != NULL; curr = curr->next) + { + if (curr->namelen != namelen) continue; + if (memcmp (curr->name, name, namelen) == 0) break; + /* Not found: try the next one */ + } + if (curr == NULL) return NULL; + if (!S_ISLNK (curr->mode) || !traverse_symlink) return curr; + /* Need to follow the link: this is a stack chomper */ + return search_for_entry (parent, + curr->u.symlink.linkname, curr->u.symlink.length, + FALSE, FALSE, NULL, TRUE); + return curr; +} /* End Function search_for_entry_in_dir */ + +static struct devfs_entry *create_entry (struct devfs_entry *parent, + const char *name,unsigned int namelen) +{ + struct devfs_entry *new; + + if ( name && (namelen < 1) ) namelen = strlen (name); + if ( ( new = kmalloc (sizeof *new + namelen, GFP_KERNEL) ) == NULL ) + return NULL; + memset (new, 0, sizeof *new + namelen); + new->parent = parent; + if (name) memcpy (new->name, name, namelen); + new->namelen = namelen; + if (parent == NULL) return new; + new->prev = parent->u.dir.last; + /* Insert into the parent directory's list of children */ + if (parent->u.dir.first == NULL) parent->u.dir.first = new; + else parent->u.dir.last->next = new; + parent->u.dir.last = new; + return new; +} /* End Function create_entry */ + +static struct devfs_entry *get_root_entry (void) +/* [SUMMARY] Get the root devfs entry. + [RETURNS] The root devfs entry on success, else NULL. +*/ +{ + struct devfs_entry *new; + + /* Always ensure the root is created */ + if (root_entry != NULL) return root_entry; + if ( ( root_entry = create_entry (NULL, NULL, 0) ) == NULL ) return NULL; + root_entry->registered = TRUE; + root_entry->mode = S_IFDIR; + /* And create the entry for ".devfsd" */ + if ( ( new = create_entry (root_entry, ".devfsd", 0) ) == NULL ) + return NULL; + new->registered = TRUE; + new->u.fcb.u.device.major = next_devnum_char >> 8; + new->u.fcb.u.device.minor = next_devnum_char & 0xff; + ++next_devnum_char; + new->mode = S_IFCHR | S_IRUSR | S_IWUSR; + new->u.fcb.default_uid = 0; + new->u.fcb.default_gid = 0; + new->u.fcb.ops = &devfsd_fops; + return root_entry; +} /* End Function get_root_entry */ + +static struct devfs_entry *search_for_entry (struct devfs_entry *dir, + const char *name, + unsigned int namelen, int mkdir, + int mkfile, int *is_new, + int traverse_symlink) +/* [SUMMARY] Search for an entry in the devfs tree. + <dir> The parent directory to search from. If this is NULL the root is used + <name> The name of the entry. + <namelen> The number of characters in <<name>>. + <mkdir> If TRUE intermediate directories are created as needed. + <mkfile> If TRUE the file entry is created if it doesn't exist. + <is_new> If the returned entry was newly made, TRUE is written here. If + this is NULL nothing is written here. + <traverse_symlink> If TRUE then symbolic links are traversed. + [NOTE] If the entry is created, then it will be in the unregistered state. + [RETURNS] A pointer to the entry on success, else NULL. +*/ +{ + int len; + const char *subname, *stop, *ptr; + struct devfs_entry *entry; + + if (is_new) *is_new = FALSE; + if (dir == NULL) dir = get_root_entry (); + if (dir == NULL) return NULL; + /* Extract one filename component */ + subname = name; + stop = name + namelen; + while (subname < stop) + { + /* Search for a possible '/' */ + for (ptr = subname; (ptr < stop) && (*ptr != '/'); ++ptr); + if (ptr >= stop) + { + /* Look for trailing component */ + len = stop - subname; + entry = search_for_entry_in_dir (dir, subname, len, + traverse_symlink); + if (entry != NULL) return entry; + if (!mkfile) return NULL; + entry = create_entry (dir, subname, len); + if (entry && is_new) *is_new = TRUE; + return entry; + } + /* Found '/': search for directory */ + if (strncmp (subname, "../", 3) == 0) + { + /* Going up */ + dir = dir->parent; + if (dir == NULL) return NULL; /* Cannot escape from devfs */ + subname += 3; + continue; + } + len = ptr - subname; + entry = search_for_entry_in_dir (dir, subname, len, traverse_symlink); + if (!entry && !mkdir) return NULL; + if (entry == NULL) + { + /* Make it */ + if ( ( entry = create_entry (dir, subname, len) ) == NULL ) + return NULL; + entry->mode = S_IFDIR | S_IRUGO | S_IXUGO | S_IWUSR; + if (is_new) *is_new = TRUE; + } + if ( !S_ISDIR (entry->mode) ) + { + printk ("%s: existing non-directory entry\n", DEVFS_NAME); + return NULL; + } + /* Ensure an unregistered entry is re-registered and visible */ + entry->registered = TRUE; + entry->hide = FALSE; + subname = ptr + 1; + dir = entry; + } + return NULL; +} /* End Function search_for_entry */ + +static struct devfs_entry *find_by_dev (struct devfs_entry *dir, + unsigned int major, unsigned int minor, + char type) +/* [SUMMARY] Find a devfs entry in a directory. + <major> The major number to search for. + <minor> The minor number to search for. + <type> The type of special file to search for. This may be either + DEVFS_SPECIAL_CHR or DEVFS_SPECIAL_BLK. + [RETURNS] The devfs_entry pointer on success, else NULL. +*/ +{ + struct devfs_entry *entry, *de; + + if (dir == NULL) return NULL; + if ( !S_ISDIR (dir->mode) ) + { + printk ("%s: find_by_dev(): not a directory\n", DEVFS_NAME); + return NULL; + } + /* First search files in this directory */ + for (entry = dir->u.dir.first; entry != NULL; entry = entry->next) + { + if ( !S_ISCHR (entry->mode) && !S_ISBLK (entry->mode) ) continue; + if ( S_ISCHR (entry->mode) && (type != DEVFS_SPECIAL_CHR) ) continue; + if ( S_ISBLK (entry->mode) && (type != DEVFS_SPECIAL_BLK) ) continue; + if ( (entry->u.fcb.u.device.major == major) && + (entry->u.fcb.u.device.minor == minor) ) return entry; + /* Not found: try the next one */ + } + /* Now recursively search the subdirectories: this is a stack chomper */ + for (entry = dir->u.dir.first; entry != NULL; entry = entry->next) + { + if ( !S_ISDIR (entry->mode) ) continue; + de = find_by_dev (entry, major, minor, type); + if (de) return de; + } + return NULL; +} /* End Function find_by_dev */ + +static struct devfs_entry *find_entry (devfs_handle_t dir, + const char *name, unsigned int namelen, + unsigned int major, unsigned int minor, + char type, int traverse_symlink) +/* [SUMMARY] Find a devfs entry. + <dir> The handle to the parent devfs directory entry. If this is NULL the + name is relative to the root of the devfs. + <name> The name of the entry. This is ignored if <<handle>> is not NULL. + <namelen> The number of characters in <<name>>, not including a NULL + terminator. If this is 0, then <<name>> must be NULL-terminated and the + length is computed internally. + <major> The major number. This is used if <<handle>> and <<name>> are NULL. + <minor> The minor number. This is used if <<handle>> and <<name>> are NULL. + [NOTE] If <<major>> and <<minor>> are both 0, searching by major and minor + numbers is disabled. + <type> The type of special file to search for. This may be either + DEVFS_SPECIAL_CHR or DEVFS_SPECIAL_BLK. + <traverse_symlink> If TRUE then symbolic links are traversed. + [RETURNS] The devfs_entry pointer on success, else NULL. +*/ +{ + struct devfs_entry *entry; + + if (name != NULL) + { + if (namelen < 1) namelen = strlen (name); + if (name[0] == '/') + { + /* Skip leading pathname component */ + if (namelen < 2) + { + printk ("%s: find_entry(%s): too short\n", DEVFS_NAME, name); + return NULL; + } + for (++name, --namelen; (*name != '/') && (namelen > 0); + ++name, --namelen); + if (namelen < 2) + { + printk ("%s: find_entry(%s): too short\n", DEVFS_NAME, name); + return NULL; + } + ++name; + --namelen; + } + entry = search_for_entry (dir, name, namelen, TRUE, FALSE, NULL, + traverse_symlink); + if (entry != NULL) return entry; + } + /* Have to search by major and minor: slow */ + if ( (major == 0) && (minor == 0) ) return NULL; + return find_by_dev (root_entry, major, minor, type); +} /* End Function find_entry */ + +static struct devfs_inode *get_devfs_inode_from_vfs_inode (struct inode *inode) +{ + struct fs_info *fs_info; + + if (inode == NULL) return NULL; + if (inode->i_ino < FIRST_INODE) return NULL; + fs_info = inode->i_sb->u.generic_sbp; + if (fs_info == NULL) return NULL; + if (inode->i_ino - FIRST_INODE >= fs_info->num_inodes) return NULL; + return fs_info->table[inode->i_ino - FIRST_INODE]; +} /* End Function get_devfs_inode_from_vfs_inode */ + +static void free_dentries (struct devfs_entry *de) +/* [SUMMARY] Free the dentries for a device entry and invalidate inodes. + <de> The entry. + [RETURNS] Nothing. +*/ +{ + struct devfs_inode *di; + struct dentry *dentry; + + for (di = de->first_inode; di != NULL; di = di->next) + { + dentry = di->dentry; + if (dentry != NULL) + { + dget (dentry); + di->dentry = NULL; + /* Forcefully remove the inode */ + if (dentry->d_inode != NULL) dentry->d_inode->i_nlink = 0; + d_drop (dentry); + dput (dentry); + } + } +} /* End Function free_dentries */ + +static int is_devfsd_or_child (struct fs_info *fs_info) +/* [SUMMARY] Test if the current process is devfsd or one of its children. + <fs_info> The filesystem information. + [RETURNS] TRUE if devfsd or child, else FALSE. +*/ +{ + struct task_struct *p; + + for (p = current; p != &init_task; p = p->p_opptr) + { + if (p == fs_info->devfsd_task) return (TRUE); + } + return (FALSE); +} /* End Function is_devfsd_or_child */ + +static inline int devfsd_queue_empty (struct fs_info *fs_info) +/* [SUMMARY] Test if devfsd has work pending in its event queue. + <fs_info> The filesystem information. + [RETURNS] TRUE if the queue is empty, else FALSE. +*/ +{ + return (fs_info->devfsd_buf_out == fs_info->devfsd_buf_in) ? TRUE : FALSE; +} /* End Function devfsd_queue_empty */ + +static int wait_for_devfsd_finished (struct fs_info *fs_info) +/* [SUMMARY] Wait for devfsd to finish processing its event queue. + <fs_info> The filesystem information. + [RETURNS] TRUE if no more waiting will be required, else FALSE. +*/ +{ + DECLARE_WAITQUEUE (wait, current); + + if (fs_info->devfsd_task == NULL) return (TRUE); + if (devfsd_queue_empty (fs_info) && fs_info->devfsd_sleeping) return TRUE; + if ( is_devfsd_or_child (fs_info) ) return (FALSE); + add_wait_queue (&fs_info->revalidate_wait_queue, &wait); + current->state = TASK_UNINTERRUPTIBLE; + if (!devfsd_queue_empty (fs_info) || !fs_info->devfsd_sleeping) + if (fs_info->devfsd_task) schedule(); + remove_wait_queue (&fs_info->revalidate_wait_queue, &wait); + current->state = TASK_RUNNING; + return (TRUE); +} /* End Function wait_for_devfsd_finished */ + +static int devfsd_notify_one (void *data, unsigned int type, umode_t mode, + uid_t uid, gid_t gid, struct fs_info *fs_info) +/* [SUMMARY] Notify a single devfsd daemon of a change. + <data> Data to be passed. + <type> The type of change. + <mode> The mode of the entry. + <uid> The user ID. + <gid> The group ID. + <fs_info> The filesystem info. + [RETURNS] TRUE if an event was queued and devfsd woken up, else FALSE. +*/ +{ + unsigned int next_pos; + unsigned long flags; + struct devfsd_buf_entry *entry; + static spinlock_t lock = SPIN_LOCK_UNLOCKED; + + if ( !( fs_info->devfsd_event_mask & (1 << type) ) ) return (FALSE); + next_pos = fs_info->devfsd_buf_in + 1; + if (next_pos >= devfsd_buf_size) next_pos = 0; + if (next_pos == fs_info->devfsd_buf_out) + { + /* Running up the arse of the reader: drop it */ + atomic_inc (&fs_info->devfsd_overrun_count); + return (FALSE); + } + spin_lock_irqsave (&lock, flags); + fs_info->devfsd_buffer_in_use = TRUE; + next_pos = fs_info->devfsd_buf_in + 1; + if (next_pos >= devfsd_buf_size) next_pos = 0; + entry = (struct devfsd_buf_entry *) fs_info->devfsd_buffer + + fs_info->devfsd_buf_in; + entry->data = data; + entry->type = type; + entry->mode = mode; + entry->uid = uid; + entry->gid = gid; + fs_info->devfsd_buf_in = next_pos; + fs_info->devfsd_buffer_in_use = FALSE; + spin_unlock_irqrestore (&lock, flags); + wake_up_interruptible (&fs_info->devfsd_wait_queue); + return (TRUE); +} /* End Function devfsd_notify_one */ + +static void devfsd_notify (struct devfs_entry *de, unsigned int type, int wait) +/* [SUMMARY] Notify all devfsd daemons of a change. + <de> The devfs entry that has changed. + <type> The type of change event. + <wait> If TRUE, the functions waits for all daemons to finish processing + the event. + [RETURNS] Nothing. +*/ +{ + struct fs_info *fs_info; + + for (fs_info = first_fs; fs_info != NULL; fs_info = fs_info->next) + { + if (devfsd_notify_one (de, type, de->mode, current->euid, + current->egid, fs_info) && wait) + wait_for_devfsd_finished (fs_info); + } +} /* End Function devfsd_notify */ + +/*PUBLIC_FUNCTION*/ +devfs_handle_t devfs_register (devfs_handle_t dir, + const char *name, unsigned int namelen, + unsigned int flags, + unsigned int major, unsigned int minor, + umode_t mode, uid_t uid, gid_t gid, + void *ops, void *info) +/* [SUMMARY] Register a device entry. + <dir> The handle to the parent devfs directory entry. If this is NULL the + new name is relative to the root of the devfs. + <name> The name of the entry. + <namelen> The number of characters in <<name>>, not including a NULL + terminator. If this is 0, then <<name>> must be NULL-terminated and the + length is computed internally. + <flags> A set of bitwise-ORed flags (DEVFS_FL_*). + <major> The major number. Not needed for regular files. + <minor> The minor number. Not needed for regular files. + <mode> The default file mode. + <uid> The default UID of the file. + <guid> The default GID of the file. + <ops> The <<file_operations>> or <<block_device_operations>> structure. + This must not be externally deallocated. + <info> An arbitrary pointer which will be written to the <<private_data>> + field of the <<file>> structure passed to the device driver. You can set + this to whatever you like, and change it once the file is opened (the next + file opened will not see this change). + [RETURNS] A handle which may later be used in a call to + [<devfs_unregister>]. On failure NULL is returned. +*/ +{ + int is_new; + struct devfs_entry *de; + + if (name == NULL) + { + printk ("%s: devfs_register(): NULL name pointer\n", DEVFS_NAME); + return NULL; + } + if (ops == NULL) + { + if ( S_ISCHR (mode) ) ops = get_chrfops (major, 0); + else if ( S_ISBLK (mode) ) ops = (void *) get_blkfops (major); + if (ops == NULL) + { + printk ("%s: devfs_register(%s): NULL ops pointer\n", + DEVFS_NAME, name); + return NULL; + } + printk ("%s: devfs_register(%s): NULL ops, got %p from major table\n", + DEVFS_NAME, name, ops); + } + if ( S_ISDIR (mode) ) + { + printk("%s: devfs_register(%s): creating directories is not allowed\n", + DEVFS_NAME, name); + return NULL; + } + if ( S_ISLNK (mode) ) + { + printk ("%s: devfs_register(%s): creating symlinks is not allowed\n", + DEVFS_NAME, name); + return NULL; + } + if (namelen < 1) namelen = strlen (name); + if ( S_ISCHR (mode) && (flags & DEVFS_FL_AUTO_DEVNUM) ) + { + if (next_devnum_char >= MAX_DEVNUM) + { + printk ("%s: devfs_register(%s): exhausted char device numbers\n", + DEVFS_NAME, name); + return NULL; + } + major = next_devnum_char >> 8; + minor = next_devnum_char & 0xff; + ++next_devnum_char; + } + if ( S_ISBLK (mode) && (flags & DEVFS_FL_AUTO_DEVNUM) ) + { + if (next_devnum_block >= MAX_DEVNUM) + { + printk ("%s: devfs_register(%s): exhausted block device numbers\n", + DEVFS_NAME, name); + return NULL; + } + major = next_devnum_block >> 8; + minor = next_devnum_block & 0xff; + ++next_devnum_block; + } + de = search_for_entry (dir, name, namelen, TRUE, TRUE, &is_new, FALSE); + if (de == NULL) + { + printk ("%s: devfs_register(): could not create entry: \"%s\"\n", + DEVFS_NAME, name); + return NULL; + } +#ifdef CONFIG_DEVFS_DEBUG + if (devfs_debug & DEBUG_REGISTER) + printk ("%s: devfs_register(%s): de: %p %s\n", + DEVFS_NAME, name, de, is_new ? "new" : "existing"); +#endif + if (!is_new) + { + /* Existing entry */ + if ( !S_ISCHR (de->mode) && !S_ISBLK (de->mode) && + !S_ISREG (de->mode) ) + { + printk ("%s: devfs_register(): existing non-device/file entry: \"%s\"\n", + DEVFS_NAME, name); + return NULL; + } + if (de->registered) + { + printk("%s: devfs_register(): device already registered: \"%s\"\n", + DEVFS_NAME, name); + return NULL; + } + /* If entry already exists free any dentries associated with it */ + if (de->registered) free_dentries (de); + } + de->registered = TRUE; + if ( S_ISCHR (mode) || S_ISBLK (mode) ) + { + de->u.fcb.u.device.major = major; + de->u.fcb.u.device.minor = minor; + } + else if ( S_ISREG (mode) ) de->u.fcb.u.file.size = 0; + else + { + printk ("%s: devfs_register(): illegal mode: %x\n", + DEVFS_NAME, mode); + return (NULL); + } + de->info = info; + de->mode = mode; + de->u.fcb.default_uid = uid; + de->u.fcb.default_gid = gid; + de->registered = TRUE; + de->u.fcb.ops = ops; + de->u.fcb.auto_owner = (flags & DEVFS_FL_AUTO_OWNER) ? TRUE : FALSE; + de->u.fcb.aopen_notify = (flags & DEVFS_FL_AOPEN_NOTIFY) ? TRUE : FALSE; + if (flags & DEVFS_FL_REMOVABLE) + { + de->u.fcb.removable = TRUE; + ++de->parent->u.dir.num_removable; + } + de->u.fcb.open = FALSE; + de->show_unreg = ( (boot_options & OPTION_SHOW) + || (flags & DEVFS_FL_SHOW_UNREG) ) ? TRUE : FALSE; + de->hide = (flags & DEVFS_FL_HIDE) ? TRUE : FALSE; + devfsd_notify (de, DEVFSD_NOTIFY_REGISTERED, flags & DEVFS_FL_WAIT); + return de; +} /* End Function devfs_register */ + +static void unregister (struct devfs_entry *de) +/* [SUMMARY] Unregister a device entry. + <de> The entry to unregister. + [RETURNS] Nothing. +*/ +{ + struct devfs_entry *child; + + if ( (child = de->slave) != NULL ) + { + de->slave = NULL; /* Unhook first in case slave is parent directory */ + unregister (child); + } + if (de->registered) + { + devfsd_notify (de, DEVFSD_NOTIFY_UNREGISTERED, 0); + free_dentries (de); + } + de->info = NULL; + if ( S_ISCHR (de->mode) || S_ISBLK (de->mode) || S_ISREG (de->mode) ) + { + de->registered = FALSE; + de->u.fcb.ops = NULL; + return; + } + if ( S_ISLNK (de->mode) ) + { + de->registered = FALSE; + if (de->u.symlink.linkname != NULL) kfree (de->u.symlink.linkname); + de->u.symlink.linkname = NULL; + return; + } + if ( S_ISFIFO (de->mode) ) + { + de->registered = FALSE; + return; + } + if (!de->registered) return; + if ( !S_ISDIR (de->mode) ) + { + printk ("%s: unregister(): unsupported type\n", DEVFS_NAME); + return; + } + de->registered = FALSE; + /* Now recursively search the subdirectories: this is a stack chomper */ + for (child = de->u.dir.first; child != NULL; child = child->next) + { +#ifdef CONFIG_DEVFS_DEBUG + if (devfs_debug & DEBUG_UNREGISTER) + printk ("%s: unregister(): child->name: \"%s\" child: %p\n", + DEVFS_NAME, child->name, child); +#endif + unregister (child); + } +} /* End Function unregister */ + +/*PUBLIC_FUNCTION*/ +void devfs_unregister (devfs_handle_t de) +/* [SUMMARY] Unregister a device entry. + <de> A handle previously created by [<devfs_register>] or returned from + [<devfs_find_handle>]. If this is NULL the routine does nothing. + [RETURNS] Nothing. +*/ +{ + if (de == NULL) return; +#ifdef CONFIG_DEVFS_DEBUG + if (devfs_debug & DEBUG_UNREGISTER) + printk ("%s: devfs_unregister(): de->name: \"%s\" de: %p\n", + DEVFS_NAME, de->name, de); +#endif + unregister (de); +} /* End Function devfs_unregister */ + +/*PUBLIC_FUNCTION*/ +int devfs_mk_symlink (devfs_handle_t dir, + const char *name, unsigned int namelen, + unsigned int flags, + const char *link, unsigned int linklength, + devfs_handle_t *handle, void *info) +/* [SUMMARY] Create a symbolic link in the devfs namespace. + <dir> The handle to the parent devfs directory entry. If this is NULL the + new name is relative to the root of the devfs. + <name> The name of the entry. + <namelen> The number of characters in <<name>>, not including a NULL + terminator. If this is 0, then <<name>> must be NULL-terminated and the + length is computed internally. + <flags> A set of bitwise-ORed flags (DEVFS_FL_*). + <link> The destination name. + <linklength> The number of characters in <<link>>, not including a NULL + terminator. If this is 0, then <<link>> must be NULL-terminated and the + length is computed internally. + <handle> The handle to the symlink entry is written here. This may be NULL. + <info> An arbitrary pointer which will be associated with the entry. + [RETURNS] 0 on success, else a negative error code is returned. +*/ +{ + int is_new; + char *newname; + struct devfs_entry *de; + + if (handle != NULL) *handle = NULL; + if (name == NULL) + { + printk ("%s: devfs_mk_symlink(): NULL name pointer\n", DEVFS_NAME); + return -EINVAL; + } +#ifdef CONFIG_DEVFS_DEBUG + if (devfs_debug & DEBUG_REGISTER) + printk ("%s: devfs_mk_symlink(%s)\n", DEVFS_NAME, name); +#endif + if (namelen < 1) namelen = strlen (name); + if (link == NULL) + { + printk ("%s: devfs_mk_symlink(): NULL link pointer\n", DEVFS_NAME); + return -EINVAL; + } + if (linklength < 1) linklength = strlen (link); + de = search_for_entry (dir, name, namelen, TRUE, TRUE, &is_new, FALSE); + if (de == NULL) return -ENOMEM; + if (!S_ISLNK (de->mode) && de->registered) + { + printk ("%s: devfs_mk_symlink(): non-link entry already exists\n", + DEVFS_NAME); + return -EEXIST; + } + if (handle != NULL) *handle = de; + de->mode = S_IFLNK | S_IRUGO | S_IXUGO; + de->info = info; + de->show_unreg = ( (boot_options & OPTION_SHOW) + || (flags & DEVFS_FL_SHOW_UNREG) ) ? TRUE : FALSE; + de->hide = (flags & DEVFS_FL_HIDE) ? TRUE : FALSE; + /* Note there is no need to fiddle the dentry cache if the symlink changes + as the symlink follow method is called every time it's needed */ + if ( de->registered && (linklength == de->u.symlink.length) ) + { + /* New link is same length as old link */ + if (memcmp (link, de->u.symlink.linkname, linklength) == 0) return 0; + return -EEXIST; /* Contents would change */ + } + /* Have to create/update */ + if (de->registered) return -EEXIST; + de->registered = TRUE; + if ( ( newname = kmalloc (linklength + 1, GFP_KERNEL) ) == NULL ) + { + struct devfs_entry *parent = de->parent; + + if (!is_new) return -ENOMEM; + /* Have to clean up */ + if (de->prev == NULL) parent->u.dir.first = de->next; + else de->prev->next = de->next; + if (de->next == NULL) parent->u.dir.last = de->prev; + else de->next->prev = de->prev; + kfree (de); + return -ENOMEM; + } + if (de->u.symlink.linkname != NULL) kfree (de->u.symlink.linkname); + de->u.symlink.linkname = newname; + memcpy (de->u.symlink.linkname, link, linklength); + de->u.symlink.linkname[linklength] = '\0'; + de->u.symlink.length = linklength; + return 0; +} /* End Function devfs_mk_symlink */ + +/*PUBLIC_FUNCTION*/ +devfs_handle_t devfs_mk_dir (devfs_handle_t dir, const char *name, + unsigned int namelen, void *info) +/* [SUMMARY] Create a directory in the devfs namespace. + <dir> The handle to the parent devfs directory entry. If this is NULL the + new name is relative to the root of the devfs. + <name> The name of the entry. + <namelen> The number of characters in <<name>>, not including a NULL + terminator. If this is 0, then <<name>> must be NULL-terminated and the + length is computed internally. + <info> An arbitrary pointer which will be associated with the entry. + [NOTE] Use of this function is optional. The [<devfs_register>] function + will automatically create intermediate directories as needed. This function + is provided for efficiency reasons, as it provides a handle to a directory. + [RETURNS] A handle which may later be used in a call to + [<devfs_unregister>]. On failure NULL is returned. +*/ +{ + int is_new; + struct devfs_entry *de; + + if (name == NULL) + { + printk ("%s: devfs_mk_dir(): NULL name pointer\n", DEVFS_NAME); + return NULL; + } + if (namelen < 1) namelen = strlen (name); + de = search_for_entry (dir, name, namelen, TRUE, TRUE, &is_new, FALSE); + if (de == NULL) + { + printk ("%s: devfs_mk_dir(): could not create entry: \"%s\"\n", + DEVFS_NAME, name); + return NULL; + } + if (!S_ISDIR (de->mode) && de->registered) + { + printk ("%s: devfs_mk_dir(): existing non-directory entry: \"%s\"\n", + DEVFS_NAME, name); + return NULL; + } +#ifdef CONFIG_DEVFS_DEBUG + if (devfs_debug & DEBUG_REGISTER) + printk ("%s: devfs_mk_dir(%s): de: %p %s\n", + DEVFS_NAME, name, de, is_new ? "new" : "existing"); +#endif + if (!S_ISDIR (de->mode) && !is_new) + { + /* Transmogrifying an old entry */ + de->u.dir.first = NULL; + de->u.dir.last = NULL; + } + de->mode = S_IFDIR | S_IRUGO | S_IXUGO; + de->info = info; + if (!de->registered) de->u.dir.num_removable = 0; + de->registered = TRUE; + de->show_unreg = (boot_options & OPTION_SHOW) ? TRUE : FALSE; + de->hide = FALSE; + return de; +} /* End Function devfs_mk_dir */ + +/*PUBLIC_FUNCTION*/ +devfs_handle_t devfs_find_handle (devfs_handle_t dir, + const char *name, unsigned int namelen, + unsigned int major, unsigned int minor, + char type, int traverse_symlinks) +/* [SUMMARY] Find the handle of a devfs entry. + <dir> The handle to the parent devfs directory entry. If this is NULL the + name is relative to the root of the devfs. + <name> The name of the entry. + <namelen> The number of characters in <<name>>, not including a NULL + terminator. If this is 0, then <<name>> must be NULL-terminated and the + length is computed internally. + <major> The major number. This is used if <<name>> is NULL. + <minor> The minor number. This is used if <<name>> is NULL. + <type> The type of special file to search for. This may be either + DEVFS_SPECIAL_CHR or DEVFS_SPECIAL_BLK. + <traverse_symlinks> If TRUE then symlink entries in the devfs namespace are + traversed. Symlinks pointing out of the devfs namespace will cause a + failure. Symlink traversal consumes stack space. + [RETURNS] A handle which may later be used in a call to + [<devfs_unregister>], [<devfs_get_flags>], or [<devfs_set_flags>]. + On failure NULL is returned. +*/ +{ + devfs_handle_t de; + + if ( (name != NULL) && (name[0] == '\0') ) name = NULL; + de = find_entry (dir, name, namelen, major, minor, type, + traverse_symlinks); + if (de == NULL) return NULL; + if (!de->registered) return NULL; + return de; +} /* End Function devfs_find_handle */ + +/*PUBLIC_FUNCTION*/ +int devfs_get_flags (devfs_handle_t de, unsigned int *flags) +/* [SUMMARY] Get the flags for a devfs entry. + <de> The handle to the device entry. + <flags> The flags are written here. + [RETURNS] 0 on success, else a negative error code. +*/ +{ + unsigned int fl = 0; + + if (de == NULL) return -EINVAL; + if (!de->registered) return -ENODEV; + if (de->show_unreg) fl |= DEVFS_FL_SHOW_UNREG; + if (de->hide) fl |= DEVFS_FL_HIDE; + if ( S_ISCHR (de->mode) || S_ISBLK (de->mode) || S_ISREG (de->mode) ) + { + if (de->u.fcb.auto_owner) fl |= DEVFS_FL_AUTO_OWNER; + if (de->u.fcb.aopen_notify) fl |= DEVFS_FL_AOPEN_NOTIFY; + if (de->u.fcb.removable) fl |= DEVFS_FL_REMOVABLE; + } + *flags = fl; + return 0; +} /* End Function devfs_get_flags */ + +/*PUBLIC_FUNCTION*/ +int devfs_set_flags (devfs_handle_t de, unsigned int flags) +/* [SUMMARY] Set the flags for a devfs entry. + <de> The handle to the device entry. + <flags> The flags to set. Unset flags are cleared. + [RETURNS] 0 on success, else a negative error code. +*/ +{ + if (de == NULL) return -EINVAL; + if (!de->registered) return -ENODEV; +#ifdef CONFIG_DEVFS_DEBUG + if (devfs_debug & DEBUG_SET_FLAGS) + printk ("%s: devfs_set_flags(): de->name: \"%s\"\n", + DEVFS_NAME, de->name); +#endif + de->show_unreg = (flags & DEVFS_FL_SHOW_UNREG) ? TRUE : FALSE; + de->hide = (flags & DEVFS_FL_HIDE) ? TRUE : FALSE; + if ( S_ISCHR (de->mode) || S_ISBLK (de->mode) || S_ISREG (de->mode) ) + { + de->u.fcb.auto_owner = (flags & DEVFS_FL_AUTO_OWNER) ? TRUE : FALSE; + de->u.fcb.aopen_notify = (flags & DEVFS_FL_AOPEN_NOTIFY) ? TRUE:FALSE; + if ( de->u.fcb.removable && !(flags & DEVFS_FL_REMOVABLE) ) + { + de->u.fcb.removable = FALSE; + --de->parent->u.dir.num_removable; + } + else if ( !de->u.fcb.removable && (flags & DEVFS_FL_REMOVABLE) ) + { + de->u.fcb.removable = TRUE; + ++de->parent->u.dir.num_removable; + } + } + return 0; +} /* End Function devfs_set_flags */ + +/*PUBLIC_FUNCTION*/ +int devfs_get_maj_min (devfs_handle_t de, unsigned int *major, + unsigned int *minor) +/* [SUMMARY] Get the major and minor numbers for a devfs entry. + <de> The handle to the device entry. + <major> The major number is written here. This may be NULL. + <minor> The minor number is written here. This may be NULL. + [RETURNS] 0 on success, else a negative error code. +*/ +{ + if (de == NULL) return -EINVAL; + if (!de->registered) return -ENODEV; + if ( S_ISDIR (de->mode) ) return -EISDIR; + if ( !S_ISCHR (de->mode) && !S_ISBLK (de->mode) ) return -EINVAL; + if (major != NULL) *major = de->u.fcb.u.device.major; + if (minor != NULL) *minor = de->u.fcb.u.device.minor; + return 0; +} /* End Function devfs_get_maj_min */ + +/*PUBLIC_FUNCTION*/ +devfs_handle_t devfs_get_handle_from_inode (struct inode *inode) +/* [SUMMARY] Get the devfs handle for a VFS inode. + <inode> The VFS inode. + [RETURNS] The devfs handle on success, else NULL. +*/ +{ + struct devfs_inode *di; + + if (!inode || !inode->i_sb) return NULL; + if (inode->i_sb->s_magic != DEVFS_SUPER_MAGIC) return NULL; + di = get_devfs_inode_from_vfs_inode (inode); + if (!di) return NULL; + return di->de; +} /* End Function devfs_get_handle_from_inode */ + +/*PUBLIC_FUNCTION*/ +int devfs_generate_path (devfs_handle_t de, char *path, int buflen) +/* [SUMMARY] Generate a pathname for an entry, relative to the devfs root. + <de> The devfs entry. + <path> The buffer to write the pathname to. The pathname and '\0' + terminator will be written at the end of the buffer. + <buflen> The length of the buffer. + [RETURNS] The offset in the buffer where the pathname starts on success, + else a negative error code. +*/ +{ + int pos; + + if (de == NULL) return -EINVAL; + if (de->namelen >= buflen) return -ENAMETOOLONG; /* Must be first */ + if (de->parent == NULL) return buflen; /* Don't prepend root */ + pos = buflen - de->namelen - 1; + memcpy (path + pos, de->name, de->namelen); + path[buflen - 1] = '\0'; + for (de = de->parent; de->parent != NULL; de = de->parent) + { + if (pos - de->namelen - 1 < 0) return -ENAMETOOLONG; + path[--pos] = '/'; + pos -= de->namelen; + memcpy (path + pos, de->name, de->namelen); + } + return pos; +} /* End Function devfs_generate_path */ + +/*PUBLIC_FUNCTION*/ +void *devfs_get_ops (devfs_handle_t de) +/* [SUMMARY] Get the device operations for a devfs entry. + <de> The handle to the device entry. + [RETURNS] A pointer to the device operations on success, else NULL. +*/ +{ + if (de == NULL) return NULL; + if (!de->registered) return NULL; + if ( S_ISCHR (de->mode) || S_ISBLK (de->mode) || S_ISREG (de->mode) ) + return de->u.fcb.ops; + return NULL; +} /* End Function devfs_get_ops */ + +/*PUBLIC_FUNCTION*/ +int devfs_set_file_size (devfs_handle_t de, unsigned long size) +/* [SUMMARY] Set the file size for a devfs regular file. + <de> The handle to the device entry. + <size> The new file size. + [RETURNS] 0 on success, else a negative error code. +*/ +{ + struct devfs_inode *di; + + if (de == NULL) return -EINVAL; + if (!de->registered) return -EINVAL; + if ( !S_ISREG (de->mode) ) return -EINVAL; + if (de->u.fcb.u.file.size == size) return 0; + de->u.fcb.u.file.size = size; + for (di = de->first_inode; di != NULL; di = di->next) + { + if (di->dentry == NULL) continue; + if (di->dentry->d_inode == NULL) continue; + di->dentry->d_inode->i_size = size; + } + return 0; +} /* End Function devfs_set_file_size */ + +/*PUBLIC_FUNCTION*/ +void *devfs_get_info (devfs_handle_t de) +/* [SUMMARY] Get the info pointer written to <<private_data>> upon open. + <de> The handle to the device entry. + [RETURNS] The info pointer. +*/ +{ + if (de == NULL) return NULL; + if (!de->registered) return NULL; + return de->info; +} /* End Function devfs_get_info */ + +/*PUBLIC_FUNCTION*/ +int devfs_set_info (devfs_handle_t de, void *info) +/* [SUMMARY] Set the info pointer written to <<private_data>> upon open. + <de> The handle to the device entry. + [RETURNS] 0 on success, else a negative error code. +*/ +{ + if (de == NULL) return -EINVAL; + if (!de->registered) return -EINVAL; + de->info = info; + return 0; +} /* End Function devfs_set_info */ + +/*PUBLIC_FUNCTION*/ +devfs_handle_t devfs_get_parent (devfs_handle_t de) +/* [SUMMARY] Get the parent device entry. + <de> The handle to the device entry. + [RETURNS] The parent device entry if it exists, else NULL. +*/ +{ + if (de == NULL) return NULL; + if (!de->registered) return NULL; + return de->parent; +} /* End Function devfs_get_parent */ + +/*PUBLIC_FUNCTION*/ +devfs_handle_t devfs_get_first_child (devfs_handle_t de) +/* [SUMMARY] Get the first leaf node in a directory. + <de> The handle to the device entry. + [RETURNS] The leaf node device entry if it exists, else NULL. +*/ +{ + if (de == NULL) return NULL; + if (!de->registered) return NULL; + if ( !S_ISDIR (de->mode) ) return NULL; + return de->u.dir.first; +} /* End Function devfs_get_first_child */ + +/*PUBLIC_FUNCTION*/ +devfs_handle_t devfs_get_next_sibling (devfs_handle_t de) +/* [SUMMARY] Get the next sibling leaf node. for a device entry. + <de> The handle to the device entry. + [RETURNS] The leaf node device entry if it exists, else NULL. +*/ +{ + if (de == NULL) return NULL; + if (!de->registered) return NULL; + return de->next; +} /* End Function devfs_get_next_sibling */ + +/*PUBLIC_FUNCTION*/ +void devfs_auto_unregister (devfs_handle_t master, devfs_handle_t slave) +/* [SUMMARY] Configure a devfs entry to be automatically unregistered. + <master> The master devfs entry. Only one slave may be registered. + <slave> The devfs entry which will be automatically unregistered when the + master entry is unregistered. It is illegal to call [<devfs_unregister>] on + this entry. + [RETURNS] Nothing. +*/ +{ + if (master == NULL) return; + if (master->slave != NULL) + { + /* Because of the dumbness of the layers above, ignore duplicates */ + if (master->slave == slave) return; + printk ("%s: devfs_auto_unregister(): only one slave allowed\n", + DEVFS_NAME); + OOPS (" master: \"%s\" old slave: \"%s\" new slave: \"%s\"\n", + master->name, master->slave->name, slave->name); + } + master->slave = slave; +} /* End Function devfs_auto_unregister */ + +/*PUBLIC_FUNCTION*/ +devfs_handle_t devfs_get_unregister_slave (devfs_handle_t master) +/* [SUMMARY] Get the slave entry which will be automatically unregistered. + <master> The master devfs entry. + [RETURNS] The slave which will be unregistered when <<master>> is + unregistered. +*/ +{ + if (master == NULL) return NULL; + return master->slave; +} /* End Function devfs_get_unregister_slave */ + +/*PUBLIC_FUNCTION*/ +const char *devfs_get_name (devfs_handle_t de, unsigned int *namelen) +/* [SUMMARY] Get the name for a device entry in its parent directory. + <de> The handle to the device entry. + <namelen> The length of the name is written here. This may be NULL. + [RETURNS] The name on success, else NULL. +*/ +{ + if (de == NULL) return NULL; + if (!de->registered) return NULL; + if (namelen != NULL) *namelen = de->namelen; + return de->name; +} /* End Function devfs_get_name */ + +/*PUBLIC_FUNCTION*/ +int devfs_register_chrdev (unsigned int major, const char *name, + struct file_operations *fops) +/* [SUMMARY] Optionally register a conventional character driver. + [PURPOSE] This function will register a character driver provided the + "devfs=only" option was not provided at boot time. + <major> The major number for the driver. + <name> The name of the driver (as seen in /proc/devices). + <fops> The file_operations structure pointer. + [RETURNS] 0 on success, else a negative error code on failure. +*/ +{ + if (boot_options & OPTION_ONLY) return 0; + return register_chrdev (major, name, fops); +} /* End Function devfs_register_chrdev */ + +/*PUBLIC_FUNCTION*/ +int devfs_register_blkdev (unsigned int major, const char *name, + struct block_device_operations *bdops) +/* [SUMMARY] Optionally register a conventional block driver. + [PURPOSE] This function will register a block driver provided the + "devfs=only" option was not provided at boot time. + <major> The major number for the driver. + <name> The name of the driver (as seen in /proc/devices). + <bdops> The block_device_operations structure pointer. + [RETURNS] 0 on success, else a negative error code on failure. +*/ +{ + if (boot_options & OPTION_ONLY) return 0; + return register_blkdev (major, name, bdops); +} /* End Function devfs_register_blkdev */ + +/*PUBLIC_FUNCTION*/ +int devfs_unregister_chrdev (unsigned int major, const char *name) +/* [SUMMARY] Optionally unregister a conventional character driver. + [PURPOSE] This function will unregister a character driver provided the + "devfs=only" option was not provided at boot time. + <major> The major number for the driver. + <name> The name of the driver (as seen in /proc/devices). + [RETURNS] 0 on success, else a negative error code on failure. +*/ +{ + if (boot_options & OPTION_ONLY) return 0; + return unregister_chrdev (major, name); +} /* End Function devfs_unregister_chrdev */ + +/*PUBLIC_FUNCTION*/ +int devfs_unregister_blkdev (unsigned int major, const char *name) +/* [SUMMARY] Optionally unregister a conventional block driver. + [PURPOSE] This function will unregister a block driver provided the + "devfs=only" option was not provided at boot time. + <major> The major number for the driver. + <name> The name of the driver (as seen in /proc/devices). + [RETURNS] 0 on success, else a negative error code on failure. +*/ +{ + if (boot_options & OPTION_ONLY) return 0; + return unregister_blkdev (major, name); +} /* End Function devfs_unregister_blkdev */ + +#ifndef MODULE + +/*UNPUBLISHED_FUNCTION*/ +SETUP_STATIC int __init devfs_setup (char *str) +/* [SUMMARY] Process kernel boot options. + <str> The boot options after the "devfs=". + <unused> Unused. + [RETURNS] Nothing. +*/ +{ + while ( (*str != '\0') && !isspace (*str) ) + { +# ifdef CONFIG_DEVFS_DEBUG + if (strncmp (str, "dall", 4) == 0) + { + devfs_debug_init |= DEBUG_ALL; + str += 4; + } + else if (strncmp (str, "dmod", 4) == 0) + { + devfs_debug_init |= DEBUG_MODULE_LOAD; + str += 4; + } + else if (strncmp (str, "dreg", 4) == 0) + { + devfs_debug_init |= DEBUG_REGISTER; + str += 4; + } + else if (strncmp (str, "dunreg", 6) == 0) + { + devfs_debug_init |= DEBUG_UNREGISTER; + str += 6; + } + else if (strncmp (str, "diread", 6) == 0) + { + devfs_debug_init |= DEBUG_I_READ; + str += 6; + } + else if (strncmp (str, "dchange", 7) == 0) + { + devfs_debug_init |= DEBUG_SET_FLAGS; + str += 7; + } + else if (strncmp (str, "diwrite", 7) == 0) + { + devfs_debug_init |= DEBUG_I_WRITE; + str += 7; + } + else if (strncmp (str, "dimknod", 7) == 0) + { + devfs_debug_init |= DEBUG_I_MKNOD; + str += 7; + } + else if (strncmp (str, "dilookup", 8) == 0) + { + devfs_debug_init |= DEBUG_I_LOOKUP; + str += 8; + } + else if (strncmp (str, "diunlink", 8) == 0) + { + devfs_debug_init |= DEBUG_I_UNLINK; + str += 8; + } + else +# endif /* CONFIG_DEVFS_DEBUG */ + if (strncmp (str, "show", 4) == 0) + { + boot_options |= OPTION_SHOW; + str += 4; + } + else if (strncmp (str, "only", 4) == 0) + { + boot_options |= OPTION_ONLY; + str += 4; + } + else if (strncmp (str, "nomount", 7) == 0) + { + boot_options |= OPTION_NOMOUNT; + str += 7; + } + else + return 0; + if (*str != ',') return 0; + ++str; + } + return 1; +} /* End Function devfs_setup */ + +__setup("devfs=", devfs_setup); + +#endif /* !MODULE */ + +EXPORT_SYMBOL(devfs_register); +EXPORT_SYMBOL(devfs_unregister); +EXPORT_SYMBOL(devfs_mk_symlink); +EXPORT_SYMBOL(devfs_mk_dir); +EXPORT_SYMBOL(devfs_find_handle); +EXPORT_SYMBOL(devfs_get_flags); +EXPORT_SYMBOL(devfs_set_flags); +EXPORT_SYMBOL(devfs_get_maj_min); +EXPORT_SYMBOL(devfs_get_handle_from_inode); +EXPORT_SYMBOL(devfs_generate_path); +EXPORT_SYMBOL(devfs_get_ops); +EXPORT_SYMBOL(devfs_set_file_size); +EXPORT_SYMBOL(devfs_get_info); +EXPORT_SYMBOL(devfs_set_info); +EXPORT_SYMBOL(devfs_get_parent); +EXPORT_SYMBOL(devfs_get_first_child); +EXPORT_SYMBOL(devfs_get_next_sibling); +EXPORT_SYMBOL(devfs_auto_unregister); +EXPORT_SYMBOL(devfs_get_unregister_slave); +EXPORT_SYMBOL(devfs_register_chrdev); +EXPORT_SYMBOL(devfs_register_blkdev); +EXPORT_SYMBOL(devfs_unregister_chrdev); +EXPORT_SYMBOL(devfs_unregister_blkdev); + +#ifdef CONFIG_DEVFS_DEBUG +MODULE_PARM(devfs_debug, "i"); +#endif + +static void update_devfs_inode_from_entry (struct devfs_inode *di) +{ + if (di == NULL) return; + if (di->de == NULL) + { + printk ("%s: update_devfs_inode_from_entry(): NULL entry\n", + DEVFS_NAME); + return; + } + if ( S_ISDIR (di->de->mode) ) + { + di->mode = S_IFDIR | S_IRWXU | S_IRUGO | S_IXUGO; + di->uid = 0; + di->gid = 0; + } + else if ( S_ISLNK (di->de->mode) ) + { + di->mode = S_IFLNK | S_IRUGO | S_IXUGO; + di->uid = 0; + di->gid = 0; + } + else if ( S_ISFIFO (di->de->mode) ) + { + di->mode = di->de->mode; + di->uid = di->de->u.fifo.uid; + di->gid = di->de->u.fifo.gid; + } + else + { + if (di->de->u.fcb.auto_owner) + { + mode_t mode = di->de->mode; + + di->mode = (mode & ~S_IALLUGO) | S_IRUGO | S_IWUGO; + } + else + { + di->mode = di->de->mode; + } + di->uid = di->de->u.fcb.default_uid; + di->gid = di->de->u.fcb.default_gid; + } +} /* End Function update_devfs_inode_from_entry */ + +static struct devfs_inode *create_devfs_inode (struct devfs_entry *entry, + struct fs_info *fs_info) +/* [SUMMARY] Create a devfs inode entry. + <entry> The devfs entry to associate the new inode with. + <fs_info> The FS info. + [RETURNS] A pointer to the devfs inode on success, else NULL. +*/ +{ + struct devfs_inode *di, **table; + + /* First ensure table size is enough */ + if (fs_info->num_inodes >= fs_info->table_size) + { + if ( ( table = kmalloc (sizeof *table * + (fs_info->table_size + INODE_TABLE_INC), + GFP_KERNEL) ) == NULL ) return NULL; + fs_info->table_size += INODE_TABLE_INC; +#ifdef CONFIG_DEVFS_DEBUG + if (devfs_debug & DEBUG_I_CREATE) + printk ("%s: create_devfs_inode(): grew inode table to: %u entries\n", + DEVFS_NAME, fs_info->table_size); +#endif + if (fs_info->table) + { + memcpy (table, fs_info->table, sizeof *table *fs_info->num_inodes); + kfree (fs_info->table); + } + fs_info->table = table; + } + if ( ( di = kmalloc (sizeof *di, GFP_KERNEL) ) == NULL ) return NULL; + memset (di, 0, sizeof *di); + di->ino = fs_info->num_inodes + FIRST_INODE; + di->nlink = 1; + fs_info->table[fs_info->num_inodes] = di; + ++fs_info->num_inodes; + di->de = entry; + di->fs_info = fs_info; + di->prev = entry->last_inode; + if (entry->first_inode == NULL) entry->first_inode = di; + else entry->last_inode->next = di; + entry->last_inode = di; + update_devfs_inode_from_entry (di); +#ifdef CONFIG_DEVFS_DEBUG + if (devfs_debug & DEBUG_I_CREATE) + printk ("%s: create_devfs_inode(): new di(%u): %p\n", + DEVFS_NAME, di->ino, di); +#endif + return di; +} /* End Function create_devfs_inode */ + +static int try_modload (struct devfs_entry *parent, struct fs_info *fs_info, + const char *name, unsigned namelen, + char buf[STRING_LENGTH]) +/* [SUMMARY] Notify devfsd of an inode lookup. + <parent> The parent devfs entry. + <fs_info> The filesystem info. + <name> The device name. + <namelen> The number of characters in <<name>>. + <buf> A working area that will be used. This must not go out of scope until + devfsd is idle again. + [RETURNS] 0 on success, else a negative error code. +*/ +{ + int pos; + + if ( !( fs_info->devfsd_event_mask & (1 << DEVFSD_NOTIFY_LOOKUP) ) ) + return -ENOENT; + if ( is_devfsd_or_child (fs_info) ) return -ENOENT; + if (namelen >= STRING_LENGTH) return -ENAMETOOLONG; + memcpy (buf + STRING_LENGTH - namelen - 1, name, namelen); + buf[STRING_LENGTH - 1] = '\0'; + pos = devfs_generate_path (parent, buf, STRING_LENGTH - namelen - 1); + if (pos < 0) return pos; + buf[STRING_LENGTH - namelen - 2] = '/'; + if ( !devfsd_notify_one (buf + pos, DEVFSD_NOTIFY_LOOKUP, 0, + current->euid, current->egid, fs_info) ) + return -ENOENT; + /* Possible success */ + return 0; +} /* End Function try_modload */ + +static void delete_fs (struct fs_info *fs_info) +{ + unsigned int count; + struct devfs_inode *di; + struct devfs_entry *de; + + if (fs_info == NULL) return; + for (count = 0; count < fs_info->num_inodes; ++count) + { + /* Unhook this inode from the devfs tree */ + di = fs_info->table[count]; + de = di->de; + if (di->prev == NULL) de->first_inode = di->next; + else di->prev->next = di->next; + if (di->next == NULL) de->last_inode = di->prev; + else di->next->prev = di->prev; + memset (di, 0, sizeof *di); + kfree (di); + } + if (fs_info->table) kfree (fs_info->table); + if (fs_info->prev == NULL) first_fs = fs_info->next; + else fs_info->prev->next = fs_info->next; + if (fs_info->next == NULL) last_fs = fs_info->prev; + else fs_info->next->prev = fs_info->prev; + memset (fs_info, 0, sizeof *fs_info); + kfree (fs_info); +} /* End Function delete_fs */ + +static int check_disc_changed (struct devfs_entry *de) +/* [SUMMARY] Check if a removable disc was changed. + <de> The device. + [RETURNS] 1 if the media was changed, else 0. +*/ +{ + int tmp; + kdev_t dev = MKDEV (de->u.fcb.u.device.major, de->u.fcb.u.device.minor); + struct block_device_operations *bdops = de->u.fcb.ops; + struct super_block * sb; + extern int warn_no_part; + + if ( !S_ISBLK (de->mode) ) return 0; + if (bdops == NULL) return 0; + if (bdops->check_media_change == NULL) return 0; + if ( !bdops->check_media_change (dev) ) return 0; + printk ( KERN_DEBUG "VFS: Disk change detected on device %s\n", + kdevname (dev) ); + sb = get_super (dev); + if ( sb && invalidate_inodes (sb) ) + printk("VFS: busy inodes on changed media..\n"); + invalidate_buffers (dev); + /* Ugly hack to disable messages about unable to read partition table */ + tmp = warn_no_part; + warn_no_part = 0; + if (bdops->revalidate) bdops->revalidate (dev); + warn_no_part = tmp; + return 1; +} /* End Function check_disc_changed */ + +static void scan_dir_for_removable (struct devfs_entry *dir) +/* [SUMMARY] Scan a directory for removable media devices and check media. + <dir> The directory. + [RETURNS] Nothing. +*/ +{ + struct devfs_entry *de; + + if (dir->u.dir.num_removable < 1) return; + for (de = dir->u.dir.first; de != NULL; de = de->next) + { + if (!de->registered) continue; + if ( !S_ISBLK (de->mode) ) continue; + if (!de->u.fcb.removable) continue; + check_disc_changed (de); + } +} /* End Function scan_dir_for_removable */ + +static int get_removable_partition (struct devfs_entry *dir, const char *name, + unsigned int namelen) +/* [SUMMARY] Get removable media partition. + <dir> The parent directory. + <name> The name of the entry. + <namelen> The number of characters in <<name>>. + [RETURNS] 1 if the media was changed, else 0. +*/ +{ + struct devfs_entry *de; + + for (de = dir->u.dir.first; de != NULL; de = de->next) + { + if (!de->registered) continue; + if ( !S_ISBLK (de->mode) ) continue; + if (!de->u.fcb.removable) continue; + if (strcmp (de->name, "disc") == 0) return check_disc_changed (de); + /* Support for names where the partition is appended to the disc name + */ + if (de->namelen >= namelen) continue; + if (strncmp (de->name, name, de->namelen) != 0) continue; + return check_disc_changed (de); + } + return 0; +} /* End Function get_removable_partition */ + + +/* Superblock operations follow */ + +extern struct inode_operations devfs_iops; + +static void devfs_read_inode (struct inode *inode) +{ + struct devfs_inode *di; + + di = get_devfs_inode_from_vfs_inode (inode); + if (di == NULL) + { + printk ("%s: read_inode(%d): VFS inode: %p NO devfs_inode\n", + DEVFS_NAME, (int) inode->i_ino, inode); + return; + } +#ifdef CONFIG_DEVFS_DEBUG + if (devfs_debug & DEBUG_I_READ) + printk ("%s: read_inode(%d): VFS inode: %p devfs_inode: %p\n", + DEVFS_NAME, (int) inode->i_ino, inode, di); +#endif + inode->i_size = 0; + inode->i_blocks = 0; + inode->i_blksize = 1024; + inode->i_op = &devfs_iops; + inode->i_rdev = NODEV; + if ( S_ISCHR (di->mode) ) + inode->i_rdev = MKDEV (di->de->u.fcb.u.device.major, + di->de->u.fcb.u.device.minor); + else if ( S_ISBLK (di->mode) ) + { + inode->i_rdev = MKDEV (di->de->u.fcb.u.device.major, + di->de->u.fcb.u.device.minor); + inode->i_bdev = bdget (inode->i_rdev); + if (inode->i_bdev) inode->i_bdev->bd_op = di->de->u.fcb.ops; + else printk ("%s: read_inode(%d): no block device from bdget()\n", + DEVFS_NAME, (int) inode->i_ino); + } + else if ( S_ISFIFO (di->mode) ) inode->i_op = &fifo_inode_operations; + else if ( S_ISREG (di->mode) ) inode->i_size = di->de->u.fcb.u.file.size; + inode->i_mode = di->mode; + inode->i_uid = di->uid; + inode->i_gid = di->gid; + inode->i_atime = di->atime; + inode->i_mtime = di->mtime; + inode->i_ctime = di->ctime; + inode->i_nlink = di->nlink; +#ifdef CONFIG_DEVFS_DEBUG + if (devfs_debug & DEBUG_I_READ) + printk ("%s: mode: 0%o uid: %d gid: %d\n", + DEVFS_NAME, (int) inode->i_mode, + (int) inode->i_uid, (int) inode->i_gid); +#endif +} /* End Function devfs_read_inode */ + +static void devfs_write_inode (struct inode *inode) +{ + int index; + struct devfs_inode *di; + struct fs_info *fs_info = inode->i_sb->u.generic_sbp; + + if (inode->i_ino < FIRST_INODE) return; + index = inode->i_ino - FIRST_INODE; + if (index >= fs_info->num_inodes) + { + printk ("%s: writing inode: %lu for which there is no entry!\n", + DEVFS_NAME, inode->i_ino); + return; + } + di = fs_info->table[index]; +#ifdef CONFIG_DEVFS_DEBUG + if (devfs_debug & DEBUG_I_WRITE) + { + printk ("%s: write_inode(%d): VFS inode: %p devfs_inode: %p\n", + DEVFS_NAME, (int) inode->i_ino, inode, di); + printk ("%s: mode: 0%o uid: %d gid: %d\n", + DEVFS_NAME, (int) inode->i_mode, + (int) inode->i_uid, (int) inode->i_gid); + } +#endif + di->mode = inode->i_mode; + di->uid = inode->i_uid; + di->gid = inode->i_gid; + di->atime = inode->i_atime; + di->mtime = inode->i_mtime; + di->ctime = inode->i_ctime; +} /* End Function devfs_write_inode */ + +static int devfs_notify_change (struct dentry *dentry, struct iattr *iattr) +{ + int retval; + struct devfs_inode *di; + struct inode *inode = dentry->d_inode; + struct fs_info *fs_info = inode->i_sb->u.generic_sbp; + + di = get_devfs_inode_from_vfs_inode (inode); + if (di == NULL) return -ENODEV; + retval = inode_change_ok (inode, iattr); + if (retval != 0) return retval; + inode_setattr (inode, iattr); + if ( iattr->ia_valid & (ATTR_MODE | ATTR_UID | ATTR_GID) ) + devfsd_notify_one (di->de, DEVFSD_NOTIFY_CHANGE, inode->i_mode, + inode->i_uid, inode->i_gid, fs_info); + return 0; +} /* End Function devfs_notify_change */ + +static void devfs_put_super (struct super_block *sb) +{ + struct fs_info *fs_info = sb->u.generic_sbp; + +#ifdef CONFIG_DEVFS_DEBUG + if (devfs_debug & DEBUG_S_PUT) + printk ("%s: put_super(): devfs ptr: %p\n", DEVFS_NAME, fs_info); +#endif + sb->s_dev = 0; +#ifdef CONFIG_DEVFS_TUNNEL + dput (fs_info->table[0]->covered); +#endif + delete_fs (fs_info); + MOD_DEC_USE_COUNT; +} /* End Function devfs_put_super */ + +static int devfs_statfs (struct super_block *sb, struct statfs *buf,int bufsiz) +{ + struct statfs tmp; + + tmp.f_type = DEVFS_SUPER_MAGIC; + tmp.f_bsize = PAGE_SIZE / sizeof (long); + tmp.f_blocks = 0; + tmp.f_bfree = 0; + tmp.f_bavail = 0; + tmp.f_files = 0; + tmp.f_ffree = 0; + tmp.f_namelen = NAME_MAX; + return copy_to_user (buf, &tmp, bufsiz) ? -EFAULT : 0; +} /* End Function devfs_statfs */ + +static struct super_operations devfs_sops = +{ + read_inode: devfs_read_inode, + write_inode: devfs_write_inode, + notify_change: devfs_notify_change, + put_super: devfs_put_super, + statfs: devfs_statfs, +}; + +static struct inode *get_vfs_inode (struct super_block *sb, + struct devfs_inode *di, + struct dentry *dentry) +/* [SUMMARY] Get a VFS inode. + <sb> The super block. + <di> The devfs inode. + <dentry> The dentry to register with the devfs inode. + [RETURNS] The inode on success, else NULL. +*/ +{ + struct inode *inode; + + if (di->dentry != NULL) + { + printk ("%s: get_vfs_inode(%u): old di->dentry: %p \"%s\" new dentry: %p \"%s\"\n", + DEVFS_NAME, di->ino, di->dentry, di->dentry->d_name.name, + dentry, dentry->d_name.name); + printk (" old inode: %p\n", di->dentry->d_inode); + return NULL; + } + if ( ( inode = iget (sb, di->ino) ) == NULL ) return NULL; + di->dentry = dentry; + return inode; +} /* End Function get_vfs_inode */ + + +/* File operations for device entries follow */ + +static int devfs_read (struct file *file, char *buf, size_t len, loff_t *ppos) +{ + if ( S_ISDIR (file->f_dentry->d_inode->i_mode) ) return -EISDIR; + return -EINVAL; +} /* End Function devfs_read */ + +static int devfs_readdir (struct file *file, void *dirent, filldir_t filldir) +{ + int err, count; + int stored = 0; + struct fs_info *fs_info; + struct devfs_inode *di; + struct devfs_entry *parent, *de; + struct inode *inode = file->f_dentry->d_inode; + + if (inode == NULL) + { + printk ("%s: readdir(): NULL inode\n", DEVFS_NAME); + return -EBADF; + } + if ( !S_ISDIR (inode->i_mode) ) + { + printk ("%s: readdir(): inode is not a directory\n", DEVFS_NAME); + return -ENOTDIR; + } + fs_info = inode->i_sb->u.generic_sbp; + di = get_devfs_inode_from_vfs_inode (file->f_dentry->d_inode); + parent = di->de; + if ( (long) file->f_pos < 0 ) return -EINVAL; +#ifdef CONFIG_DEVFS_DEBUG + if (devfs_debug & DEBUG_F_READDIR) + printk ("%s: readdir(): fs_info: %p pos: %ld\n", DEVFS_NAME, + fs_info, (long) file->f_pos); +#endif + switch ( (long) file->f_pos ) + { + case 0: + scan_dir_for_removable (parent); + err = (*filldir) (dirent, "..", 2, file->f_pos, + file->f_dentry->d_parent->d_inode->i_ino); + if (err == -EINVAL) break; + if (err < 0) return err; + file->f_pos++; + ++stored; + /* Fall through */ + case 1: + err = (*filldir) (dirent, ".", 1, file->f_pos, inode->i_ino); + if (err == -EINVAL) break; + if (err < 0) return err; + file->f_pos++; + ++stored; + /* Fall through */ + default: + /* Skip entries */ + count = file->f_pos - 2; + for (de = parent->u.dir.first; (de != NULL) && (count > 0); + de = de->next) + { + if ( IS_HIDDEN (de) ) continue; + if (!fs_info->require_explicit) + { + --count; + continue; + } + /* Must search for an inode for this FS */ + for (di = de->first_inode; di != NULL; di = di->next) + { + if (fs_info == di->fs_info) break; + } + if (di != NULL) --count; + } + /* Now add all remaining entries */ + for (; de != NULL; de = de->next) + { + if ( IS_HIDDEN (de) ) continue; + /* Must search for an inode for this FS */ + for (di = de->first_inode; di != NULL; di = di->next) + { + if (fs_info == di->fs_info) break; + } + if (di == NULL) + { + if (fs_info->require_explicit) continue; + /* Have to create the inode right now */ + di = create_devfs_inode (de, fs_info); + if (di == NULL) return -ENOMEM; + } + else if (di->ctime == 0) update_devfs_inode_from_entry (di); + err = (*filldir) (dirent, de->name, de->namelen, + file->f_pos, di->ino); + if (err == -EINVAL) break; + if (err < 0) return err; + file->f_pos++; + ++stored; + } + break; + } + return stored; +} /* End Function devfs_readdir */ + +static int devfs_open (struct inode *inode, struct file *file) +{ + int err; + struct fcb_type *df; + struct devfs_inode *di; + struct dentry *dentry = file->f_dentry; + struct fs_info *fs_info = inode->i_sb->u.generic_sbp; + + di = get_devfs_inode_from_vfs_inode (inode); + if (di == NULL) return -ENODEV; + if ( S_ISDIR (di->de->mode) ) return 0; + df = &di->de->u.fcb; + if (!di->de->registered) return -ENODEV; + file->private_data = di->de->info; + if ( S_ISBLK (inode->i_mode) ) + { + file->f_op = &def_blk_fops; + if (df->ops) inode->i_bdev->bd_op = df->ops; + } + else file->f_op = df->ops; + if (file->f_op) + err = file->f_op->open ? (*file->f_op->open) (inode, file) : 0; + else + { + /* Fallback to legacy scheme */ + if ( S_ISCHR (inode->i_mode) ) err = chrdev_open (inode, file); + else err = -ENODEV; + } + if (err < 0) return err; + /* Open was successful */ + df->open = TRUE; + if (dentry->d_count != 1) return 0; /* No fancy operations */ + /* This is the first open */ + if (df->auto_owner) + { + /* Change the ownership/protection */ + di->mode = (di->mode & ~S_IALLUGO) | (di->de->mode & S_IRWXUGO); + di->uid = current->euid; + di->gid = current->egid; + inode->i_mode = di->mode; + inode->i_uid = di->uid; + inode->i_gid = di->gid; + } + if (df->aopen_notify) + devfsd_notify_one (di->de, DEVFSD_NOTIFY_ASYNC_OPEN, inode->i_mode, + current->euid, current->egid, fs_info); + return 0; +} /* End Function devfs_open */ + +static struct file_operations devfs_fops = +{ + read: devfs_read, + readdir: devfs_readdir, + open: devfs_open, +}; + + +/* Dentry operations for device entries follow */ + +static void devfs_d_release (struct dentry *dentry) +/* [SUMMARY] Callback for when a dentry is freed. +*/ +{ +#ifdef CONFIG_DEVFS_DEBUG + struct inode *inode = dentry->d_inode; + + if (devfs_debug & DEBUG_D_RELEASE) + printk ("%s: d_release(): dentry: %p inode: %p\n", + DEVFS_NAME, dentry, inode); +#endif +} /* End Function devfs_d_release */ + +static void devfs_d_iput (struct dentry *dentry, struct inode *inode) +/* [SUMMARY] Callback for when a dentry loses its inode. +*/ +{ + struct devfs_inode *di; + + di = get_devfs_inode_from_vfs_inode (inode); +#ifdef CONFIG_DEVFS_DEBUG + if (devfs_debug & DEBUG_D_IPUT) + printk ("%s: d_iput(): dentry: %p inode: %p di: %p di->dentry: %p\n", + DEVFS_NAME, dentry, inode, di, di->dentry); +#endif + if (di->dentry == dentry) + { + di->dentry = NULL; +#ifdef CONFIG_DEVFS_TUNNEL + dput (di->covered); + di->covered = NULL; +#endif + } + iput (inode); +} /* End Function devfs_d_iput */ + +static void devfs_d_delete (struct dentry *dentry); + +static struct dentry_operations devfs_dops = +{ + d_delete: devfs_d_delete, + d_release: devfs_d_release, + d_iput: devfs_d_iput, +}; + +static int devfs_d_revalidate_wait (struct dentry *dentry, int flags); + +static struct dentry_operations devfs_wait_dops = +{ + d_delete: devfs_d_delete, + d_release: devfs_d_release, + d_iput: devfs_d_iput, + d_revalidate: devfs_d_revalidate_wait, +}; + +static void devfs_d_delete (struct dentry *dentry) +/* [SUMMARY] Callback for when all files for a dentry are closed. +*/ +{ + struct inode *inode = dentry->d_inode; + struct devfs_inode *di; + struct fs_info *fs_info; + + if (dentry->d_op == &devfs_wait_dops) dentry->d_op = &devfs_dops; + /* Unhash dentry if negative (has no inode) */ + if (inode == NULL) + { +#ifdef CONFIG_DEVFS_DEBUG + if (devfs_debug & DEBUG_D_DELETE) + printk ("%s: d_delete(): dropping negative dentry: %p\n", + DEVFS_NAME, dentry); +#endif + d_drop (dentry); + return; + } + fs_info = inode->i_sb->u.generic_sbp; + di = get_devfs_inode_from_vfs_inode (inode); +#ifdef CONFIG_DEVFS_DEBUG + if (devfs_debug & DEBUG_D_DELETE) + printk ("%s: d_delete(): dentry: %p inode: %p devfs_inode: %p\n", + DEVFS_NAME, dentry, inode, di); +#endif + if (di == NULL) return; + if (di->de == NULL) return; + if ( !S_ISCHR (di->mode) && !S_ISBLK (di->mode) && !S_ISREG (di->mode) ) + return; + if (!di->de->u.fcb.open) return; + di->de->u.fcb.open = FALSE; + if (di->de->u.fcb.aopen_notify) + devfsd_notify_one (di->de, DEVFSD_NOTIFY_CLOSE, inode->i_mode, + current->euid, current->egid, fs_info); + if (!di->de->u.fcb.auto_owner) return; + /* Change the ownership/protection back */ + di->mode = (di->mode & ~S_IALLUGO) | S_IRUGO | S_IWUGO; + di->uid = di->de->u.fcb.default_uid; + di->gid = di->de->u.fcb.default_gid; + inode->i_mode = di->mode; + inode->i_uid = di->uid; + inode->i_gid = di->gid; +} /* End Function devfs_d_delete */ + +static int devfs_d_revalidate_wait (struct dentry *dentry, int flags) +{ + devfs_handle_t de = dentry->d_fsdata; + struct inode *dir = dentry->d_parent->d_inode; + struct fs_info *fs_info = dir->i_sb->u.generic_sbp; + + if (!de || de->registered) + { + if ( !dentry->d_inode && is_devfsd_or_child (fs_info) ) + { + struct devfs_inode *di = NULL; + struct inode *inode; + +#ifdef CONFIG_DEVFS_DEBUG + char txt[STRING_LENGTH]; + + memset (txt, 0, STRING_LENGTH); + memcpy (txt, dentry->d_name.name, + (dentry->d_name.len >= STRING_LENGTH) ? + (STRING_LENGTH - 1) : dentry->d_name.len); + if (devfs_debug & DEBUG_I_LOOKUP) + printk ("%s: d_revalidate(): dentry: %p name: \"%s\" by: \"%s\"\n", + DEVFS_NAME, dentry, txt, current->comm); +#endif + if (de) + { + /* Search for an inode for this FS */ + for (di = de->first_inode; di != NULL; di = di->next) + if (di->fs_info == fs_info) break; + } + if (de == NULL) + { + devfs_handle_t parent; + struct devfs_inode *pi; + + pi = get_devfs_inode_from_vfs_inode (dir); + parent = pi->de; + de = search_for_entry_in_dir (parent, dentry->d_name.name, + dentry->d_name.len, FALSE); + } + if (de == NULL) return 1; + /* Create an inode, now that the driver information is available + */ + if (di == NULL) di = create_devfs_inode (de, fs_info); + else if (di->ctime == 0) update_devfs_inode_from_entry (di); + else di->mode = (de->mode & ~S_IALLUGO) | (di->mode & S_IALLUGO); + if (di == NULL) return 1; + if ( ( inode = get_vfs_inode (dir->i_sb, di, dentry) ) == NULL ) + return 1; +#ifdef CONFIG_DEVFS_DEBUG + if (devfs_debug & DEBUG_I_LOOKUP) + printk ("%s: d_revalidate(): new VFS inode(%u): %p devfs_inode: %p\n", + DEVFS_NAME, di->ino, inode, di); +#endif + d_instantiate (dentry, inode); + return 1; + } + } + if ( wait_for_devfsd_finished (fs_info) ) dentry->d_op = &devfs_dops; + return 1; +} /* End Function devfs_d_revalidate_wait */ + + +/* Inode operations for device entries follow */ + +static struct dentry *devfs_lookup (struct inode *dir, struct dentry *dentry) +{ + struct fs_info *fs_info; + struct devfs_inode *di = NULL; + struct devfs_inode *pi; + struct devfs_entry *parent, *de; + struct inode *inode; + char txt[STRING_LENGTH]; + + /* Set up the dentry operations before anything else, to ensure cleaning + up on any error */ + dentry->d_op = &devfs_dops; + if (dir == NULL) + { + printk ("%s: lookup(): NULL directory inode\n", DEVFS_NAME); + return ERR_PTR (-ENOTDIR); + } + if ( !S_ISDIR (dir->i_mode) ) return ERR_PTR (-ENOTDIR); + memset (txt, 0, STRING_LENGTH); + memcpy (txt, dentry->d_name.name, + (dentry->d_name.len >= STRING_LENGTH) ? + (STRING_LENGTH - 1) : dentry->d_name.len); +#ifdef CONFIG_DEVFS_DEBUG + if (devfs_debug & DEBUG_I_LOOKUP) + printk ("%s: lookup(%s): dentry: %p by: \"%s\"\n", + DEVFS_NAME, txt, dentry, current->comm); +#endif + fs_info = dir->i_sb->u.generic_sbp; + /* First try to get the devfs entry for this directory */ + pi = get_devfs_inode_from_vfs_inode (dir); + if (pi == NULL) return ERR_PTR (-EINVAL); + parent = pi->de; + if (!parent->registered) return ERR_PTR (-ENOENT); + /* Try to reclaim an existing devfs entry */ + de = search_for_entry_in_dir (parent, + dentry->d_name.name, dentry->d_name.len, + FALSE); + if (de) + { + /* Search for an inode for this FS */ + for (di = de->first_inode; di != NULL; di = di->next) + if (di->fs_info == fs_info) break; + } + if (fs_info->require_explicit) + { + if (di == NULL) + { + /* Make the dentry negative so a subsequent operation can deal + with it (for the benefit of mknod()). Leaving the dentry + unhashed will cause <lock_parent> to fail which in turns causes + <do_mknod> to fail */ + d_add (dentry, NULL); + return NULL; + } + } + if ( ( (de == NULL) || !de->registered ) && + (parent->u.dir.num_removable > 0) && + get_removable_partition (parent, dentry->d_name.name, + dentry->d_name.len) ) + { + if (de == NULL) + de = search_for_entry_in_dir (parent, dentry->d_name.name, + dentry->d_name.len, FALSE); + } + if ( (de == NULL) || (!de->registered) ) + { + /* Try with devfsd. For any kind of failure, leave a negative dentry + so someone else can deal with it (in the case where the sysadmin + does a mknod()). It's important to do this before hashing the + dentry, so that the devfsd queue is filled before revalidates + can start */ + if (try_modload (parent, fs_info, + dentry->d_name.name, dentry->d_name.len, txt) < 0) + { + d_add (dentry, NULL); + return NULL; + } + /* devfsd claimed success */ + dentry->d_op = &devfs_wait_dops; + dentry->d_fsdata = de; + d_add (dentry, NULL); /* Open the floodgates */ + /* Unlock directory semaphore, which will release any waiters. They + will get the hashed dentry, and may be forced to wait for + revalidation */ + up (&dir->i_sem); + devfs_d_revalidate_wait (dentry, 0); /* I might have to wait too */ + down (&dir->i_sem); /* Grab it again because them's the rules */ + /* If someone else has been so kind as to make the inode, we go home + early */ + if (dentry->d_inode) return NULL; + if (de && !de->registered) return NULL; + if (de == NULL) + de = search_for_entry_in_dir (parent, dentry->d_name.name, + dentry->d_name.len, FALSE); + if (de == NULL) return NULL; + /* OK, there's an entry now, but no VFS inode yet */ + } + else + { + dentry->d_op = &devfs_wait_dops; + d_add (dentry, NULL); /* Open the floodgates */ + } + /* Create an inode, now that the driver information is available */ + if (di == NULL) di = create_devfs_inode (de, fs_info); + else if (di->ctime == 0) update_devfs_inode_from_entry (di); + else di->mode = (de->mode & ~S_IALLUGO) | (di->mode & S_IALLUGO); + if (di == NULL) return ERR_PTR (-ENOMEM); + if ( ( inode = get_vfs_inode (dir->i_sb, di, dentry) ) == NULL ) + return ERR_PTR (-ENOMEM); +#ifdef CONFIG_DEVFS_DEBUG + if (devfs_debug & DEBUG_I_LOOKUP) + printk ("%s: lookup(): new VFS inode(%u): %p devfs_inode: %p\n", + DEVFS_NAME, di->ino, inode, di); +#endif + d_instantiate (dentry, inode); + /* Unlock directory semaphore, which will release any waiters. They will + get the hashed dentry, and may be forced to wait for revalidation */ + up (&dir->i_sem); + if (dentry->d_op == &devfs_wait_dops) + devfs_d_revalidate_wait (dentry, 0); /* I might have to wait too */ + down (&dir->i_sem); /* Grab it again because them's the rules */ + return NULL; +} /* End Function devfs_lookup */ + +static int devfs_link (struct dentry *old_dentry, struct inode *dir, + struct dentry *dentry) +{ + /*struct inode *inode = old_dentry->d_inode;*/ + char txt[STRING_LENGTH]; + + memset (txt, 0, STRING_LENGTH); + memcpy (txt, old_dentry->d_name.name, old_dentry->d_name.len); + txt[STRING_LENGTH - 1] = '\0'; + printk ("%s: link of \"%s\"\n", DEVFS_NAME, txt); + return -EPERM; +} /* End Function devfs_link */ + +static int devfs_unlink (struct inode *dir, struct dentry *dentry) +{ + struct devfs_inode *di; + +#ifdef CONFIG_DEVFS_DEBUG + char txt[STRING_LENGTH]; + + if (devfs_debug & DEBUG_I_UNLINK) + { + memset (txt, 0, STRING_LENGTH); + memcpy (txt, dentry->d_name.name, dentry->d_name.len); + txt[STRING_LENGTH - 1] = '\0'; + printk ("%s: unlink(%s)\n", DEVFS_NAME, txt); + } +#endif + + if ( !dir || !S_ISDIR (dir->i_mode) ) return -ENOTDIR; + if (!dentry || !dentry->d_inode) return -ENOENT; + di = get_devfs_inode_from_vfs_inode (dentry->d_inode); + if (di == NULL) return -ENOENT; + if (!di->de->registered) return -ENOENT; + di->de->registered = FALSE; + di->de->hide = TRUE; + free_dentries (di->de); + return 0; +} /* End Function devfs_unlink */ + +static int devfs_symlink (struct inode *dir, struct dentry *dentry, + const char *symname) +{ + int err; + struct fs_info *fs_info; + struct devfs_inode *pi; + struct devfs_inode *di = NULL; + struct devfs_entry *parent, *de; + struct inode *inode; + + if ( !dir || !S_ISDIR (dir->i_mode) ) return -ENOTDIR; + fs_info = dir->i_sb->u.generic_sbp; + /* First try to get the devfs entry for this directory */ + pi = get_devfs_inode_from_vfs_inode (dir); + if (pi == NULL) return -EINVAL; + parent = pi->de; + if (!parent->registered) return -ENOENT; + err = devfs_mk_symlink (parent, dentry->d_name.name, dentry->d_name.len, + DEVFS_FL_NONE, symname, 0, &de, NULL); +#ifdef CONFIG_DEVFS_DEBUG + if (devfs_debug & DEBUG_DISABLED) + printk ("%s: symlink(): errcode from <devfs_mk_symlink>: %d\n", + DEVFS_NAME, err); +#endif + if (err < 0) return err; + /* Search for an inode for this FS */ + for (di = de->first_inode; di != NULL; di = di->next) + { + if (di->fs_info == fs_info) break; + } + if (di == NULL) di = create_devfs_inode (de, fs_info); + if (di == NULL) return -ENOMEM; + di->mode = de->mode; + di->atime = CURRENT_TIME; + di->mtime = CURRENT_TIME; + di->ctime = CURRENT_TIME; + if ( ( inode = get_vfs_inode (dir->i_sb, di, dentry) ) == NULL ) + return -ENOMEM; +#ifdef CONFIG_DEVFS_DEBUG + if (devfs_debug & DEBUG_DISABLED) + printk ("%s: symlink(): new VFS inode(%u): %p dentry: %p\n", + DEVFS_NAME, di->ino, inode, dentry); +#endif + de->hide = FALSE; + d_instantiate (dentry, inode); + devfsd_notify_one (di->de, DEVFSD_NOTIFY_CREATE, inode->i_mode, + inode->i_uid, inode->i_gid, fs_info); + return 0; +} /* End Function devfs_symlink */ + +static int devfs_mkdir (struct inode *dir, struct dentry *dentry, int mode) +{ + int is_new; + struct fs_info *fs_info; + struct devfs_inode *di = NULL; + struct devfs_inode *pi; + struct devfs_entry *parent, *de; + struct inode *inode; + + mode = (mode & ~S_IFMT) | S_IFDIR; + if ( !dir || !S_ISDIR (dir->i_mode) ) return -ENOTDIR; + fs_info = dir->i_sb->u.generic_sbp; + /* We are allowed to create the directory */ + /* First try to get the devfs entry for this directory */ + pi = get_devfs_inode_from_vfs_inode (dir); + if (pi == NULL) return -EINVAL; + parent = pi->de; + if (!parent->registered) return -ENOENT; + /* Try to reclaim an existing devfs entry, create if there isn't one */ + de = search_for_entry (parent, dentry->d_name.name, dentry->d_name.len, + FALSE, TRUE, &is_new, FALSE); + if (de == NULL) return -ENOMEM; + if (de->registered) + { + printk ("%s: mkdir(): existing entry\n", DEVFS_NAME); + return -EEXIST; + } + de->registered = TRUE; + de->hide = FALSE; + if (!S_ISDIR (de->mode) && !is_new) + { + /* Transmogrifying an old entry */ + de->u.dir.first = NULL; + de->u.dir.last = NULL; + } + de->mode = mode; + de->u.dir.num_removable = 0; + /* Search for an inode for this FS */ + for (di = de->first_inode; di != NULL; di = di->next) + { + if (di->fs_info == fs_info) break; + } + if (di == NULL) di = create_devfs_inode (de, fs_info); + if (di == NULL) return -ENOMEM; + di->mode = mode; + di->uid = current->euid; + di->gid = current->egid; + di->atime = CURRENT_TIME; + di->mtime = CURRENT_TIME; + di->ctime = CURRENT_TIME; + if ( ( inode = get_vfs_inode (dir->i_sb, di, dentry) ) == NULL ) + return -ENOMEM; +#ifdef CONFIG_DEVFS_DEBUG + if (devfs_debug & DEBUG_DISABLED) + printk ("%s: mkdir(): new VFS inode(%u): %p dentry: %p\n", + DEVFS_NAME, di->ino, inode, dentry); +#endif + d_instantiate (dentry, inode); + devfsd_notify_one (di->de, DEVFSD_NOTIFY_CREATE, inode->i_mode, + inode->i_uid, inode->i_gid, fs_info); + return 0; +} /* End Function devfs_mkdir */ + +static int devfs_rmdir (struct inode *dir, struct dentry *dentry) +{ + int has_children = FALSE; + struct fs_info *fs_info; + struct devfs_inode *di = NULL; + struct devfs_entry *de, *child; + struct inode *inode = dentry->d_inode; + + if ( !dir || !S_ISDIR (dir->i_mode) ) return -ENOTDIR; + if (dir->i_sb->u.generic_sbp != inode->i_sb->u.generic_sbp) return -EINVAL; + if (inode == dir) return -EPERM; + fs_info = dir->i_sb->u.generic_sbp; + di = get_devfs_inode_from_vfs_inode (inode); + if (di == NULL) return -ENOENT; + de = di->de; + if (!de->registered) return -ENOENT; + if ( !S_ISDIR (de->mode) ) return -ENOTDIR; + for (child = de->u.dir.first; child != NULL; child = child->next) + { + if (child->registered) + { + has_children = TRUE; + break; + } + } + if (has_children) return -ENOTEMPTY; + de->registered = FALSE; + de->hide = TRUE; + free_dentries (de); + return 0; +} /* End Function devfs_rmdir */ + +static int devfs_mknod (struct inode *dir, struct dentry *dentry, int mode, + int rdev) +{ + int is_new; + struct fs_info *fs_info; + struct devfs_inode *di = NULL; + struct devfs_inode *pi; + struct devfs_entry *parent, *de; + struct inode *inode; + +#ifdef CONFIG_DEVFS_DEBUG + char txt[STRING_LENGTH]; + + if (devfs_debug & DEBUG_I_MKNOD) + { + memset (txt, 0, STRING_LENGTH); + memcpy (txt, dentry->d_name.name, dentry->d_name.len); + txt[STRING_LENGTH - 1] = '\0'; + printk ("%s: mknod(%s): mode: 0%o dev: %d\n", + DEVFS_NAME, txt, mode, rdev); + } +#endif + + if ( !dir || !S_ISDIR (dir->i_mode) ) return -ENOTDIR; + fs_info = dir->i_sb->u.generic_sbp; + if ( !S_ISBLK (mode) && !S_ISCHR (mode) && !S_ISFIFO (mode) && + !S_ISSOCK (mode) ) return -EPERM; + /* We are allowed to create the node */ + /* First try to get the devfs entry for this directory */ + pi = get_devfs_inode_from_vfs_inode (dir); + if (pi == NULL) return -EINVAL; + parent = pi->de; + if (!parent->registered) return -ENOENT; + /* Try to reclaim an existing devfs entry, create if there isn't one */ + de = search_for_entry (parent, dentry->d_name.name, dentry->d_name.len, + FALSE, TRUE, &is_new, FALSE); + if (de == NULL) return -ENOMEM; + if (!de->registered) + { + /* Since we created the devfs entry we get to choose things */ + de->info = NULL; + de->mode = mode; + if ( S_ISBLK (mode) || S_ISCHR (mode) ) + { + de->u.fcb.u.device.major = MAJOR (rdev); + de->u.fcb.u.device.minor = MINOR (rdev); + de->u.fcb.default_uid = current->euid; + de->u.fcb.default_gid = current->egid; + de->u.fcb.ops = NULL; + de->u.fcb.auto_owner = FALSE; + de->u.fcb.aopen_notify = FALSE; + de->u.fcb.open = FALSE; + } + else if ( S_ISFIFO (mode) ) + { + de->u.fifo.uid = current->euid; + de->u.fifo.gid = current->egid; + } + } + de->registered = TRUE; + de->show_unreg = FALSE; + de->hide = FALSE; + /* Search for an inode for this FS */ + for (di = de->first_inode; di != NULL; di = di->next) + { + if (di->fs_info == fs_info) break; + } + if (di == NULL) di = create_devfs_inode (de, fs_info); + if (di == NULL) return -ENOMEM; + di->mode = mode; + di->uid = current->euid; + di->gid = current->egid; + di->atime = CURRENT_TIME; + di->mtime = CURRENT_TIME; + di->ctime = CURRENT_TIME; + if ( ( inode = get_vfs_inode (dir->i_sb, di, dentry) ) == NULL ) + return -ENOMEM; +#ifdef CONFIG_DEVFS_DEBUG + if (devfs_debug & DEBUG_I_MKNOD) + printk ("%s: new VFS inode(%u): %p dentry: %p\n", + DEVFS_NAME, di->ino, inode, dentry); +#endif + d_instantiate (dentry, inode); + devfsd_notify_one (di->de, DEVFSD_NOTIFY_CREATE, inode->i_mode, + inode->i_uid, inode->i_gid, fs_info); + return 0; +} /* End Function devfs_mknod */ + +static int devfs_readlink (struct dentry *dentry, char *buffer, int buflen) +{ + struct inode *inode = dentry->d_inode; + struct devfs_inode *di; + + if ( !inode || !S_ISLNK (inode->i_mode) ) return -EINVAL; + di = get_devfs_inode_from_vfs_inode (inode); + if (di == NULL) return -ENOENT; + if (!di->de->registered) return -ENOENT; +#ifdef CONFIG_DEVFS_DEBUG + if (devfs_debug & DEBUG_I_RLINK) + printk ("%s: readlink(): dentry: %p\n", DEVFS_NAME, dentry); +#endif + if (buflen > di->de->u.symlink.length + 1) + buflen = di->de->u.symlink.length + 1; + if (copy_to_user (buffer, di->de->u.symlink.linkname, buflen) == 0) + return buflen; + return -EFAULT; +} /* End Function devfs_readlink */ + +static struct dentry *devfs_follow_link (struct dentry *dentry, + struct dentry *base, + unsigned int follow) +{ + struct inode *inode = dentry->d_inode; + struct devfs_inode *di; + + if ( !inode || !S_ISLNK (inode->i_mode) ) + { + dget (dentry); + dput (base); + return dentry; + } +#ifdef CONFIG_DEVFS_DEBUG + if (devfs_debug & DEBUG_I_FLINK) + printk ("%s: follow_link(): dentry: %p\n", DEVFS_NAME, dentry); +#endif + di = get_devfs_inode_from_vfs_inode (inode); + if ( (di == NULL) || !di->de->registered ) + { + dput (base); + return ERR_PTR (-ENOENT); + } + base = lookup_dentry (di->de->u.symlink.linkname, base, follow); + return base; +} /* End Function devfs_follow_link */ + +static struct inode_operations devfs_iops = +{ + default_file_ops: &devfs_fops, + lookup: devfs_lookup, + link: devfs_link, + unlink: devfs_unlink, + symlink: devfs_symlink, + mkdir: devfs_mkdir, + rmdir: devfs_rmdir, + mknod: devfs_mknod, + readlink: devfs_readlink, + follow_link: devfs_follow_link, +}; + +static struct super_block *devfs_read_super (struct super_block *sb, + void *data, int silent) +{ + char *aopt = data; + struct fs_info *fs_info = NULL; + struct devfs_inode *di; + struct inode *root_inode = NULL; + + if (get_root_entry () == NULL) goto out_no_root; + if ( ( fs_info = kmalloc (sizeof *fs_info, GFP_KERNEL) ) == NULL ) + return NULL; + memset (fs_info, 0, sizeof *fs_info); + atomic_set (&fs_info->devfsd_overrun_count, 0); + init_waitqueue_head (&fs_info->devfsd_wait_queue); + init_waitqueue_head (&fs_info->revalidate_wait_queue); + fs_info->prev = last_fs; + if (first_fs == NULL) first_fs = fs_info; + else last_fs->next = fs_info; + last_fs = fs_info; + fs_info->sb = sb; + if (aopt) + { + if (strcmp (aopt, "explicit") == 0) fs_info->require_explicit = TRUE; + } + lock_super (sb); + sb->u.generic_sbp = fs_info; + sb->s_blocksize = 1024; + sb->s_blocksize_bits = 10; + sb->s_magic = DEVFS_SUPER_MAGIC; + sb->s_op = &devfs_sops; + di = create_devfs_inode (root_entry, fs_info); + if (di == NULL) goto out_no_root; + if (di->ino != 1) + { + printk ("%s: read_super: root inode number is: %d!\n", + DEVFS_NAME, di->ino); + goto out_no_root; + } + if ( ( root_inode = get_vfs_inode (sb, di, NULL) ) == NULL ) + goto out_no_root; + sb->s_root = D_ALLOC_ROOT (root_inode); + if (!sb->s_root) goto out_no_root; +#ifdef CONFIG_DEVFS_TUNNEL + di->covered = dget (sb->s_root->d_covered); +#endif + unlock_super (sb); +#ifdef CONFIG_DEVFS_DEBUG + if (devfs_debug & DEBUG_DISABLED) + printk ("%s: read super, made devfs ptr: %p\n", + DEVFS_NAME, sb->u.generic_sbp); +#endif + MOD_INC_USE_COUNT; + return sb; + +out_no_root: + printk ("devfs_read_super: get root inode failed\n"); + delete_fs (fs_info); + if (root_inode) iput (root_inode); + sb->s_dev = 0; + unlock_super (sb); + return NULL; +} /* End Function devfs_read_super */ + + +static struct file_system_type devfs_fs_type = +{ + DEVFS_NAME, + 0, + devfs_read_super, + NULL, +}; + + +/* File operations for devfsd follow */ + +static ssize_t devfsd_read (struct file *file, char *buf, size_t len, + loff_t *ppos) +{ + int done = FALSE; + int ival; + loff_t pos, devname_offset, tlen, rpos; + struct devfsd_notify_struct info; + struct devfsd_buf_entry *entry; + struct fs_info *fs_info = file->f_dentry->d_inode->i_sb->u.generic_sbp; + DECLARE_WAITQUEUE (wait, current); + + /* Can't seek (pread) on this device */ + if (ppos != &file->f_pos) return -ESPIPE; + /* Verify the task has grabbed the queue */ + if (fs_info->devfsd_task != current) return -EPERM; + info.major = 0; + info.minor = 0; + /* Block for a new entry */ + add_wait_queue (&fs_info->devfsd_wait_queue, &wait); + current->state = TASK_INTERRUPTIBLE; + while ( devfsd_queue_empty (fs_info) ) + { + fs_info->devfsd_sleeping = TRUE; + wake_up (&fs_info->revalidate_wait_queue); + schedule (); + fs_info->devfsd_sleeping = FALSE; + if ( signal_pending (current) ) + { + remove_wait_queue (&fs_info->devfsd_wait_queue, &wait); + current->state = TASK_RUNNING; + return -EINTR; + } + } + remove_wait_queue (&fs_info->devfsd_wait_queue, &wait); + current->state = TASK_RUNNING; + /* Now play with the data */ + ival = atomic_read (&fs_info->devfsd_overrun_count); + if (ival > 0) atomic_sub (ival, &fs_info->devfsd_overrun_count); + info.overrun_count = ival; + entry = (struct devfsd_buf_entry *) fs_info->devfsd_buffer + + fs_info->devfsd_buf_out; + info.type = entry->type; + info.mode = entry->mode; + info.uid = entry->uid; + info.gid = entry->gid; + if (entry->type == DEVFSD_NOTIFY_LOOKUP) + { + info.namelen = strlen (entry->data); + pos = 0; + memcpy (info.devname, entry->data, info.namelen + 1); + } + else + { + devfs_handle_t de = entry->data; + + if ( S_ISCHR (de->mode) || S_ISBLK (de->mode) || S_ISREG (de->mode) ) + { + info.major = de->u.fcb.u.device.major; + info.minor = de->u.fcb.u.device.minor; + } + pos = devfs_generate_path (de, info.devname, DEVFS_PATHLEN); + if (pos < 0) return pos; + info.namelen = DEVFS_PATHLEN - pos - 1; + if (info.mode == 0) info.mode = de->mode; + } + devname_offset = info.devname - (char *) &info; + rpos = *ppos; + if (rpos < devname_offset) + { + /* Copy parts of the header */ + tlen = devname_offset - rpos; + if (tlen > len) tlen = len; + if ( copy_to_user (buf, (char *) &info + rpos, tlen) ) + { + return -EFAULT; + } + rpos += tlen; + buf += tlen; + len -= tlen; + } + if ( (rpos >= devname_offset) && (len > 0) ) + { + /* Copy the name */ + tlen = info.namelen + 1; + if (tlen > len) tlen = len; + else done = TRUE; + if ( copy_to_user (buf, info.devname + pos + rpos - devname_offset, + tlen) ) + { + return -EFAULT; + } + rpos += tlen; + } + tlen = rpos - *ppos; + if (done) + { + unsigned int next_pos = fs_info->devfsd_buf_out + 1; + + if (next_pos >= devfsd_buf_size) next_pos = 0; + fs_info->devfsd_buf_out = next_pos; + *ppos = 0; + } + else *ppos = rpos; + return tlen; +} /* End Function devfsd_read */ + +static int devfsd_ioctl (struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + int ival; + struct fs_info *fs_info = inode->i_sb->u.generic_sbp; + + switch (cmd) + { + case DEVFSDIOC_GET_PROTO_REV: + ival = DEVFSD_PROTOCOL_REVISION_KERNEL; + if ( copy_to_user ( (void *)arg, &ival, sizeof ival ) ) return -EFAULT; + break; + case DEVFSDIOC_SET_EVENT_MASK: + /* Ensure only one reader has access to the queue. This scheme will + work even if the global kernel lock were to be removed, because it + doesn't matter who gets in first, as long as only one gets it */ + if (fs_info->devfsd_task == NULL) + { +#ifdef __SMP__ + /* Looks like no-one has it: check again and grab, with interrupts + disabled */ + __cli (); + if (fs_info->devfsd_task == NULL) +#endif + { + fs_info->devfsd_event_mask = 0; /* Temporary disable */ + fs_info->devfsd_task = current; + } +#ifdef __SMP__ + __sti (); +#endif + } + /* Verify the task has grabbed the queue */ + if (fs_info->devfsd_task != current) return -EBUSY; + fs_info->devfsd_file = file; + fs_info->devfsd_buffer = (void *) __get_free_page (GFP_KERNEL); + if (fs_info->devfsd_buffer == NULL) + { + devfsd_close (inode, file); + return -ENOMEM; + } + fs_info->devfsd_buf_out = fs_info->devfsd_buf_in; + fs_info->devfsd_event_mask = arg; /* Let the masses come forth */ + break; + case DEVFSDIOC_RELEASE_EVENT_QUEUE: + if (fs_info->devfsd_file != file) return -EPERM; + return devfsd_close (inode, file); + /*break;*/ +#ifdef CONFIG_DEVFS_DEBUG + case DEVFSDIOC_SET_DEBUG_MASK: + if ( copy_from_user (&ival, (void *) arg, sizeof ival) )return -EFAULT; + devfs_debug = ival; + break; +#endif + default: + return -ENOIOCTLCMD; + } + return 0; +} /* End Function devfsd_ioctl */ + +static int devfsd_close (struct inode *inode, struct file *file) +{ + struct fs_info *fs_info = inode->i_sb->u.generic_sbp; + + if (fs_info->devfsd_file != file) return 0; + fs_info->devfsd_event_mask = 0; + fs_info->devfsd_file = NULL; + if (fs_info->devfsd_buffer) + { + while (fs_info->devfsd_buffer_in_use) schedule (); + free_page ( (unsigned long) fs_info->devfsd_buffer ); + } + fs_info->devfsd_buffer = NULL; + fs_info->devfsd_task = NULL; + wake_up (&fs_info->revalidate_wait_queue); + return 0; +} /* End Function devfsd_close */ + + +#ifdef MODULE +int init_module (void) +#else +int __init init_devfs_fs (void) +#endif +{ + printk ("%s: v%s Richard Gooch (rgooch@atnf.csiro.au)\n", + DEVFS_NAME, DEVFS_VERSION); +#if defined(CONFIG_DEVFS_DEBUG) && !defined(MODULE) + devfs_debug = devfs_debug_init; + printk ("%s: devfs_debug: 0x%0x\n", DEVFS_NAME, devfs_debug); +#endif +#if !defined(MODULE) + printk ("%s: boot_options: 0x%0x\n", DEVFS_NAME, boot_options); +#endif + return register_filesystem (&devfs_fs_type); +} + +#ifndef MODULE +void __init mount_devfs_fs (void) +{ + int err; + extern int do_mount (struct block_device *bdev, const char *dev_name, + const char *dir_name, const char * type, int flags, + void * data); + + if ( (boot_options & OPTION_NOMOUNT) ) return; + err = do_mount (NULL, "none", "/dev", "devfs", 0, ""); + if (err == 0) printk ("Mounted devfs on /dev\n"); + else printk ("Warning: unable to mount devfs, err: %d\n", err); +} /* End Function mount_devfs_fs */ +#endif + +#ifdef MODULE +static void free_entry (struct devfs_entry *parent) +{ + struct devfs_entry *de, *next; + + if (parent == NULL) return; + for (de = parent->u.dir.first; de != NULL; de = next) + { + next = de->next; + if (de->first_inode != NULL) + { + printk ("%s: free_entry(): unfreed inodes!\n", DEVFS_NAME); + } + if ( S_ISDIR (de->mode) ) + { + /* Recursively free the subdirectories: this is a stack chomper */ + free_entry (de); + } + else kfree (de); + } + kfree (parent); +} /* End Function free_entry */ + +void cleanup_module (void) +{ + unregister_filesystem (&devfs_fs_type); + if (first_fs != NULL) + { + printk ("%s: cleanup_module(): still mounted mounted filesystems!\n", + DEVFS_NAME); + } + free_entry (root_entry); +} +#endif /* MODULE */ diff --git a/fs/devfs/util.c b/fs/devfs/util.c new file mode 100644 index 000000000..7e22ae7cb --- /dev/null +++ b/fs/devfs/util.c @@ -0,0 +1,160 @@ +/* devfs (Device FileSystem) utilities. + + Copyright (C) 1999-2000 Richard Gooch + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free + Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + Richard Gooch may be reached by email at rgooch@atnf.csiro.au + The postal address is: + Richard Gooch, c/o ATNF, P. O. Box 76, Epping, N.S.W., 2121, Australia. + + ChangeLog + + 19991031 Richard Gooch <rgooch@atnf.csiro.au> + Created. + 19991103 Richard Gooch <rgooch@atnf.csiro.au> + Created <_devfs_convert_name> and supported SCSI and IDE CD-ROMs + 20000203 Richard Gooch <rgooch@atnf.csiro.au> + Changed operations pointer type to void *. +*/ +#include <linux/module.h> +#include <linux/init.h> +#include <linux/locks.h> +#include <linux/kdev_t.h> +#include <linux/devfs_fs_kernel.h> + + +/* Private functions follow */ + +static void __init _devfs_convert_name (char *new, const char *old, int disc) +/* [SUMMARY] Convert from an old style location-based name to new style. + <new> The new name will be written here. + <old> The old name. + <disc> If true, disc partitioning information should be processed. + [RETURNS] Nothing. +*/ +{ + int host, bus, target, lun; + char *ptr; + char part[8]; + + /* Decode "c#b#t#u#" */ + if (old[0] != 'c') return; + host = simple_strtol (old + 1, &ptr, 10); + if (ptr[0] != 'b') return; + bus = simple_strtol (ptr + 1, &ptr, 10); + if (ptr[0] != 't') return; + target = simple_strtol (ptr + 1, &ptr, 10); + if (ptr[0] != 'u') return; + lun = simple_strtol (ptr + 1, &ptr, 10); + if (disc) + { + /* Decode "p#" */ + if (ptr[0] == 'p') sprintf (part, "part%s", ptr + 1); + else strcpy (part, "disc"); + } + else part[0] = '\0'; + sprintf (new, "/host%d/bus%d/target%d/lun%d/%s", + host, bus, target, lun, part); +} /* End Function _devfs_convert_name */ + + +/* Public functions follow */ + +/*PUBLIC_FUNCTION*/ +void __init devfs_make_root (const char *name) +/* [SUMMARY] Create the root FS device entry if required. + <name> The name of the root FS device, as passed by "root=". + [RETURNS] Nothing. +*/ +{ + char dest[64]; + + if ( (strncmp (name, "sd/", 3) == 0) || (strncmp (name, "sr/", 3) == 0) ) + { + strcpy (dest, "../scsi"); + _devfs_convert_name (dest + 7, name + 3, (name[1] == 'd') ? 1 : 0); + } + else if ( (strncmp (name, "ide/hd/", 7) == 0) || + (strncmp (name, "ide/cd/", 7) == 0) ) + { + strcpy (dest, ".."); + _devfs_convert_name (dest + 2, name + 7, (name[4] == 'h') ? 1 : 0); + } + else return; + devfs_mk_symlink (NULL, name, 0, DEVFS_FL_DEFAULT, dest, 0, NULL,NULL); +} /* End Function devfs_make_root */ + +/*PUBLIC_FUNCTION*/ +void devfs_register_tape (devfs_handle_t de) +/* [SUMMARY] Register a tape device in the "/dev/tapes" hierarchy. + <de> Any tape device entry in the device directory. + [RETURNS] Nothing. +*/ +{ + int pos; + devfs_handle_t parent, slave; + char name[16], dest[64]; + static unsigned int tape_counter = 0; + static devfs_handle_t tape_dir = NULL; + + if (tape_dir == NULL) tape_dir = devfs_mk_dir (NULL, "tapes", 5, NULL); + parent = devfs_get_parent (de); + pos = devfs_generate_path (parent, dest + 3, sizeof dest - 3); + if (pos < 0) return; + strncpy (dest + pos, "../", 3); + sprintf (name, "tape%u", tape_counter++); + devfs_mk_symlink (tape_dir, name, 0, DEVFS_FL_DEFAULT, dest + pos, 0, + &slave, NULL); + devfs_auto_unregister (de, slave); +} /* End Function devfs_register_tape */ +EXPORT_SYMBOL(devfs_register_tape); + +/*PUBLIC_FUNCTION*/ +void devfs_register_series (devfs_handle_t dir, const char *format, + unsigned int num_entries, unsigned int flags, + unsigned int major, unsigned int minor_start, + umode_t mode, uid_t uid, gid_t gid, + void *ops, void *info) +/* [SUMMARY] Register a sequence of device entries. + <dir> The handle to the parent devfs directory entry. If this is NULL the + new names are relative to the root of the devfs. + <format> The printf-style format string. A single "%u" is allowed. + <flags> A set of bitwise-ORed flags (DEVFS_FL_*). + <major> The major number. Not needed for regular files. + <minor_start> The starting minor number. Not needed for regular files. + <mode> The default file mode. + <uid> The default UID of the file. + <guid> The default GID of the file. + <ops> The <<file_operations>> or <<block_device_operations>> structure. + This must not be externally deallocated. + <info> An arbitrary pointer which will be written to the <<private_data>> + field of the <<file>> structure passed to the device driver. You can set + this to whatever you like, and change it once the file is opened (the next + file opened will not see this change). + [RETURNS] Nothing. +*/ +{ + unsigned int count; + char devname[128]; + + for (count = 0; count < num_entries; ++count) + { + sprintf (devname, format, count); + devfs_register (dir, devname, 0, flags, major, minor_start + count, + mode, uid, gid, ops, info); + } +} /* End Function devfs_register_series */ +EXPORT_SYMBOL(devfs_register_series); diff --git a/fs/ext2/inode.c b/fs/ext2/inode.c index ab7ac0a96..a520ab40f 100644 --- a/fs/ext2/inode.c +++ b/fs/ext2/inode.c @@ -35,7 +35,9 @@ static int ext2_update_inode(struct inode * inode, int do_sync); */ void ext2_put_inode (struct inode * inode) { + lock_kernel(); ext2_discard_prealloc (inode); + unlock_kernel(); } /* @@ -43,6 +45,8 @@ void ext2_put_inode (struct inode * inode) */ void ext2_delete_inode (struct inode * inode) { + lock_kernel(); + if (is_bad_inode(inode) || inode->i_ino == EXT2_ACL_IDX_INO || inode->i_ino == EXT2_ACL_DATA_INO) @@ -54,6 +58,8 @@ void ext2_delete_inode (struct inode * inode) if (inode->i_blocks) ext2_truncate (inode); ext2_free_inode (inode); + + unlock_kernel(); } #define inode_bmap(inode, nr) (le32_to_cpu((inode)->u.ext2_i.i_data[(nr)])) @@ -893,7 +899,9 @@ static int ext2_update_inode(struct inode * inode, int do_sync) void ext2_write_inode (struct inode * inode) { + lock_kernel(); ext2_update_inode (inode, 0); + unlock_kernel(); } int ext2_sync_inode (struct inode *inode) diff --git a/fs/fat/cache.c b/fs/fat/cache.c index 175182959..038773b94 100644 --- a/fs/fat/cache.c +++ b/fs/fat/cache.c @@ -283,7 +283,7 @@ int default_fat_bmap(struct inode *inode,int sector) return 0; return sector+sb->dir_start; } - if (sector >= MSDOS_I(inode)->mmu_private>>9) + if (sector >= (MSDOS_I(inode)->mmu_private+511)>>9) return 0; cluster = sector/sb->cluster_size; offset = sector % sb->cluster_size; diff --git a/fs/filesystems.c b/fs/filesystems.c index 10c8f8bd3..d0a446c6e 100644 --- a/fs/filesystems.c +++ b/fs/filesystems.c @@ -14,6 +14,7 @@ #include <linux/msdos_fs.h> #include <linux/umsdos_fs.h> #include <linux/proc_fs.h> +#include <linux/devfs_fs_kernel.h> #include <linux/nfs_fs.h> #include <linux/iso_fs.h> #include <linux/sysv_fs.h> @@ -86,6 +87,8 @@ void __init filesystem_setup(void) init_proc_fs(); #endif + init_devfs_fs(); /* Header file may make this empty */ + #ifdef CONFIG_LOCKD nlmxdr_init(); #endif diff --git a/fs/hpfs/namei.c b/fs/hpfs/namei.c index 3ca25fedf..663817085 100644 --- a/fs/hpfs/namei.c +++ b/fs/hpfs/namei.c @@ -329,21 +329,18 @@ int hpfs_unlink(struct inode *dir, struct dentry *dentry) d_delete(dentry); } else { /* no space for deleting, try to truncate file */ struct iattr newattrs; + int err; hpfs_unlock_2inodes(dir, inode); if (rep || dentry->d_count > 1 || permission(inode, MAY_WRITE) || get_write_access(inode)) goto ret; /*printk("HPFS: truncating file before delete.\n");*/ - down(&inode->i_sem); /* do_truncate should be called here, but it's */ - newattrs.ia_size = 0; /* not exported */ + down(&inode->i_sem); + newattrs.ia_size = 0; newattrs.ia_valid = ATTR_SIZE | ATTR_CTIME; - if (notify_change(dentry, &newattrs)) { - up(&inode->i_sem); - put_write_access(inode); - goto ret; - } - vmtruncate(inode, 0); - if (inode->i_op && inode->i_op->truncate) inode->i_op->truncate(inode); + err = notify_change(dentry, &newattrs); up(&inode->i_sem); put_write_access(inode); + if (err) + goto ret; rep = 1; goto again; } diff --git a/fs/inode.c b/fs/inode.c index ccc1a62f3..e6cb6ef59 100644 --- a/fs/inode.c +++ b/fs/inode.c @@ -94,7 +94,7 @@ static void init_once(void * foo, kmem_cache_t * cachep, unsigned long flags) INIT_LIST_HEAD(&inode->i_data.pages); INIT_LIST_HEAD(&inode->i_dentry); sema_init(&inode->i_sem, 1); - spin_lock_init(&inode->i_shared_lock); + spin_lock_init(&inode->i_data.i_shared_lock); } } @@ -156,11 +156,26 @@ static inline void write_inode(struct inode *inode) inode->i_sb->s_op->write_inode(inode); } +static inline void __iget(struct inode * inode) +{ + if (!inode->i_count++) + { + if (!(inode->i_state & I_DIRTY)) + { + list_del(&inode->i_list); + list_add(&inode->i_list, &inode_in_use); + } + inodes_stat.nr_unused--; + } +} + static inline void sync_one(struct inode *inode) { if (inode->i_state & I_LOCK) { + __iget(inode); spin_unlock(&inode_lock); __wait_on_inode(inode); + iput(inode); spin_lock(&inode_lock); } else { list_del(&inode->i_list); @@ -253,6 +268,8 @@ void clear_inode(struct inode *inode) BUG(); if (!(inode->i_state & I_FREEING)) BUG(); + if (inode->i_state & I_CLEAR) + BUG(); wait_on_inode(inode); if (IS_QUOTAINIT(inode)) DQUOT_DROP(inode); @@ -262,7 +279,7 @@ void clear_inode(struct inode *inode) bdput(inode->i_bdev); inode->i_bdev = NULL; } - inode->i_state = 0; + inode->i_state = I_CLEAR; } /* @@ -280,7 +297,7 @@ static void dispose_list(struct list_head * head) inode = list_entry(inode_entry, struct inode, i_list); if (inode->i_data.nrpages) - truncate_inode_pages(inode, 0); + truncate_inode_pages(&inode->i_data, 0); clear_inode(inode); destroy_inode(inode); } @@ -377,6 +394,8 @@ void prune_icache(int goal) entry = entry->prev; inode = INODE(tmp); + if (inode->i_state & (I_FREEING|I_CLEAR)) + BUG(); if (!CAN_UNUSE(inode)) continue; if (inode->i_count) @@ -413,19 +432,6 @@ int shrink_icache_memory(int priority, int gfp_mask, zone_t *zone) return 0; } -static inline void __iget(struct inode * inode) -{ - if (!inode->i_count++) - { - if (!(inode->i_state & I_DIRTY)) - { - list_del(&inode->i_list); - list_add(&inode->i_list, &inode_in_use); - } - inodes_stat.nr_unused--; - } -} - /* * Called with the inode lock held. * NOTE: we are not increasing the inode-refcount, you must call __iget() @@ -464,6 +470,7 @@ static struct inode * find_inode(struct super_block * sb, unsigned long ino, str */ static void clean_inode(struct inode *inode) { + static struct address_space_operations empty_aops = {}; memset(&inode->u, 0, sizeof(inode->u)); inode->i_sock = 0; inode->i_op = NULL; @@ -474,8 +481,9 @@ static void clean_inode(struct inode *inode) memset(&inode->i_dquot, 0, sizeof(inode->i_dquot)); inode->i_pipe = NULL; inode->i_bdev = NULL; + inode->i_data.a_ops = &empty_aops; + inode->i_data.host = (void*)inode; inode->i_mapping = &inode->i_data; - inode->i_mapping->host = (void*)inode; } /* @@ -601,6 +609,11 @@ struct inode *igrab(struct inode *inode) if (!(inode->i_state & I_FREEING)) __iget(inode); else + /* + * Handle the case where s_op->clear_inode is not been + * called yet, and somebody is calling igrab + * while the inode is getting freed. + */ inode = NULL; spin_unlock(&inode_lock); if (inode) @@ -667,32 +680,39 @@ void iput(struct inode *inode) list_del(&inode->i_list); INIT_LIST_HEAD(&inode->i_list); inode->i_state|=I_FREEING; + spin_unlock(&inode_lock); + + destroy = 1; if (op && op->delete_inode) { void (*delete)(struct inode *) = op->delete_inode; - spin_unlock(&inode_lock); if (inode->i_data.nrpages) - truncate_inode_pages(inode, 0); + truncate_inode_pages(&inode->i_data, 0); + /* s_op->delete_inode internally recalls clear_inode() */ delete(inode); - spin_lock(&inode_lock); - } - } - if (list_empty(&inode->i_hash)) { - list_del(&inode->i_list); - INIT_LIST_HEAD(&inode->i_list); - inode->i_state|=I_FREEING; - spin_unlock(&inode_lock); - clear_inode(inode); - destroy = 1; + } else + clear_inode(inode); + if (inode->i_state != I_CLEAR) + BUG(); + spin_lock(&inode_lock); - } - else - { - if (!(inode->i_state & I_DIRTY)) { + } else { + if (!list_empty(&inode->i_hash)) { + if (!(inode->i_state & I_DIRTY)) { + list_del(&inode->i_list); + list_add(&inode->i_list, + &inode_unused); + } + inodes_stat.nr_unused++; + } else { + /* magic nfs path */ list_del(&inode->i_list); - list_add(&inode->i_list, - &inode_unused); + INIT_LIST_HEAD(&inode->i_list); + inode->i_state|=I_FREEING; + spin_unlock(&inode_lock); + clear_inode(inode); + destroy = 1; + spin_lock(&inode_lock); } - inodes_stat.nr_unused++; } #ifdef INODE_PARANOIA if (inode->i_flock) diff --git a/fs/lockd/Makefile b/fs/lockd/Makefile index 7c319ffc3..ceab1f466 100644 --- a/fs/lockd/Makefile +++ b/fs/lockd/Makefile @@ -10,6 +10,11 @@ O_TARGET := lockd.o O_OBJS := clntlock.o clntproc.o host.o svc.o svclock.o svcshare.o \ svcproc.o svcsubs.o mon.o xdr.o + +ifdef CONFIG_LOCKD_V4 + O_OBJS += xdr4.o svc4proc.o +endif + OX_OBJS := lockd_syms.o M_OBJS := $(O_TARGET) diff --git a/fs/lockd/clntproc.c b/fs/lockd/clntproc.c index 4160bf2e3..517456326 100644 --- a/fs/lockd/clntproc.c +++ b/fs/lockd/clntproc.c @@ -69,7 +69,7 @@ nlmclnt_setgrantargs(struct nlm_rqst *call, struct nlm_lock *lock) call->a_args.lock = *lock; call->a_args.lock.caller = system_utsname.nodename; - init_waitqueue_head(&lock->fl.fl_wait); + init_waitqueue_head(&call->a_args.lock.fl.fl_wait); /* set default data area */ call->a_args.lock.oh.data = call->a_owner; diff --git a/fs/lockd/lockd_syms.c b/fs/lockd/lockd_syms.c index b3990ed98..db36300ba 100644 --- a/fs/lockd/lockd_syms.c +++ b/fs/lockd/lockd_syms.c @@ -39,4 +39,15 @@ EXPORT_SYMBOL(nlmsvc_ops); EXPORT_SYMBOL(nlmsvc_grace_period); EXPORT_SYMBOL(nlmsvc_timeout); +#ifdef CONFIG_LOCKD_V4 + +/* NLM4 exported symbols */ +EXPORT_SYMBOL(nlm4_rofs); +EXPORT_SYMBOL(nlm4_stale_fh); +EXPORT_SYMBOL(nlm4_deadlock); +EXPORT_SYMBOL(nlm4_failed); +EXPORT_SYMBOL(nlm4_fbig); + +#endif + #endif /* CONFIG_MODULES */ diff --git a/fs/lockd/svc.c b/fs/lockd/svc.c index e22007445..62401adbd 100644 --- a/fs/lockd/svc.c +++ b/fs/lockd/svc.c @@ -343,7 +343,7 @@ static struct svc_version nlmsvc_version1 = { static struct svc_version nlmsvc_version3 = { 3, 24, nlmsvc_procedures, NULL }; -#ifdef CONFIG_NFSD_NFS3 +#ifdef CONFIG_LOCKD_V4 static struct svc_version nlmsvc_version4 = { 4, 24, nlmsvc_procedures4, NULL }; @@ -353,7 +353,7 @@ static struct svc_version * nlmsvc_version[] = { &nlmsvc_version1, NULL, &nlmsvc_version3, -#ifdef CONFIG_NFSD_NFS3 +#ifdef CONFIG_LOCKD_V4 &nlmsvc_version4, #endif }; diff --git a/fs/lockd/svc4proc.c b/fs/lockd/svc4proc.c new file mode 100644 index 000000000..b690eb97e --- /dev/null +++ b/fs/lockd/svc4proc.c @@ -0,0 +1,561 @@ +/* + * linux/fs/lockd/svc4proc.c + * + * Lockd server procedures. We don't implement the NLM_*_RES + * procedures because we don't use the async procedures. + * + * Copyright (C) 1996, Olaf Kirch <okir@monad.swb.de> + */ + +#include <linux/types.h> +#include <linux/sched.h> +#include <linux/malloc.h> +#include <linux/in.h> +#include <linux/sunrpc/svc.h> +#include <linux/sunrpc/clnt.h> +#include <linux/nfsd/nfsd.h> +#include <linux/lockd/lockd.h> +#include <linux/lockd/share.h> +#include <linux/lockd/sm_inter.h> + + +#define NLMDBG_FACILITY NLMDBG_CLIENT + +static u32 nlm4svc_callback(struct svc_rqst *, u32, struct nlm_res *); +static void nlm4svc_callback_exit(struct rpc_task *); + +/* + * Obtain client and file from arguments + */ +static u32 +nlm4svc_retrieve_args(struct svc_rqst *rqstp, struct nlm_args *argp, + struct nlm_host **hostp, struct nlm_file **filp) +{ + struct nlm_host *host = NULL; + struct nlm_file *file = NULL; + struct nlm_lock *lock = &argp->lock; + u32 error = 0; + + /* nfsd callbacks must have been installed for this procedure */ + if (!nlmsvc_ops) + return nlm_lck_denied_nolocks; + + /* Obtain handle for client host */ + if (rqstp->rq_client == NULL) { + printk(KERN_NOTICE + "lockd: unauthenticated request from (%08x:%d)\n", + ntohl(rqstp->rq_addr.sin_addr.s_addr), + ntohs(rqstp->rq_addr.sin_port)); + return nlm_lck_denied_nolocks; + } + + /* Obtain host handle */ + if (!(host = nlmsvc_lookup_host(rqstp)) + || (argp->monitor && !host->h_monitored && nsm_monitor(host) < 0)) + goto no_locks; + *hostp = host; + + /* Obtain file pointer. Not used by FREE_ALL call. */ + if (filp != NULL) { + if ((error = nlm_lookup_file(rqstp, &file, &lock->fh)) != 0) + goto no_locks; + *filp = file; + + /* Set up the missing parts of the file_lock structure */ + lock->fl.fl_file = &file->f_file; + lock->fl.fl_owner = (fl_owner_t) host; + } + + return 0; + +no_locks: + if (host) + nlm_release_host(host); + if (error) + return error; + return nlm_lck_denied_nolocks; +} + +/* + * NULL: Test for presence of service + */ +static int +nlm4svc_proc_null(struct svc_rqst *rqstp, void *argp, void *resp) +{ + dprintk("lockd: NULL called\n"); + return rpc_success; +} + +/* + * TEST: Check for conflicting lock + */ +static int +nlm4svc_proc_test(struct svc_rqst *rqstp, struct nlm_args *argp, + struct nlm_res *resp) +{ + struct nlm_host *host; + struct nlm_file *file; + + dprintk("lockd: TEST4 called\n"); + resp->cookie = argp->cookie; + + /* Don't accept test requests during grace period */ + if (nlmsvc_grace_period) { + resp->status = nlm_lck_denied_grace_period; + return rpc_success; + } + + /* Obtain client and file */ + if ((resp->status = nlm4svc_retrieve_args(rqstp, argp, &host, &file))) + return rpc_success; + + /* Now check for conflicting locks */ + resp->status = nlmsvc_testlock(file, &argp->lock, &resp->lock); + + dprintk("lockd: TEST4 status %d\n", ntohl(resp->status)); + nlm_release_host(host); + nlm_release_file(file); + return rpc_success; +} + +static int +nlm4svc_proc_lock(struct svc_rqst *rqstp, struct nlm_args *argp, + struct nlm_res *resp) +{ + struct nlm_host *host; + struct nlm_file *file; + + dprintk("lockd: LOCK called\n"); + + resp->cookie = argp->cookie; + + /* Don't accept new lock requests during grace period */ + if (nlmsvc_grace_period && !argp->reclaim) { + resp->status = nlm_lck_denied_grace_period; + return rpc_success; + } + + /* Obtain client and file */ + if ((resp->status = nlm4svc_retrieve_args(rqstp, argp, &host, &file))) + return rpc_success; + +#if 0 + /* If supplied state doesn't match current state, we assume it's + * an old request that time-warped somehow. Any error return would + * do in this case because it's irrelevant anyway. + * + * NB: We don't retrieve the remote host's state yet. + */ + if (host->h_nsmstate && host->h_nsmstate != argp->state) { + resp->status = nlm_lck_denied_nolocks; + } else +#endif + + /* Now try to lock the file */ + resp->status = nlmsvc_lock(rqstp, file, &argp->lock, + argp->block, &argp->cookie); + + dprintk("lockd: LOCK status %d\n", ntohl(resp->status)); + nlm_release_host(host); + nlm_release_file(file); + return rpc_success; +} + +static int +nlm4svc_proc_cancel(struct svc_rqst *rqstp, struct nlm_args *argp, + struct nlm_res *resp) +{ + struct nlm_host *host; + struct nlm_file *file; + + dprintk("lockd: CANCEL called\n"); + + resp->cookie = argp->cookie; + + /* Don't accept requests during grace period */ + if (nlmsvc_grace_period) { + resp->status = nlm_lck_denied_grace_period; + return rpc_success; + } + + /* Obtain client and file */ + if ((resp->status = nlm4svc_retrieve_args(rqstp, argp, &host, &file))) + return rpc_success; + + /* Try to cancel request. */ + resp->status = nlmsvc_cancel_blocked(file, &argp->lock); + + dprintk("lockd: CANCEL status %d\n", ntohl(resp->status)); + nlm_release_host(host); + nlm_release_file(file); + return rpc_success; +} + +/* + * UNLOCK: release a lock + */ +static int +nlm4svc_proc_unlock(struct svc_rqst *rqstp, struct nlm_args *argp, + struct nlm_res *resp) +{ + struct nlm_host *host; + struct nlm_file *file; + + dprintk("lockd: UNLOCK called\n"); + + resp->cookie = argp->cookie; + + /* Don't accept new lock requests during grace period */ + if (nlmsvc_grace_period) { + resp->status = nlm_lck_denied_grace_period; + return rpc_success; + } + + /* Obtain client and file */ + if ((resp->status = nlm4svc_retrieve_args(rqstp, argp, &host, &file))) + return rpc_success; + + /* Now try to remove the lock */ + resp->status = nlmsvc_unlock(file, &argp->lock); + + dprintk("lockd: UNLOCK status %d\n", ntohl(resp->status)); + nlm_release_host(host); + nlm_release_file(file); + return rpc_success; +} + +/* + * GRANTED: A server calls us to tell that a process' lock request + * was granted + */ +static int +nlm4svc_proc_granted(struct svc_rqst *rqstp, struct nlm_args *argp, + struct nlm_res *resp) +{ + resp->cookie = argp->cookie; + + dprintk("lockd: GRANTED called\n"); + resp->status = nlmclnt_grant(&argp->lock); + dprintk("lockd: GRANTED status %d\n", ntohl(resp->status)); + return rpc_success; +} + +/* + * `Async' versions of the above service routines. They aren't really, + * because we send the callback before the reply proper. I hope this + * doesn't break any clients. + */ +static int +nlm4svc_proc_test_msg(struct svc_rqst *rqstp, struct nlm_args *argp, + void *resp) +{ + struct nlm_res res; + u32 stat; + + dprintk("lockd: TEST_MSG called\n"); + + if ((stat = nlm4svc_proc_test(rqstp, argp, &res)) == 0) + stat = nlm4svc_callback(rqstp, NLMPROC_TEST_RES, &res); + return stat; +} + +static int +nlm4svc_proc_lock_msg(struct svc_rqst *rqstp, struct nlm_args *argp, + void *resp) +{ + struct nlm_res res; + u32 stat; + + dprintk("lockd: LOCK_MSG called\n"); + + if ((stat = nlm4svc_proc_lock(rqstp, argp, &res)) == 0) + stat = nlm4svc_callback(rqstp, NLMPROC_LOCK_RES, &res); + return stat; +} + +static int +nlm4svc_proc_cancel_msg(struct svc_rqst *rqstp, struct nlm_args *argp, + void *resp) +{ + struct nlm_res res; + u32 stat; + + dprintk("lockd: CANCEL_MSG called\n"); + + if ((stat = nlm4svc_proc_cancel(rqstp, argp, &res)) == 0) + stat = nlm4svc_callback(rqstp, NLMPROC_CANCEL_RES, &res); + return stat; +} + +static int +nlm4svc_proc_unlock_msg(struct svc_rqst *rqstp, struct nlm_args *argp, + void *resp) +{ + struct nlm_res res; + u32 stat; + + dprintk("lockd: UNLOCK_MSG called\n"); + + if ((stat = nlm4svc_proc_unlock(rqstp, argp, &res)) == 0) + stat = nlm4svc_callback(rqstp, NLMPROC_UNLOCK_RES, &res); + return stat; +} + +static int +nlm4svc_proc_granted_msg(struct svc_rqst *rqstp, struct nlm_args *argp, + void *resp) +{ + struct nlm_res res; + u32 stat; + + dprintk("lockd: GRANTED_MSG called\n"); + + if ((stat = nlm4svc_proc_granted(rqstp, argp, &res)) == 0) + stat = nlm4svc_callback(rqstp, NLMPROC_GRANTED_RES, &res); + return stat; +} + +/* + * SHARE: create a DOS share or alter existing share. + */ +static int +nlm4svc_proc_share(struct svc_rqst *rqstp, struct nlm_args *argp, + struct nlm_res *resp) +{ + struct nlm_host *host; + struct nlm_file *file; + + dprintk("lockd: SHARE called\n"); + + resp->cookie = argp->cookie; + + /* Don't accept new lock requests during grace period */ + if (nlmsvc_grace_period && !argp->reclaim) { + resp->status = nlm_lck_denied_grace_period; + return rpc_success; + } + + /* Obtain client and file */ + if ((resp->status = nlm4svc_retrieve_args(rqstp, argp, &host, &file))) + return rpc_success; + + /* Now try to create the share */ + resp->status = nlmsvc_share_file(host, file, argp); + + dprintk("lockd: SHARE status %d\n", ntohl(resp->status)); + nlm_release_host(host); + nlm_release_file(file); + return rpc_success; +} + +/* + * UNSHARE: Release a DOS share. + */ +static int +nlm4svc_proc_unshare(struct svc_rqst *rqstp, struct nlm_args *argp, + struct nlm_res *resp) +{ + struct nlm_host *host; + struct nlm_file *file; + + dprintk("lockd: UNSHARE called\n"); + + resp->cookie = argp->cookie; + + /* Don't accept requests during grace period */ + if (nlmsvc_grace_period) { + resp->status = nlm_lck_denied_grace_period; + return rpc_success; + } + + /* Obtain client and file */ + if ((resp->status = nlm4svc_retrieve_args(rqstp, argp, &host, &file))) + return rpc_success; + + /* Now try to lock the file */ + resp->status = nlmsvc_unshare_file(host, file, argp); + + dprintk("lockd: UNSHARE status %d\n", ntohl(resp->status)); + nlm_release_host(host); + nlm_release_file(file); + return rpc_success; +} + +/* + * NM_LOCK: Create an unmonitored lock + */ +static int +nlm4svc_proc_nm_lock(struct svc_rqst *rqstp, struct nlm_args *argp, + struct nlm_res *resp) +{ + dprintk("lockd: NM_LOCK called\n"); + + argp->monitor = 0; /* just clean the monitor flag */ + return nlm4svc_proc_lock(rqstp, argp, resp); +} + +/* + * FREE_ALL: Release all locks and shares held by client + */ +static int +nlm4svc_proc_free_all(struct svc_rqst *rqstp, struct nlm_args *argp, + void *resp) +{ + struct nlm_host *host; + + /* Obtain client */ + if (nlm4svc_retrieve_args(rqstp, argp, &host, NULL)) + return rpc_success; + + nlmsvc_free_host_resources(host); + nlm_release_host(host); + return rpc_success; +} + +/* + * SM_NOTIFY: private callback from statd (not part of official NLM proto) + */ +static int +nlm4svc_proc_sm_notify(struct svc_rqst *rqstp, struct nlm_reboot *argp, + void *resp) +{ + struct sockaddr_in saddr = rqstp->rq_addr; + struct nlm_host *host; + + dprintk("lockd: SM_NOTIFY called\n"); + if (saddr.sin_addr.s_addr != htonl(INADDR_LOOPBACK) + || ntohs(saddr.sin_port) >= 1024) { + printk(KERN_WARNING + "lockd: rejected NSM callback from %08x:%d\n", + ntohl(rqstp->rq_addr.sin_addr.s_addr), + ntohs(rqstp->rq_addr.sin_port)); + return rpc_system_err; + } + + /* Obtain the host pointer for this NFS server and try to + * reclaim all locks we hold on this server. + */ + saddr.sin_addr.s_addr = argp->addr; + if ((host = nlm_lookup_host(NULL, &saddr, IPPROTO_UDP, 1)) != NULL) { + nlmclnt_recovery(host, argp->state); + nlm_release_host(host); + } + + /* If we run on an NFS server, delete all locks held by the client */ + if (nlmsvc_ops != NULL) { + struct svc_client *clnt; + saddr.sin_addr.s_addr = argp->addr; + if ((clnt = nlmsvc_ops->exp_getclient(&saddr)) != NULL + && (host = nlm_lookup_host(clnt, &saddr, 0, 0)) != NULL) { + nlmsvc_free_host_resources(host); + } + nlm_release_host(host); + } + + return rpc_success; +} + +/* + * This is the generic lockd callback for async RPC calls + */ +static u32 +nlm4svc_callback(struct svc_rqst *rqstp, u32 proc, struct nlm_res *resp) +{ + struct nlm_host *host; + struct nlm_rqst *call; + + if (!(call = nlmclnt_alloc_call())) + return rpc_system_err; + + host = nlmclnt_lookup_host(&rqstp->rq_addr, + rqstp->rq_prot, rqstp->rq_vers); + if (!host) { + rpc_free(call); + return rpc_system_err; + } + + call->a_flags = RPC_TASK_ASYNC; + call->a_host = host; + memcpy(&call->a_args, resp, sizeof(*resp)); + +/* FIXME this should become nlmSVC_async_call when that code gets + merged in XXX */ + if (nlmclnt_async_call(call, proc, nlm4svc_callback_exit) < 0) + return rpc_system_err; + + return rpc_success; +} + +static void +nlm4svc_callback_exit(struct rpc_task *task) +{ + struct nlm_rqst *call = (struct nlm_rqst *) task->tk_calldata; + + if (task->tk_status < 0) { + dprintk("lockd: %4d callback failed (errno = %d)\n", + task->tk_pid, -task->tk_status); + } + nlm_release_host(call->a_host); + rpc_free(call); +} + +/* + * NLM Server procedures. + */ + +#define nlm4svc_encode_norep nlm4svc_encode_void +#define nlm4svc_decode_norep nlm4svc_decode_void +#define nlm4svc_decode_testres nlm4svc_decode_void +#define nlm4svc_decode_lockres nlm4svc_decode_void +#define nlm4svc_decode_unlockres nlm4svc_decode_void +#define nlm4svc_decode_cancelres nlm4svc_decode_void +#define nlm4svc_decode_grantedres nlm4svc_decode_void + +#define nlm4svc_proc_none nlm4svc_proc_null +#define nlm4svc_proc_test_res nlm4svc_proc_null +#define nlm4svc_proc_lock_res nlm4svc_proc_null +#define nlm4svc_proc_cancel_res nlm4svc_proc_null +#define nlm4svc_proc_unlock_res nlm4svc_proc_null +#define nlm4svc_proc_granted_res nlm4svc_proc_null + +struct nlm_void { int dummy; }; + +#define PROC(name, xargt, xrest, argt, rest) \ + { (svc_procfunc) nlm4svc_proc_##name, \ + (kxdrproc_t) nlm4svc_decode_##xargt, \ + (kxdrproc_t) nlm4svc_encode_##xrest, \ + NULL, \ + sizeof(struct nlm_##argt), \ + sizeof(struct nlm_##rest), \ + 0, \ + 0 \ + } +struct svc_procedure nlmsvc_procedures4[] = { + PROC(null, void, void, void, void), + PROC(test, testargs, testres, args, res), + PROC(lock, lockargs, res, args, res), + PROC(cancel, cancargs, res, args, res), + PROC(unlock, unlockargs, res, args, res), + PROC(granted, testargs, res, args, res), + PROC(test_msg, testargs, norep, args, void), + PROC(lock_msg, lockargs, norep, args, void), + PROC(cancel_msg, cancargs, norep, args, void), + PROC(unlock_msg, unlockargs, norep, args, void), + PROC(granted_msg, testargs, norep, args, void), + PROC(test_res, testres, norep, res, void), + PROC(lock_res, lockres, norep, res, void), + PROC(cancel_res, cancelres, norep, res, void), + PROC(unlock_res, unlockres, norep, res, void), + PROC(granted_res, grantedres, norep, res, void), + PROC(none, void, void, void, void), + PROC(none, void, void, void, void), + PROC(none, void, void, void, void), + PROC(none, void, void, void, void), + PROC(share, shareargs, shareres, args, res), + PROC(unshare, shareargs, shareres, args, res), + PROC(nm_lock, lockargs, res, args, res), + PROC(free_all, notify, void, args, void), + + /* statd callback */ + PROC(sm_notify, reboot, void, reboot, void), +}; diff --git a/fs/lockd/svclock.c b/fs/lockd/svclock.c index 73caf6349..1bed616cb 100644 --- a/fs/lockd/svclock.c +++ b/fs/lockd/svclock.c @@ -20,6 +20,7 @@ * Copyright (C) 1996, Olaf Kirch <okir@monad.swb.de> */ +#include <linux/config.h> #include <linux/types.h> #include <linux/errno.h> #include <linux/kernel.h> @@ -98,9 +99,10 @@ nlmsvc_lookup_block(struct nlm_file *file, struct nlm_lock *lock, int remove) lock->fl.fl_end, lock->fl.fl_type); for (head = &nlm_blocked; (block = *head); head = &block->b_next) { fl = &block->b_call.a_args.lock.fl; - dprintk(" check f=%p pd=%d %ld-%ld ty=%d\n", + dprintk("lockd: check f=%p pd=%d %ld-%ld ty=%d cookie=%x\n", block->b_file, fl->fl_pid, fl->fl_start, - fl->fl_end, fl->fl_type); + fl->fl_end, fl->fl_type, + *(unsigned int*)(block->b_call.a_args.cookie.data)); if (block->b_file == file && nlm_compare_locks(fl, &lock->fl)) { if (remove) *head = block->b_next; @@ -129,6 +131,8 @@ nlmsvc_find_block(struct nlm_cookie *cookie) struct nlm_block *block; for (block = nlm_blocked; block; block = block->b_next) { + dprintk("cookie: head of blocked queue %p, block %p\n", + nlm_blocked, block); if (nlm_cookie_match(&block->b_call.a_args.cookie,cookie)) break; } @@ -310,7 +314,13 @@ again: switch(-error) { case 0: return nlm_granted; - case EDEADLK: /* no applicable NLM status */ + case EDEADLK: +#ifdef CONFIG_LOCKD_V4 + return nlm4_deadlock; /* will be downgraded to lck_deined if this + * is a NLMv1,3 request */ +#else + /* no applicable NLM status */ +#endif case EAGAIN: return nlm_lck_denied; default: /* includes ENOLCK */ @@ -541,6 +551,8 @@ nlmsvc_grant_callback(struct rpc_task *task) unsigned long timeout; dprintk("lockd: GRANT_MSG RPC callback\n"); + dprintk("callback: looking for cookie %x \n", + *(unsigned int *)(call->a_args.cookie.data)); if (!(block = nlmsvc_find_block(&call->a_args.cookie))) { dprintk("lockd: no block for cookie %x\n", *(u32 *)(call->a_args.cookie.data)); return; @@ -617,7 +629,7 @@ nlmsvc_retry_blocked(void) dprintk("nlmsvc_retry_blocked(%p, when=%ld)\n", nlm_blocked, nlm_blocked? nlm_blocked->b_when : 0); - while ((block = nlm_blocked) && block->b_when < jiffies) { + while ((block = nlm_blocked) && block->b_when <= jiffies) { dprintk("nlmsvc_retry_blocked(%p, when=%ld, done=%d)\n", block, block->b_when, block->b_done); if (block->b_done) diff --git a/fs/lockd/svcproc.c b/fs/lockd/svcproc.c index 142d593b2..c92358dec 100644 --- a/fs/lockd/svcproc.c +++ b/fs/lockd/svcproc.c @@ -7,6 +7,7 @@ * Copyright (C) 1996, Olaf Kirch <okir@monad.swb.de> */ +#include <linux/config.h> #include <linux/types.h> #include <linux/sched.h> #include <linux/malloc.h> @@ -24,6 +25,31 @@ static u32 nlmsvc_callback(struct svc_rqst *, u32, struct nlm_res *); static void nlmsvc_callback_exit(struct rpc_task *); +#ifdef CONFIG_LOCKD_V4 +static u32 +cast_to_nlm(u32 status, u32 vers) +{ + + if (vers != 4){ + switch(ntohl(status)){ + case NLM_LCK_GRANTED: + case NLM_LCK_DENIED: + case NLM_LCK_DENIED_NOLOCKS: + case NLM_LCK_BLOCKED: + case NLM_LCK_DENIED_GRACE_PERIOD: + break; + default: + status = NLM_LCK_DENIED_NOLOCKS; + } + } + + return (status); +} +#define cast_status(status) (cast_to_nlm(status, rqstp->rq_vers)) +#else +#define cast_status(status) (status) +#endif + /* * Obtain client and file from arguments */ @@ -108,9 +134,10 @@ nlmsvc_proc_test(struct svc_rqst *rqstp, struct nlm_args *argp, return rpc_success; /* Now check for conflicting locks */ - resp->status = nlmsvc_testlock(file, &argp->lock, &resp->lock); + resp->status = cast_status(nlmsvc_testlock(file, &argp->lock, &resp->lock)); - dprintk("lockd: TEST status %d\n", ntohl(resp->status)); + dprintk("lockd: TEST status %d vers %d\n", + ntohl(resp->status), rqstp->rq_vers); nlm_release_host(host); nlm_release_file(file); return rpc_success; @@ -150,8 +177,8 @@ nlmsvc_proc_lock(struct svc_rqst *rqstp, struct nlm_args *argp, #endif /* Now try to lock the file */ - resp->status = nlmsvc_lock(rqstp, file, &argp->lock, - argp->block, &argp->cookie); + resp->status = cast_status(nlmsvc_lock(rqstp, file, &argp->lock, + argp->block, &argp->cookie)); dprintk("lockd: LOCK status %d\n", ntohl(resp->status)); nlm_release_host(host); @@ -181,7 +208,7 @@ nlmsvc_proc_cancel(struct svc_rqst *rqstp, struct nlm_args *argp, return rpc_success; /* Try to cancel request. */ - resp->status = nlmsvc_cancel_blocked(file, &argp->lock); + resp->status = cast_status(nlmsvc_cancel_blocked(file, &argp->lock)); dprintk("lockd: CANCEL status %d\n", ntohl(resp->status)); nlm_release_host(host); @@ -214,7 +241,7 @@ nlmsvc_proc_unlock(struct svc_rqst *rqstp, struct nlm_args *argp, return rpc_success; /* Now try to remove the lock */ - resp->status = nlmsvc_unlock(file, &argp->lock); + resp->status = cast_status(nlmsvc_unlock(file, &argp->lock)); dprintk("lockd: UNLOCK status %d\n", ntohl(resp->status)); nlm_release_host(host); @@ -338,7 +365,7 @@ nlmsvc_proc_share(struct svc_rqst *rqstp, struct nlm_args *argp, return rpc_success; /* Now try to create the share */ - resp->status = nlmsvc_share_file(host, file, argp); + resp->status = cast_status(nlmsvc_share_file(host, file, argp)); dprintk("lockd: SHARE status %d\n", ntohl(resp->status)); nlm_release_host(host); @@ -370,8 +397,8 @@ nlmsvc_proc_unshare(struct svc_rqst *rqstp, struct nlm_args *argp, if ((resp->status = nlmsvc_retrieve_args(rqstp, argp, &host, &file))) return rpc_success; - /* Now try to lock the file */ - resp->status = nlmsvc_unshare_file(host, file, argp); + /* Now try to unshare the file */ + resp->status = cast_status(nlmsvc_unshare_file(host, file, argp)); dprintk("lockd: UNSHARE status %d\n", ntohl(resp->status)); nlm_release_host(host); diff --git a/fs/lockd/svcsubs.c b/fs/lockd/svcsubs.c index 8d15cba9a..b3489a88d 100644 --- a/fs/lockd/svcsubs.c +++ b/fs/lockd/svcsubs.c @@ -6,6 +6,7 @@ * Copyright (C) 1996, Olaf Kirch <okir@monad.swb.de> */ +#include <linux/config.h> #include <linux/types.h> #include <linux/string.h> #include <linux/sched.h> @@ -105,6 +106,11 @@ out_unlock: out_free: kfree(file); +#ifdef CONFIG_LOCKD_V4 + if (nfserr == 1) + nfserr = nlm4_stale_fh; + else +#endif nfserr = nlm_lck_denied; goto out_unlock; } diff --git a/fs/lockd/xdr.c b/fs/lockd/xdr.c index 0902eaa0d..c0797a16f 100644 --- a/fs/lockd/xdr.c +++ b/fs/lockd/xdr.c @@ -6,6 +6,7 @@ * Copyright (C) 1995, 1996 Olaf Kirch <okir@monad.swb.de> */ +#include <linux/config.h> #include <linux/types.h> #include <linux/sched.h> #include <linux/utsname.h> @@ -47,6 +48,14 @@ nlmxdr_init(void) nlm_lck_blocked = htonl(NLM_LCK_BLOCKED); nlm_lck_denied_grace_period = htonl(NLM_LCK_DENIED_GRACE_PERIOD); +#ifdef CONFIG_LOCKD_V4 + nlm4_deadlock = htonl(NLM_DEADLCK); + nlm4_rofs = htonl(NLM_ROFS); + nlm4_stale_fh = htonl(NLM_STALE_FH); + nlm4_fbig = htonl(NLM_FBIG); + nlm4_failed = htonl(NLM_FAILED); +#endif + inited = 1; } @@ -541,7 +550,8 @@ nlmclt_decode_res(struct rpc_rqst *req, u32 *p, struct nlm_res *resp) { "nlm_" #proc, \ (kxdrproc_t) nlmclt_encode_##argtype, \ (kxdrproc_t) nlmclt_decode_##restype, \ - MAX(NLM_##argtype##_sz, NLM_##restype##_sz) << 2 \ + MAX(NLM_##argtype##_sz, NLM_##restype##_sz) << 2, \ + 0 \ } static struct rpc_procinfo nlm_procedures[] = { @@ -586,11 +596,18 @@ static struct rpc_version nlm_version3 = { 3, 24, nlm_procedures, }; +#ifdef CONFIG_LOCKD_V4 +extern struct rpc_version nlm_version4; +#endif + static struct rpc_version * nlm_versions[] = { NULL, &nlm_version1, NULL, &nlm_version3, +#ifdef CONFIG_LOCKD_V4 + &nlm_version4, +#endif }; static struct rpc_stat nlm_stats; diff --git a/fs/lockd/xdr4.c b/fs/lockd/xdr4.c new file mode 100644 index 000000000..7cedcd849 --- /dev/null +++ b/fs/lockd/xdr4.c @@ -0,0 +1,601 @@ +/* + * linux/fs/lockd/xdr4.c + * + * XDR support for lockd and the lock client. + * + * Copyright (C) 1995, 1996 Olaf Kirch <okir@monad.swb.de> + * Copyright (C) 1999, Trond Myklebust <trond.myklebust@fys.uio.no> + */ + +#include <linux/types.h> +#include <linux/sched.h> +#include <linux/utsname.h> +#include <linux/nfs.h> + +#include <linux/sunrpc/xdr.h> +#include <linux/sunrpc/clnt.h> +#include <linux/sunrpc/svc.h> +#include <linux/sunrpc/stats.h> +#include <linux/lockd/lockd.h> +#include <linux/lockd/sm_inter.h> + +#define NLMDBG_FACILITY NLMDBG_XDR +#define NLM_MAXSTRLEN 1024 +#define OFFSET_MAX ((off_t)LONG_MAX) + +#define QUADLEN(len) (((len) + 3) >> 2) + +u32 nlm4_deadlock, nlm4_rofs, nlm4_stale_fh, nlm4_fbig, + nlm4_failed; + + +typedef struct nlm_args nlm_args; + +static inline off_t +size_to_off_t(__s64 size) +{ + size = (size > (__s64)LONG_MAX) ? (off_t)LONG_MAX : (off_t) size; + return (size < (__s64)-LONG_MAX) ? (off_t)-LONG_MAX : (off_t) size; +} + +/* + * XDR functions for basic NLM types + */ +static u32 * +nlm4_decode_cookie(u32 *p, struct nlm_cookie *c) +{ + unsigned int len; + + len = ntohl(*p++); + + if(len==0) + { + c->len=4; + memset(c->data, 0, 4); /* hockeypux brain damage */ + } + else if(len<=8) + { + c->len=len; + memcpy(c->data, p, len); + p+=(len+3)>>2; + } + else + { + printk(KERN_NOTICE + "lockd: bad cookie size %d (only cookies under 8 bytes are supported.)\n", len); + return NULL; + } + return p; +} + +static u32 * +nlm4_encode_cookie(u32 *p, struct nlm_cookie *c) +{ + *p++ = htonl(c->len); + memcpy(p, c->data, c->len); + p+=(c->len+3)>>2; + return p; +} + +static u32 * +nlm4_decode_fh(u32 *p, struct nfs_fh *f) +{ + memset(f->data, 0, sizeof(f->data)); +#ifdef NFS_MAXFHSIZE + f->size = ntohl(*p++); + if (f->size > NFS_MAXFHSIZE) { + printk(KERN_NOTICE + "lockd: bad fhandle size %x (should be %d)\n", + f->size, NFS_MAXFHSIZE); + return NULL; + } + memcpy(f->data, p, f->size); + return p + XDR_QUADLEN(f->size); +#else + if (ntohl(*p++) != NFS_FHSIZE) + return NULL; /* for now, all filehandles are 32 bytes */ + memcpy(f->data, p, NFS_FHSIZE); + return p + XDR_QUADLEN(NFS_FHSIZE); +#endif +} + +static u32 * +nlm4_encode_fh(u32 *p, struct nfs_fh *f) +{ +#ifdef NFS_MAXFHSIZE + *p++ = htonl(f->size); + memcpy(p, f->data, f->size); + return p + XDR_QUADLEN(f->size); +#else + *p++ = htonl(NFS_FHSIZE); + memcpy(p, f->data, NFS_FHSIZE); + return p + XDR_QUADLEN(NFS_FHSIZE); +#endif +} + +/* + * Encode and decode owner handle + */ +static u32 * +nlm4_decode_oh(u32 *p, struct xdr_netobj *oh) +{ + return xdr_decode_netobj(p, oh); +} + +static u32 * +nlm4_encode_oh(u32 *p, struct xdr_netobj *oh) +{ + return xdr_encode_netobj(p, oh); +} + +static u32 * +nlm4_decode_lock(u32 *p, struct nlm_lock *lock) +{ + struct file_lock *fl = &lock->fl; + __s64 len, start, end; + int tmp; + + if (!(p = xdr_decode_string(p, &lock->caller, &tmp, NLM_MAXSTRLEN)) + || !(p = nlm4_decode_fh(p, &lock->fh)) + || !(p = nlm4_decode_oh(p, &lock->oh))) + return NULL; + + memset(fl, 0, sizeof(*fl)); + fl->fl_owner = current->files; + fl->fl_pid = ntohl(*p++); + fl->fl_flags = FL_POSIX; + fl->fl_type = F_RDLCK; /* as good as anything else */ + p = xdr_decode_hyper(p, &start); + p = xdr_decode_hyper(p, &len); + end = start + len - 1; + + fl->fl_start = size_to_off_t(start); + fl->fl_end = size_to_off_t(end); + + if (len == 0 || fl->fl_end < 0) + fl->fl_end = OFFSET_MAX; + return p; +} + +/* + * Encode a lock as part of an NLM call + */ +static u32 * +nlm4_encode_lock(u32 *p, struct nlm_lock *lock) +{ + struct file_lock *fl = &lock->fl; + + if (!(p = xdr_encode_string(p, lock->caller)) + || !(p = nlm4_encode_fh(p, &lock->fh)) + || !(p = nlm4_encode_oh(p, &lock->oh))) + return NULL; + + *p++ = htonl(fl->fl_pid); + p = xdr_encode_hyper(p, fl->fl_start); + if (fl->fl_end == OFFSET_MAX) + p = xdr_encode_hyper(p, 0); + else + p = xdr_encode_hyper(p, fl->fl_end - fl->fl_start + 1); + + return p; +} + +/* + * Encode result of a TEST/TEST_MSG call + */ +static u32 * +nlm4_encode_testres(u32 *p, struct nlm_res *resp) +{ + dprintk("xdr: before encode_testres (p %p resp %p)\n", p, resp); + if (!(p = nlm4_encode_cookie(p, &resp->cookie))) + return 0; + *p++ = resp->status; + + if (resp->status == nlm_lck_denied) { + struct file_lock *fl = &resp->lock.fl; + + *p++ = (fl->fl_type == F_RDLCK)? xdr_zero : xdr_one; + *p++ = htonl(fl->fl_pid); + + /* Encode owner handle. */ + if (!(p = xdr_encode_netobj(p, &resp->lock.oh))) + return 0; + + p = xdr_encode_hyper(p, fl->fl_start); + if (fl->fl_end == OFFSET_MAX) + p = xdr_encode_hyper(p, 0); + else + p = xdr_encode_hyper(p, fl->fl_end - fl->fl_start + 1); + dprintk("xdr: encode_testres (status %d pid %d type %d start %ld end %ld)\n", resp->status, fl->fl_pid, fl->fl_type, fl->fl_start, fl->fl_end); + } + + dprintk("xdr: after encode_testres (p %p resp %p)\n", p, resp); + return p; +} + + +/* + * Check buffer bounds after decoding arguments + */ +static int +xdr_argsize_check(struct svc_rqst *rqstp, u32 *p) +{ + struct svc_buf *buf = &rqstp->rq_argbuf; + + return p - buf->base <= buf->buflen; +} + +static int +xdr_ressize_check(struct svc_rqst *rqstp, u32 *p) +{ + struct svc_buf *buf = &rqstp->rq_resbuf; + + buf->len = p - buf->base; + return (buf->len <= buf->buflen); +} + +/* + * First, the server side XDR functions + */ +int +nlm4svc_decode_testargs(struct svc_rqst *rqstp, u32 *p, nlm_args *argp) +{ + u32 exclusive; + + if (!(p = nlm4_decode_cookie(p, &argp->cookie))) + return 0; + + exclusive = ntohl(*p++); + if (!(p = nlm4_decode_lock(p, &argp->lock))) + return 0; + if (exclusive) + argp->lock.fl.fl_type = F_WRLCK; + + return xdr_argsize_check(rqstp, p); +} + +int +nlm4svc_encode_testres(struct svc_rqst *rqstp, u32 *p, struct nlm_res *resp) +{ + if (!(p = nlm4_encode_testres(p, resp))) + return 0; + return xdr_ressize_check(rqstp, p); +} + +int +nlm4svc_decode_lockargs(struct svc_rqst *rqstp, u32 *p, nlm_args *argp) +{ + u32 exclusive; + + if (!(p = nlm4_decode_cookie(p, &argp->cookie))) + return 0; + argp->block = ntohl(*p++); + exclusive = ntohl(*p++); + if (!(p = nlm4_decode_lock(p, &argp->lock))) + return 0; + if (exclusive) + argp->lock.fl.fl_type = F_WRLCK; + argp->reclaim = ntohl(*p++); + argp->state = ntohl(*p++); + argp->monitor = 1; /* monitor client by default */ + + return xdr_argsize_check(rqstp, p); +} + +int +nlm4svc_decode_cancargs(struct svc_rqst *rqstp, u32 *p, nlm_args *argp) +{ + u32 exclusive; + + if (!(p = nlm4_decode_cookie(p, &argp->cookie))) + return 0; + argp->block = ntohl(*p++); + exclusive = ntohl(*p++); + if (!(p = nlm4_decode_lock(p, &argp->lock))) + return 0; + if (exclusive) + argp->lock.fl.fl_type = F_WRLCK; + return xdr_argsize_check(rqstp, p); +} + +int +nlm4svc_decode_unlockargs(struct svc_rqst *rqstp, u32 *p, nlm_args *argp) +{ + if (!(p = nlm4_decode_cookie(p, &argp->cookie)) + || !(p = nlm4_decode_lock(p, &argp->lock))) + return 0; + argp->lock.fl.fl_type = F_UNLCK; + return xdr_argsize_check(rqstp, p); +} + +int +nlm4svc_decode_shareargs(struct svc_rqst *rqstp, u32 *p, nlm_args *argp) +{ + struct nlm_lock *lock = &argp->lock; + int len; + + memset(lock, 0, sizeof(*lock)); + lock->fl.fl_pid = ~(u32) 0; + + if (!(p = nlm4_decode_cookie(p, &argp->cookie)) + || !(p = xdr_decode_string(p, &lock->caller, &len, NLM_MAXSTRLEN)) + || !(p = nlm4_decode_fh(p, &lock->fh)) + || !(p = nlm4_decode_oh(p, &lock->oh))) + return 0; + argp->fsm_mode = ntohl(*p++); + argp->fsm_access = ntohl(*p++); + return xdr_argsize_check(rqstp, p); +} + +int +nlm4svc_encode_shareres(struct svc_rqst *rqstp, u32 *p, struct nlm_res *resp) +{ + if (!(p = nlm4_encode_cookie(p, &resp->cookie))) + return 0; + *p++ = resp->status; + *p++ = xdr_zero; /* sequence argument */ + return xdr_ressize_check(rqstp, p); +} + +int +nlm4svc_encode_res(struct svc_rqst *rqstp, u32 *p, struct nlm_res *resp) +{ + if (!(p = nlm4_encode_cookie(p, &resp->cookie))) + return 0; + *p++ = resp->status; + return xdr_ressize_check(rqstp, p); +} + +int +nlm4svc_decode_notify(struct svc_rqst *rqstp, u32 *p, struct nlm_args *argp) +{ + struct nlm_lock *lock = &argp->lock; + int len; + + if (!(p = xdr_decode_string(p, &lock->caller, &len, NLM_MAXSTRLEN))) + return 0; + argp->state = ntohl(*p++); + return xdr_argsize_check(rqstp, p); +} + +int +nlm4svc_decode_reboot(struct svc_rqst *rqstp, u32 *p, struct nlm_reboot *argp) +{ + if (!(p = xdr_decode_string(p, &argp->mon, &argp->len, SM_MAXSTRLEN))) + return 0; + argp->state = ntohl(*p++); + argp->addr = ntohl(*p++); + return xdr_argsize_check(rqstp, p); +} + +int +nlm4svc_decode_res(struct svc_rqst *rqstp, u32 *p, struct nlm_res *resp) +{ + if (!(p = nlm4_decode_cookie(p, &resp->cookie))) + return 0; + resp->status = ntohl(*p++); + return xdr_argsize_check(rqstp, p); +} + +int +nlm4svc_decode_void(struct svc_rqst *rqstp, u32 *p, void *dummy) +{ + return xdr_argsize_check(rqstp, p); +} + +int +nlm4svc_encode_void(struct svc_rqst *rqstp, u32 *p, void *dummy) +{ + return xdr_ressize_check(rqstp, p); +} + +/* + * Now, the client side XDR functions + */ +static int +nlm4clt_encode_void(struct rpc_rqst *req, u32 *p, void *ptr) +{ + req->rq_slen = xdr_adjust_iovec(req->rq_svec, p); + return 0; +} + +static int +nlm4clt_decode_void(struct rpc_rqst *req, u32 *p, void *ptr) +{ + return 0; +} + +static int +nlm4clt_encode_testargs(struct rpc_rqst *req, u32 *p, nlm_args *argp) +{ + struct nlm_lock *lock = &argp->lock; + + if (!(p = nlm4_encode_cookie(p, &argp->cookie))) + return -EIO; + *p++ = (lock->fl.fl_type == F_WRLCK)? xdr_one : xdr_zero; + if (!(p = nlm4_encode_lock(p, lock))) + return -EIO; + req->rq_slen = xdr_adjust_iovec(req->rq_svec, p); + return 0; +} + +static int +nlm4clt_decode_testres(struct rpc_rqst *req, u32 *p, struct nlm_res *resp) +{ + if (!(p = nlm4_decode_cookie(p, &resp->cookie))) + return -EIO; + resp->status = ntohl(*p++); + if (resp->status == NLM_LCK_DENIED) { + struct file_lock *fl = &resp->lock.fl; + u32 excl; + s64 start, end, len; + + memset(&resp->lock, 0, sizeof(resp->lock)); + excl = ntohl(*p++); + fl->fl_pid = ntohl(*p++); + if (!(p = nlm4_decode_oh(p, &resp->lock.oh))) + return -EIO; + + fl->fl_flags = FL_POSIX; + fl->fl_type = excl? F_WRLCK : F_RDLCK; + p = xdr_decode_hyper(p, &start); + p = xdr_decode_hyper(p, &len); + end = start + len - 1; + + fl->fl_start = size_to_off_t(start); + fl->fl_end = size_to_off_t(end); + if (len == 0 || fl->fl_end < 0) + fl->fl_end = OFFSET_MAX; + } + return 0; +} + + +static int +nlm4clt_encode_lockargs(struct rpc_rqst *req, u32 *p, nlm_args *argp) +{ + struct nlm_lock *lock = &argp->lock; + + if (!(p = nlm4_encode_cookie(p, &argp->cookie))) + return -EIO; + *p++ = argp->block? xdr_one : xdr_zero; + *p++ = (lock->fl.fl_type == F_WRLCK)? xdr_one : xdr_zero; + if (!(p = nlm4_encode_lock(p, lock))) + return -EIO; + *p++ = argp->reclaim? xdr_one : xdr_zero; + *p++ = htonl(argp->state); + req->rq_slen = xdr_adjust_iovec(req->rq_svec, p); + return 0; +} + +static int +nlm4clt_encode_cancargs(struct rpc_rqst *req, u32 *p, nlm_args *argp) +{ + struct nlm_lock *lock = &argp->lock; + + if (!(p = nlm4_encode_cookie(p, &argp->cookie))) + return -EIO; + *p++ = argp->block? xdr_one : xdr_zero; + *p++ = (lock->fl.fl_type == F_WRLCK)? xdr_one : xdr_zero; + if (!(p = nlm4_encode_lock(p, lock))) + return -EIO; + req->rq_slen = xdr_adjust_iovec(req->rq_svec, p); + return 0; +} + +static int +nlm4clt_encode_unlockargs(struct rpc_rqst *req, u32 *p, nlm_args *argp) +{ + struct nlm_lock *lock = &argp->lock; + + if (!(p = nlm4_encode_cookie(p, &argp->cookie))) + return -EIO; + if (!(p = nlm4_encode_lock(p, lock))) + return -EIO; + req->rq_slen = xdr_adjust_iovec(req->rq_svec, p); + return 0; +} + +static int +nlm4clt_encode_res(struct rpc_rqst *req, u32 *p, struct nlm_res *resp) +{ + if (!(p = nlm4_encode_cookie(p, &resp->cookie))) + return -EIO; + *p++ = resp->status; + req->rq_slen = xdr_adjust_iovec(req->rq_svec, p); + return 0; +} + +static int +nlm4clt_encode_testres(struct rpc_rqst *req, u32 *p, struct nlm_res *resp) +{ + if (!(p = nlm4_encode_testres(p, resp))) + return -EIO; + req->rq_slen = xdr_adjust_iovec(req->rq_svec, p); + return 0; +} + +static int +nlm4clt_decode_res(struct rpc_rqst *req, u32 *p, struct nlm_res *resp) +{ + if (!(p = nlm4_decode_cookie(p, &resp->cookie))) + return -EIO; + resp->status = ntohl(*p++); + return 0; +} + +/* + * Buffer requirements for NLM + */ +#define NLM4_void_sz 0 +#define NLM4_cookie_sz 3 /* 1 len , 2 data */ +#define NLM4_caller_sz 1+XDR_QUADLEN(NLM_MAXSTRLEN) +#define NLM4_netobj_sz 1+XDR_QUADLEN(XDR_MAX_NETOBJ) +/* #define NLM4_owner_sz 1+XDR_QUADLEN(NLM4_MAXOWNER) */ +#define NLM4_fhandle_sz 1+XDR_QUADLEN(NFS3_FHSIZE) +#define NLM4_lock_sz 5+NLM4_caller_sz+NLM4_netobj_sz+NLM4_fhandle_sz +#define NLM4_holder_sz 6+NLM4_netobj_sz + +#define NLM4_testargs_sz NLM4_cookie_sz+1+NLM4_lock_sz +#define NLM4_lockargs_sz NLM4_cookie_sz+4+NLM4_lock_sz +#define NLM4_cancargs_sz NLM4_cookie_sz+2+NLM4_lock_sz +#define NLM4_unlockargs_sz NLM4_cookie_sz+NLM4_lock_sz + +#define NLM4_testres_sz NLM4_cookie_sz+1+NLM4_holder_sz +#define NLM4_res_sz NLM4_cookie_sz+1 +#define NLM4_norep_sz 0 + +#ifndef MAX +# define MAX(a,b) (((a) > (b))? (a) : (b)) +#endif + +/* + * For NLM, a void procedure really returns nothing + */ +#define nlm4clt_decode_norep NULL + +#define PROC(proc, argtype, restype) \ + { "nlm4_" #proc, \ + (kxdrproc_t) nlm4clt_encode_##argtype, \ + (kxdrproc_t) nlm4clt_decode_##restype, \ + MAX(NLM4_##argtype##_sz, NLM4_##restype##_sz) << 2, \ + 0 \ + } + +static struct rpc_procinfo nlm4_procedures[] = { + PROC(null, void, void), + PROC(test, testargs, testres), + PROC(lock, lockargs, res), + PROC(canc, cancargs, res), + PROC(unlock, unlockargs, res), + PROC(granted, testargs, res), + PROC(test_msg, testargs, norep), + PROC(lock_msg, lockargs, norep), + PROC(canc_msg, cancargs, norep), + PROC(unlock_msg, unlockargs, norep), + PROC(granted_msg, testargs, norep), + PROC(test_res, testres, norep), + PROC(lock_res, res, norep), + PROC(canc_res, res, norep), + PROC(unlock_res, res, norep), + PROC(granted_res, res, norep), + PROC(undef, void, void), + PROC(undef, void, void), + PROC(undef, void, void), + PROC(undef, void, void), +#ifdef NLMCLNT_SUPPORT_SHARES + PROC(share, shareargs, shareres), + PROC(unshare, shareargs, shareres), + PROC(nm_lock, lockargs, res), + PROC(free_all, notify, void), +#else + PROC(undef, void, void), + PROC(undef, void, void), + PROC(undef, void, void), + PROC(undef, void, void), +#endif +}; + +struct rpc_version nlm_version4 = { + 4, 24, nlm4_procedures, +}; diff --git a/fs/locks.c b/fs/locks.c index 802958a68..1661a4a5c 100644 --- a/fs/locks.c +++ b/fs/locks.c @@ -416,15 +416,16 @@ int fcntl_setlk(unsigned int fd, unsigned int cmd, struct flock *l) if (IS_MANDLOCK(inode) && (inode->i_mode & (S_ISGID | S_IXGRP)) == S_ISGID) { struct vm_area_struct *vma; - spin_lock(&inode->i_shared_lock); - for(vma = inode->i_mmap;vma;vma = vma->vm_next_share) { + struct address_space *mapping = inode->i_mapping; + spin_lock(&mapping->i_shared_lock); + for(vma = mapping->i_mmap;vma;vma = vma->vm_next_share) { if (!(vma->vm_flags & VM_MAYSHARE)) continue; - spin_unlock(&inode->i_shared_lock); + spin_unlock(&mapping->i_shared_lock); error = -EAGAIN; goto out_putf; } - spin_unlock(&inode->i_shared_lock); + spin_unlock(&mapping->i_shared_lock); } error = -EINVAL; diff --git a/fs/minix/inode.c b/fs/minix/inode.c index e33fe9787..9620870ad 100644 --- a/fs/minix/inode.c +++ b/fs/minix/inode.c @@ -35,9 +35,13 @@ static int minix_remount (struct super_block * sb, int * flags, char * data); static void minix_delete_inode(struct inode *inode) { + lock_kernel(); + inode->i_size = 0; minix_truncate(inode); minix_free_inode(inode); + + unlock_kernel(); } static void minix_commit_super(struct super_block * sb) @@ -1243,7 +1247,9 @@ static void minix_write_inode(struct inode * inode) { struct buffer_head *bh; + lock_kernel(); bh = minix_update_inode(inode); + unlock_kernel(); brelse(bh); } diff --git a/fs/ncpfs/inode.c b/fs/ncpfs/inode.c index 5a4e790a7..fdbf5fc82 100644 --- a/fs/ncpfs/inode.c +++ b/fs/ncpfs/inode.c @@ -693,6 +693,8 @@ int ncp_notify_change(struct dentry *dentry, struct iattr *attr) /* According to ndir, the changes only take effect after closing the file */ result = ncp_make_closed(inode); + if (!result) + vmtruncate(inode, attr->ia_size); } out: return result; diff --git a/fs/ncpfs/sock.c b/fs/ncpfs/sock.c index 33a4293c4..3f36db262 100644 --- a/fs/ncpfs/sock.c +++ b/fs/ncpfs/sock.c @@ -203,7 +203,8 @@ static int do_ncp_rpc_call(struct ncp_server *server, int size, } n = 0; timeout = init_timeout; - init_timeout <<= 1; + if (init_timeout < max_timeout) + init_timeout <<= 1; if (!major_timeout_seen) { printk(KERN_WARNING "NCP server not responding\n"); } diff --git a/fs/nfs/file.c b/fs/nfs/file.c index 2135ece68..911b61b9c 100644 --- a/fs/nfs/file.c +++ b/fs/nfs/file.c @@ -165,11 +165,16 @@ static int nfs_prepare_write(struct page *page, unsigned offset, unsigned to) static int nfs_commit_write(struct file *file, struct page *page, unsigned offset, unsigned to) { long status; + loff_t pos = ((loff_t)page->index<<PAGE_CACHE_SHIFT) + to; + struct inode *inode = (struct inode*)page->mapping->host; kunmap(page); lock_kernel(); status = nfs_updatepage(file, page, offset, to-offset); unlock_kernel(); + /* most likely it's already done. CHECKME */ + if (pos > inode->i_size) + inode->i_size = pos; return status; } diff --git a/fs/nfs/inode.c b/fs/nfs/inode.c index c8d42adfe..16b3624c1 100644 --- a/fs/nfs/inode.c +++ b/fs/nfs/inode.c @@ -28,6 +28,7 @@ #include <linux/sunrpc/stats.h> #include <linux/nfs_fs.h> #include <linux/lockd/bind.h> +#include <linux/smp_lock.h> #include <asm/system.h> #include <asm/uaccess.h> @@ -102,6 +103,7 @@ nfs_delete_inode(struct inode * inode) dprintk("NFS: delete_inode(%x/%ld)\n", inode->i_dev, inode->i_ino); + lock_kernel(); if (S_ISDIR(inode->i_mode)) { nfs_free_dircache(inode); } else { @@ -129,6 +131,8 @@ printk("nfs_delete_inode: inode %ld has pending RPC requests\n", inode->i_ino); if (failed) printk("NFS: inode %ld had %d failed requests\n", inode->i_ino, failed); + unlock_kernel(); + clear_inode(inode); } @@ -683,8 +687,8 @@ printk("nfs_notify_change: revalidate failed, error=%d\n", error); if (attr->ia_size != fattr.size) printk("nfs_notify_change: attr=%Ld, fattr=%d??\n", (long long) attr->ia_size, fattr.size); - inode->i_size = attr->ia_size; inode->i_mtime = fattr.mtime.seconds; + vmtruncate(inode, attr->ia_size); } if (attr->ia_valid & ATTR_MTIME) inode->i_mtime = fattr.mtime.seconds; diff --git a/fs/nfsd/lockd.c b/fs/nfsd/lockd.c index cdd6f2e4e..4062410c6 100644 --- a/fs/nfsd/lockd.c +++ b/fs/nfsd/lockd.c @@ -34,7 +34,16 @@ nlm_fopen(struct svc_rqst *rqstp, struct knfs_fh *f, struct file *filp) if (!nfserr) dget(filp->f_dentry); fh_put(&fh); - return nfserr; + /* nlm and nfsd don't share error codes. + * we invent: 0 = no error + * 1 = stale file handle + * 2 = other error + */ + if (nfserr == 0) + return 0; + else if (nfserr == nfserr_stale) + return 1; + else return 2; } static void diff --git a/fs/nfsd/nfs3xdr.c b/fs/nfsd/nfs3xdr.c index 95e8e8424..8c3f8b525 100644 --- a/fs/nfsd/nfs3xdr.c +++ b/fs/nfsd/nfs3xdr.c @@ -177,15 +177,15 @@ encode_fattr3(struct svc_rqst *rqstp, u32 *p, struct dentry *dentry) *p++ = htonl((u32) nfsd_ruid(rqstp, inode->i_uid)); *p++ = htonl((u32) nfsd_rgid(rqstp, inode->i_gid)); if (S_ISLNK(inode->i_mode) && inode->i_size > NFS3_MAXPATHLEN) { - p = enc64(p, (u64) NFS3_MAXPATHLEN); + p = xdr_encode_hyper(p, (u64) NFS3_MAXPATHLEN); } else { - p = enc64(p, (u64) inode->i_size); + p = xdr_encode_hyper(p, (u64) inode->i_size); } - p = enc64(p, inode->i_blksize * inode->i_blocks); + p = xdr_encode_hyper(p, ((u64)inode->i_blocks) << 9); *p++ = htonl((u32) MAJOR(inode->i_rdev)); *p++ = htonl((u32) MINOR(inode->i_rdev)); - p = enc64(p, (u64) inode->i_dev); - p = enc64(p, (u64) inode->i_ino); + p = xdr_encode_hyper(p, (u64) inode->i_dev); + p = xdr_encode_hyper(p, (u64) inode->i_ino); p = encode_time3(p, inode->i_atime); p = encode_time3(p, inode->i_mtime); p = encode_time3(p, inode->i_ctime); @@ -207,15 +207,15 @@ encode_saved_post_attr(struct svc_rqst *rqstp, u32 *p, struct svc_fh *fhp) *p++ = htonl((u32) nfsd_ruid(rqstp, fhp->fh_post_uid)); *p++ = htonl((u32) nfsd_rgid(rqstp, fhp->fh_post_gid)); if (S_ISLNK(fhp->fh_post_mode) && fhp->fh_post_size > NFS3_MAXPATHLEN) { - p = enc64(p, (u64) NFS3_MAXPATHLEN); + p = xdr_encode_hyper(p, (u64) NFS3_MAXPATHLEN); } else { - p = enc64(p, (u64) fhp->fh_post_size); + p = xdr_encode_hyper(p, (u64) fhp->fh_post_size); } - p = enc64(p, fhp->fh_post_blksize * fhp->fh_post_blocks); + p = xdr_encode_hyper(p, ((u64)fhp->fh_post_blocks) << 9); *p++ = htonl((u32) MAJOR(fhp->fh_post_rdev)); *p++ = htonl((u32) MINOR(fhp->fh_post_rdev)); - p = enc64(p, (u64) inode->i_dev); - p = enc64(p, (u64) inode->i_ino); + p = xdr_encode_hyper(p, (u64) inode->i_dev); + p = xdr_encode_hyper(p, (u64) inode->i_ino); p = encode_time3(p, fhp->fh_post_atime); p = encode_time3(p, fhp->fh_post_mtime); p = encode_time3(p, fhp->fh_post_ctime); @@ -250,7 +250,7 @@ encode_wcc_data(struct svc_rqst *rqstp, u32 *p, struct svc_fh *fhp) if (dentry && dentry->d_inode && fhp->fh_post_saved) { if (fhp->fh_pre_saved) { *p++ = xdr_one; - p = enc64(p, (u64) fhp->fh_pre_size); + p = xdr_encode_hyper(p, (u64) fhp->fh_pre_size); p = encode_time3(p, fhp->fh_pre_mtime); p = encode_time3(p, fhp->fh_pre_ctime); } else { @@ -669,7 +669,7 @@ encode_entry(struct readdir_cd *cd, const char *name, int buflen, slen, elen; if (cd->offset) - enc64(cd->offset, (u64) offset); + xdr_encode_hyper(cd->offset, (u64) offset); /* nfsd_readdir calls us with name == 0 when it wants us to * set the last offset entry. */ @@ -693,7 +693,7 @@ encode_entry(struct readdir_cd *cd, const char *name, return -EINVAL; } *p++ = xdr_one; /* mark entry present */ - p = enc64(p, ino); /* file id */ + p = xdr_encode_hyper(p, ino); /* file id */ #ifdef XDR_ENCODE_STRING_TAKES_LENGTH p = xdr_encode_string(p, name, namlen); /* name length & name */ #else @@ -705,7 +705,7 @@ encode_entry(struct readdir_cd *cd, const char *name, p[slen - 1] = 0; /* don't leak kernel data */ cd->offset = p; /* remember pointer */ - p = enc64(p, NFS_OFFSET_MAX); /* offset of next entry */ + p = xdr_encode_hyper(p, NFS_OFFSET_MAX); /* offset of next entry */ /* throw in readdirplus baggage */ if (plus) { @@ -756,12 +756,12 @@ nfs3svc_encode_fsstatres(struct svc_rqst *rqstp, u32 *p, *p++ = xdr_zero; /* no post_op_attr */ if (resp->status == 0) { - p = enc64(p, bs * s->f_blocks); /* total bytes */ - p = enc64(p, bs * s->f_bfree); /* free bytes */ - p = enc64(p, bs * s->f_bavail); /* user available bytes */ - p = enc64(p, s->f_files); /* total inodes */ - p = enc64(p, s->f_ffree); /* free inodes */ - p = enc64(p, s->f_ffree); /* user available inodes */ + p = xdr_encode_hyper(p, bs * s->f_blocks); /* total bytes */ + p = xdr_encode_hyper(p, bs * s->f_bfree); /* free bytes */ + p = xdr_encode_hyper(p, bs * s->f_bavail); /* user available bytes */ + p = xdr_encode_hyper(p, s->f_files); /* total inodes */ + p = xdr_encode_hyper(p, s->f_ffree); /* free inodes */ + p = xdr_encode_hyper(p, s->f_ffree); /* user available inodes */ *p++ = htonl(resp->invarsec); /* mean unchanged time */ } return xdr_ressize_check(rqstp, p); @@ -782,7 +782,7 @@ nfs3svc_encode_fsinfores(struct svc_rqst *rqstp, u32 *p, *p++ = htonl(resp->f_wtpref); *p++ = htonl(resp->f_wtmult); *p++ = htonl(resp->f_dtpref); - p = enc64(p, resp->f_maxfilesize); + p = xdr_encode_hyper(p, resp->f_maxfilesize); *p++ = xdr_one; *p++ = xdr_zero; *p++ = htonl(resp->f_properties); diff --git a/fs/nfsd/nfsfh.c b/fs/nfsd/nfsfh.c index 3b9927c7a..63c8fec93 100644 --- a/fs/nfsd/nfsfh.c +++ b/fs/nfsd/nfsfh.c @@ -150,7 +150,7 @@ static struct dentry *nfsd_iget(struct super_block *sb, unsigned long ino, __u32 */ for (lp = inode->i_dentry.next; lp != &inode->i_dentry ; lp=lp->next) { result = list_entry(lp,struct dentry, d_alias); - if (! IS_ROOT(result) || inode->i_sb->s_root == result) { + if (! (result->d_flags & DCACHE_NFSD_DISCONNECTED)) { dget(result); iput(inode); return result; @@ -161,6 +161,7 @@ static struct dentry *nfsd_iget(struct super_block *sb, unsigned long ino, __u32 iput(inode); return ERR_PTR(-ENOMEM); } + result->d_flags |= DCACHE_NFSD_DISCONNECTED; d_rehash(result); /* so a dput won't loose it */ return result; } @@ -175,6 +176,8 @@ int d_splice(struct dentry *target, struct dentry *parent, struct qstr *name) #ifdef NFSD_PARANOIA if (!IS_ROOT(target)) printk("nfsd: d_splice with no-root target: %s/%s\n", parent->d_name.name, name->name); + if (!(target->d_flags & DCACHE_NFSD_DISCONNECTED)) + printk("nfsd: d_splice with non-DISCONNECTED target: %s/%s\n", parent->d_name.name, name->name); #endif name->hash = full_name_hash(name->name, name->len); tdentry = d_alloc(parent, name); @@ -189,6 +192,29 @@ int d_splice(struct dentry *target, struct dentry *parent, struct qstr *name) tdentry->d_parent = tdentry; d_rehash(target); dput(tdentry); + + /* if parent is properly connected, then we can assert that + * the children are connected, but it must be a singluar (non-forking) + * branch + */ + if (!(parent->d_flags & DCACHE_NFSD_DISCONNECTED)) { + while (target) { + target->d_flags &= ~DCACHE_NFSD_DISCONNECTED; + parent = target; + if (list_empty(&parent->d_subdirs)) + target = NULL; + else { + target = list_entry(parent->d_subdirs.next, struct dentry, d_child); +#ifdef NFSD_PARANOIA + /* must be only child */ + if (target->d_child.next != &parent->d_subdirs + || target->d_child.prev != &parent->d_subdirs) + printk("nfsd: d_splice found non-singular disconnected branch: %s/%s\n", + parent->d_name.name, target->d_name.name); +#endif + } + } + } return 0; } @@ -227,7 +253,10 @@ struct dentry *nfsd_findparent(struct dentry *child) } if (pdentry == NULL) { pdentry = d_alloc_root(igrab(tdentry->d_inode)); - if (pdentry) d_rehash(pdentry); + if (pdentry) { + pdentry->d_flags |= DCACHE_NFSD_DISCONNECTED; + d_rehash(pdentry); + } } if (pdentry == NULL) pdentry = ERR_PTR(-ENOMEM); @@ -301,14 +330,14 @@ find_fh_dentry(struct super_block *sb, struct knfs_fh *fh, int needpath) struct dentry *dentry, *result = NULL; struct dentry *tmp; int found =0; - u32 err; - /* This semaphore is needed to make sure that only one unconnected (free) + int err; + /* the sb->s_nfsd_free_path_sem semaphore is needed to make sure that only one unconnected (free) * dcache path ever exists, as otherwise two partial paths might get * joined together, which would be very confusing. * If there is ever an unconnected non-root directory, then this lock - * must be held. This could sensibly be per-filesystem. + * must be held. */ - static DECLARE_MUTEX(free_path_sem); + nfsdstats.fh_lookup++; /* @@ -324,7 +353,7 @@ find_fh_dentry(struct super_block *sb, struct knfs_fh *fh, int needpath) dprintk("find_fh_dentry: No inode found.\n"); goto err_out; } - if (!IS_ROOT(result) || result->d_inode->i_sb->s_root ==result) + if (! (result->d_flags & DCACHE_NFSD_DISCONNECTED)) return result; /* result is now an anonymous dentry, which may be adequate as it stands, or else @@ -339,7 +368,15 @@ find_fh_dentry(struct super_block *sb, struct knfs_fh *fh, int needpath) * location in the tree. */ dprintk("nfs_fh: need to look harder for %d/%d\n",sb->s_dev,fh->fh_ino); - down(&free_path_sem); + down(&sb->s_nfsd_free_path_sem); + + /* claiming the semaphore might have allow things to get fixed up */ + if (! (result->d_flags & DCACHE_NFSD_DISCONNECTED)) { + up(&sb->s_nfsd_free_path_sem); + return result; + } + + found = 0; if (!S_ISDIR(result->d_inode->i_mode)) { nfsdstats.fh_nocache_nondir++; @@ -356,7 +393,7 @@ find_fh_dentry(struct super_block *sb, struct knfs_fh *fh, int needpath) || !S_ISDIR(dentry->d_inode->i_mode)) { goto err_dentry; } - if (!IS_ROOT(dentry) || dentry->d_inode->i_sb->s_root ==dentry) + if ((!dentry->d_flags & DCACHE_NFSD_DISCONNECTED)) found = 1; tmp = splice(result, dentry); err = PTR_ERR(tmp); @@ -393,10 +430,8 @@ find_fh_dentry(struct super_block *sb, struct knfs_fh *fh, int needpath) dput(pdentry); goto err_dentry; } - /* I'm not sure that this is the best test for - * "is it not a floating dentry?" - */ - if (!IS_ROOT(pdentry) || parent->i_sb->s_root == pdentry) + + if (!(dentry->d_flags & DCACHE_NFSD_DISCONNECTED)) found = 1; tmp = splice(dentry, pdentry); @@ -419,21 +454,21 @@ find_fh_dentry(struct super_block *sb, struct knfs_fh *fh, int needpath) dput(tmp); dput(dentry); dput(result); /* this will discard the whole free path, so we can up the semaphore */ - up(&free_path_sem); + up(&sb->s_nfsd_free_path_sem); goto retry; } dput(dentry); dentry = pdentry; } dput(dentry); - up(&free_path_sem); + up(&sb->s_nfsd_free_path_sem); return result; err_dentry: dput(dentry); err_result: dput(result); - up(&free_path_sem); + up(&sb->s_nfsd_free_path_sem); err_out: if (err == -ESTALE) nfsdstats.fh_stale++; @@ -515,6 +550,13 @@ fh_verify(struct svc_rqst *rqstp, struct svc_fh *fhp, int type, int access) error = nfserrno(-PTR_ERR(dentry)); goto out; } +#ifdef NFSD_PARANOIA + if (S_ISDIR(dentry->d_inode->i_mode) && + (dentry->d_flags & DCACHE_NFSD_DISCONNECTED)) { + printk("nfsd: find_fh_dentry returned a DISCONNECTED directory: %s/%s\n", + dentry->d_parent->d_name.name, dentry->d_name.name); + } +#endif fhp->fh_dentry = dentry; fhp->fh_export = exp; diff --git a/fs/nfsd/nfssvc.c b/fs/nfsd/nfssvc.c index 940460a1b..d69bba8d0 100644 --- a/fs/nfsd/nfssvc.c +++ b/fs/nfsd/nfssvc.c @@ -42,7 +42,13 @@ extern struct svc_program nfsd_program; static void nfsd(struct svc_rqst *rqstp); struct timeval nfssvc_boot = { 0, 0 }; -static int nfsd_active = 0; +static struct svc_serv *nfsd_serv = NULL; + +struct nfsd_list { + struct list_head list; + struct task_struct *task; +}; +struct list_head nfsd_list = LIST_HEAD_INIT(nfsd_list); /* * Maximum number of nfsd processes @@ -52,45 +58,60 @@ static int nfsd_active = 0; int nfsd_svc(unsigned short port, int nrservs) { - struct svc_serv * serv; - int error; + int error; + int none_left; + struct list_head *victim; dprintk("nfsd: creating service\n"); error = -EINVAL; if (nrservs <= 0) - goto out; + nrservs = 0; if (nrservs > NFSD_MAXSERVS) nrservs = NFSD_MAXSERVS; - nfsd_nservers = nrservs; - - error = -ENOMEM; - nfsd_racache_init(); /* Readahead param cache */ - if (nfsd_nservers == 0) - goto out; - - serv = svc_create(&nfsd_program, NFSD_BUFSIZE, NFSSVC_XDRSIZE); - if (serv == NULL) + + /* Readahead param cache - will no-op if it already exists */ + error = nfsd_racache_init(2*nrservs); + if (error<0) goto out; - - error = svc_makesock(serv, IPPROTO_UDP, port); - if (error < 0) - goto failure; + if (!nfsd_serv) { + nfsd_serv = svc_create(&nfsd_program, NFSD_BUFSIZE, NFSSVC_XDRSIZE); + if (nfsd_serv == NULL) + goto out; + error = svc_makesock(nfsd_serv, IPPROTO_UDP, port); + if (error < 0) + goto failure; #if 0 /* Don't even pretend that TCP works. It doesn't. */ - error = svc_makesock(serv, IPPROTO_TCP, port); - if (error < 0) - goto failure; + error = svc_makesock(nfsd_serv, IPPROTO_TCP, port); + if (error < 0) + goto failure; #endif - - while (nrservs--) { - error = svc_create_thread(nfsd, serv); + get_fast_time(&nfssvc_boot); /* record boot time */ + } else + nfsd_serv->sv_nrthreads++; + nrservs -= (nfsd_serv->sv_nrthreads-1); + while (nrservs > 0) { + nrservs--; + error = svc_create_thread(nfsd, nfsd_serv); if (error < 0) break; } - -failure: - svc_destroy(serv); /* Release server */ -out: + victim = nfsd_list.next; + while (nrservs < 0 && victim != &nfsd_list) { + struct nfsd_list *nl = + list_entry(victim,struct nfsd_list, list); + victim = victim->next; + send_sig(SIGKILL, nl->task, 1); + nrservs++; + } + failure: + none_left = (nfsd_serv->sv_nrthreads == 1); + svc_destroy(nfsd_serv); /* Release server */ + if (none_left) { + nfsd_serv = NULL; + nfsd_racache_shutdown(); + } + out: return error; } @@ -101,7 +122,8 @@ static void nfsd(struct svc_rqst *rqstp) { struct svc_serv *serv = rqstp->rq_server; - int oldumask, err; + int err; + struct nfsd_list me; /* Lock module and set up kernel thread */ MOD_INC_USE_COUNT; @@ -109,17 +131,17 @@ nfsd(struct svc_rqst *rqstp) exit_mm(current); current->session = 1; current->pgrp = 1; + sprintf(current->comm, "nfsd"); + current->fs->umask = 0; + /* Let svc_process check client's authentication. */ rqstp->rq_auth = 1; - sprintf(current->comm, "nfsd"); - oldumask = current->fs->umask; /* Set umask to 0. */ - current->fs->umask = 0; - if (!nfsd_active++) { - nfssvc_boot = xtime; /* record boot time */ - } lockd_up(); /* start lockd */ + me.task = current; + list_add(&me.list, &nfsd_list); + /* * The main request loop */ @@ -173,17 +195,16 @@ nfsd(struct svc_rqst *rqstp) /* Release lockd */ lockd_down(); - if (!--nfsd_active) { - printk("nfsd: last server exiting\n"); - /* revoke all exports */ - nfsd_export_shutdown(); - /* release read-ahead cache */ - nfsd_racache_shutdown(); + + /* Check if this is last thread */ + if (serv->sv_nrthreads==1) { + nfsd_serv = NULL; + nfsd_racache_shutdown(); /* release read-ahead cache */ } + list_del(&me.list); - /* Destroy the thread */ + /* Release the thread */ svc_exit_thread(rqstp); - current->fs->umask = oldumask; /* Release module */ MOD_DEC_USE_COUNT; diff --git a/fs/nfsd/vfs.c b/fs/nfsd/vfs.c index b1af9399e..5ea680286 100644 --- a/fs/nfsd/vfs.c +++ b/fs/nfsd/vfs.c @@ -73,8 +73,6 @@ struct raparms { p_rawin; }; -int nfsd_nservers = 0; -#define FILECACHE_MAX (2 * nfsd_nservers) static struct raparms * raparml = NULL; static struct raparms * raparm_cache = NULL; @@ -272,10 +270,7 @@ printk("nfsd_setattr: size change??\n"); if (err) goto out_nfserr; - err = locks_verify_area(FLOCK_VERIFY_WRITE, inode, NULL, - iap->ia_size<inode->i_size ? iap->ia_size : inode->i_size, - abs(inode->i_size - iap->ia_size)); - + err = locks_verify_truncate(inode, NULL, iap->ia_size); if (err) goto out_nfserr; DQUOT_INIT(inode); @@ -310,11 +305,6 @@ printk("nfsd_setattr: size change??\n"); if (iap->ia_valid & ATTR_SIZE) { fh_lock(fhp); err = notify_change(dentry, iap); - if (!err) { - vmtruncate(inode,iap->ia_size); - if (inode->i_op && inode->i_op->truncate) - inode->i_op->truncate(inode); - } fh_unlock(fhp); put_write_access(inode); } @@ -382,7 +372,7 @@ nfsd_access(struct svc_rqst *rqstp, struct svc_fh *fhp, u32 *access) int error; error = fh_verify(rqstp, fhp, 0, MAY_NOP); - if (error < 0) + if (error) goto out; export = fhp->fh_export; @@ -1066,9 +1056,7 @@ nfsd_truncate(struct svc_rqst *rqstp, struct svc_fh *fhp, unsigned long size) err = get_write_access(inode); if (err) goto out_nfserr; - err = locks_verify_area(FLOCK_VERIFY_WRITE, inode, NULL, - size<inode->i_size ? size : inode->i_size, - abs(inode->i_size - size)); + err = locks_verify_truncate(inode, NULL, size); if (err) goto out_nfserr; @@ -1082,11 +1070,6 @@ nfsd_truncate(struct svc_rqst *rqstp, struct svc_fh *fhp, unsigned long size) cap_clear(current->cap_effective); } err = notify_change(dentry, &newattrs); - if (!err) { - vmtruncate(inode, size); - if (inode->i_op && inode->i_op->truncate) - inode->i_op->truncate(inode); - } if (current->fsuid != 0) current->cap_effective = saved_cap; put_write_access(inode); @@ -1574,7 +1557,7 @@ nfsd_readdir(struct svc_rqst *rqstp, struct svc_fh *fhp, loff_t offset, if (cd.offset) { #ifdef CONFIG_NFSD_V3 if (rqstp->rq_vers == 3) - (void)enc64(cd.offset, file.f_pos); + (void)xdr_encode_hyper(cd.offset, file.f_pos); else #endif /* CONFIG_NFSD_V3 */ *cd.offset = htonl(file.f_pos); @@ -1713,34 +1696,34 @@ nfsd_racache_shutdown(void) { if (!raparm_cache) return; - dprintk("nfsd: freeing %d readahead buffers.\n", FILECACHE_MAX); + dprintk("nfsd: freeing readahead buffers.\n"); kfree(raparml); - nfsd_nservers = 0; raparm_cache = raparml = NULL; } /* * Initialize readahead param cache */ -void -nfsd_racache_init(void) +int +nfsd_racache_init(int cache_size) { int i; if (raparm_cache) - return; - raparml = kmalloc(sizeof(struct raparms) * FILECACHE_MAX, GFP_KERNEL); + return 0; + raparml = kmalloc(sizeof(struct raparms) * cache_size, GFP_KERNEL); if (raparml != NULL) { dprintk("nfsd: allocating %d readahead buffers.\n", - FILECACHE_MAX); - memset(raparml, 0, sizeof(struct raparms) * FILECACHE_MAX); - for (i = 0; i < FILECACHE_MAX - 1; i++) { + cache_size); + memset(raparml, 0, sizeof(struct raparms) * cache_size); + for (i = 0; i < cache_size - 1; i++) { raparml[i].p_next = raparml + i + 1; } raparm_cache = raparml; } else { printk(KERN_WARNING "nfsd: Could not allocate memory read-ahead cache.\n"); - nfsd_nservers = 0; + return -ENOMEM; } + return 0; } @@ -69,12 +69,6 @@ int do_truncate(struct dentry *dentry, loff_t length) newattrs.ia_size = length; newattrs.ia_valid = ATTR_SIZE | ATTR_CTIME; error = notify_change(dentry, &newattrs); - if (!error) { - /* truncate virtual mappings of this file */ - vmtruncate(inode, length); - if (inode->i_op && inode->i_op->truncate) - inode->i_op->truncate(inode); - } up(&inode->i_sem); return error; } @@ -118,9 +112,7 @@ static inline long do_sys_truncate(const char * path, loff_t length) if (error) goto dput_and_out; - error = locks_verify_area(FLOCK_VERIFY_WRITE, inode, NULL, - length < inode->i_size ? length : inode->i_size, - abs(inode->i_size - length)); + error = locks_verify_truncate(inode, NULL, length); if (!error) { DQUOT_INIT(inode); error = do_truncate(dentry, length); @@ -163,9 +155,7 @@ static inline long do_sys_ftruncate(unsigned int fd, loff_t length) error = -EPERM; if (IS_IMMUTABLE(inode) || IS_APPEND(inode)) goto out_putf; - error = locks_verify_area(FLOCK_VERIFY_WRITE, inode, file, - length<inode->i_size ? length : inode->i_size, - abs(inode->i_size - length)); + error = locks_verify_truncate(inode, file, length); lock_kernel(); if (!error) error = do_truncate(dentry, length); diff --git a/fs/openpromfs/inode.c b/fs/openpromfs/inode.c index 710c8a7dd..e43f8feec 100644 --- a/fs/openpromfs/inode.c +++ b/fs/openpromfs/inode.c @@ -1,4 +1,4 @@ -/* $Id: inode.c,v 1.4 2000/02/09 22:35:50 davem Exp $ +/* $Id: inode.c,v 1.5 2000/02/10 21:16:06 davem Exp $ * openpromfs.c: /proc/openprom handling routines * * Copyright (C) 1996-1999 Jakub Jelinek (jakub@redhat.com) diff --git a/fs/partitions/Config.in b/fs/partitions/Config.in index 6974e1a2d..a0a58e3dd 100644 --- a/fs/partitions/Config.in +++ b/fs/partitions/Config.in @@ -28,9 +28,6 @@ else if [ "$ARCH" = "alpha" ]; then define_bool CONFIG_OSF_PARTITION y fi - if [ "$ARCH" = "ppc" -o "$CONFIG_MAC" = "y" ]; then - define_bool CONFIG_MAC_PARTITION y - fi if [ "$CONFIG_AMIGA" != "y" -a "$CONFIG_ATARI" != "y" -a \ "$CONFIG_MAC" != "y" -a "$CONFIG_SGI_IP22" != "y" -a \ "$CONFIG_SGI_IP27" != "y" ]; then diff --git a/fs/partitions/acorn.c b/fs/partitions/acorn.c index 34aab5f44..4ffc43b0b 100644 --- a/fs/partitions/acorn.c +++ b/fs/partitions/acorn.c @@ -466,7 +466,7 @@ int acorn_partition(struct gendisk *hd, kdev_t dev, r = adfspart_check_POWERTEC(hd, dev, first_sector, first_part_minor); #endif if (r < 0) - printk(" unable to read boot sectors / partition sectors\n"); + if (warn_no_part) printk(" unable to read boot sectors / partition sectors\n"); else if (r) printk("\n"); return r; diff --git a/fs/partitions/amiga.c b/fs/partitions/amiga.c index aa8605315..4717a1b5f 100644 --- a/fs/partitions/amiga.c +++ b/fs/partitions/amiga.c @@ -54,7 +54,7 @@ amiga_partition(struct gendisk *hd, kdev_t dev, unsigned long first_sector, int for (blk = 0; blk < RDB_ALLOCATION_LIMIT; blk++) { if(!(bh = bread(dev,blk,blocksize))) { - printk("Dev %s: unable to read RDB block %d\n", + if (warn_no_part) printk("Dev %s: unable to read RDB block %d\n", kdevname(dev),blk); goto rdb_done; } @@ -80,7 +80,7 @@ amiga_partition(struct gendisk *hd, kdev_t dev, unsigned long first_sector, int brelse(bh); for (part = 1; blk > 0 && part <= 16; part++) { if (!(bh = bread(dev,blk,blocksize))) { - printk("Dev %s: unable to read partition block %d\n", + if (warn_no_part) printk("Dev %s: unable to read partition block %d\n", kdevname(dev),blk); goto rdb_done; } diff --git a/fs/partitions/atari.c b/fs/partitions/atari.c index ae8596e6a..be7028e14 100644 --- a/fs/partitions/atari.c +++ b/fs/partitions/atari.c @@ -48,7 +48,7 @@ int atari_partition (struct gendisk *hd, kdev_t dev, bh = bread (dev, 0, get_ptable_blocksize(dev)); if (!bh) { - printk (" unable to read block 0 (partition table)\n"); + if (warn_no_part) printk (" unable to read block 0 (partition table)\n"); return -1; } diff --git a/fs/partitions/check.c b/fs/partitions/check.c index 4f6cfd07d..31e1e64b6 100644 --- a/fs/partitions/check.c +++ b/fs/partitions/check.c @@ -39,6 +39,7 @@ extern void rd_load(void); extern void initrd_load(void); struct gendisk *gendisk_head; +int warn_no_part = 1; /*This is ugly: should make genhd removable media aware*/ static int (*check_part[])(struct gendisk *hd, kdev_t dev, unsigned long first_sect, int first_minor) = { #ifdef CONFIG_ACORN_PARTITION @@ -83,6 +84,14 @@ char *disk_name (struct gendisk *hd, int minor, char *buf) const char *maj = hd->major_name; int unit = (minor >> hd->minor_shift) + 'a'; + part = minor & ((1 << hd->minor_shift) - 1); + if (hd->part[minor].de) { + int pos; + + pos = devfs_generate_path (hd->part[minor].de, buf, 64); + if (pos >= 0) + return buf + pos; + } /* * IDE devices use multiple major numbers, but the drives * are named as: {hda,hdb}, {hdc,hdd}, {hde,hdf}, {hdg,hdh}.. @@ -111,7 +120,6 @@ char *disk_name (struct gendisk *hd, int minor, char *buf) maj = "hd"; break; } - part = minor & ((1 << hd->minor_shift) - 1); if (hd->major >= SCSI_DISK1_MAJOR && hd->major <= SCSI_DISK7_MAJOR) { unit = unit + (hd->major - SCSI_DISK1_MAJOR + 1) * 16; if (unit > 'z') { @@ -154,13 +162,20 @@ char *disk_name (struct gendisk *hd, int minor, char *buf) */ void add_gd_partition(struct gendisk *hd, int minor, int start, int size) { +#ifndef CONFIG_DEVFS_FS char buf[40]; +#endif + hd->part[minor].start_sect = start; hd->part[minor].nr_sects = size; +#ifdef CONFIG_DEVFS_FS + printk(" p%d", (minor & ((1 << hd->minor_shift) - 1))); +#else if (hd->major >= COMPAQ_SMART2_MAJOR+0 && hd->major <= COMPAQ_SMART2_MAJOR+7) printk(" p%d", (minor & ((1 << hd->minor_shift) - 1))); else printk(" %s", disk_name(hd, minor, buf)); +#endif } int get_hardsect_size(kdev_t dev) @@ -218,7 +233,7 @@ unsigned int get_ptable_blocksize(kdev_t dev) int get_partition_list(char * page) { struct gendisk *p; - char buf[40]; + char buf[64]; int n, len; len = sprintf(page, "major minor #blocks name\n\n"); @@ -238,9 +253,10 @@ int get_partition_list(char * page) static void check_partition(struct gendisk *hd, kdev_t dev, int first_part_minor) { + devfs_handle_t de = NULL; static int first_time = 1; unsigned long first_sector; - char buf[40]; + char buf[64]; int i; if (first_time) @@ -257,12 +273,104 @@ static void check_partition(struct gendisk *hd, kdev_t dev, int first_part_minor return; } - printk(KERN_INFO " %s:", disk_name(hd, MINOR(dev), buf)); + if (hd->de_arr) + de = hd->de_arr[MINOR(dev) >> hd->minor_shift]; + i = devfs_generate_path (de, buf, sizeof buf); + if (i >= 0) + printk(KERN_INFO " /dev/%s:", buf + i); + else + printk(KERN_INFO " %s:", disk_name(hd, MINOR(dev), buf)); for (i = 0; check_part[i]; i++) if (check_part[i](hd, dev, first_sector, first_part_minor)) - return; + goto setup_devfs; printk(" unknown partition table\n"); +setup_devfs: + i = first_part_minor - 1; + devfs_register_partitions (hd, i, hd->sizes ? 0 : 1); +} + +#ifdef CONFIG_DEVFS_FS +static void devfs_register_partition (struct gendisk *dev, int minor, int part) +{ + int devnum = minor >> dev->minor_shift; + devfs_handle_t dir; + unsigned int devfs_flags = DEVFS_FL_DEFAULT; + char devname[16]; + + if (dev->part[minor + part].de) return; + dir = devfs_get_parent (dev->part[minor].de); + if (!dir) return; + if ( dev->flags && (dev->flags[devnum] & GENHD_FL_REMOVABLE) ) + devfs_flags |= DEVFS_FL_REMOVABLE; + sprintf (devname, "part%d", part); + dev->part[minor + part].de = + devfs_register (dir, devname, 0, devfs_flags, + dev->major, minor + part, + S_IFBLK | S_IRUSR | S_IWUSR, 0, 0, + dev->fops, NULL); +} + +static void devfs_register_disc (struct gendisk *dev, int minor) +{ + int pos = 0; + int devnum = minor >> dev->minor_shift; + devfs_handle_t dir, slave; + unsigned int devfs_flags = DEVFS_FL_DEFAULT; + char dirname[64], symlink[16]; + static unsigned int disc_counter = 0; + static devfs_handle_t devfs_handle = NULL; + + if (dev->part[minor].de) return; + if ( dev->flags && (dev->flags[devnum] & GENHD_FL_REMOVABLE) ) + devfs_flags |= DEVFS_FL_REMOVABLE; + if (dev->de_arr) { + dir = dev->de_arr[devnum]; + if (!dir) /* Aware driver wants to block disc management */ + return; + pos = devfs_generate_path (dir, dirname + 3, sizeof dirname-3); + if (pos < 0) return; + strncpy (dirname + pos, "../", 3); + } + else { + /* Unaware driver: construct "real" directory */ + sprintf (dirname, "../%s/disc%d", dev->major_name, devnum); + dir = devfs_mk_dir (NULL, dirname + 3, 0, NULL); + } + if (!devfs_handle) + devfs_handle = devfs_mk_dir (NULL, "discs", 5, NULL); + sprintf (symlink, "disc%u", disc_counter++); + devfs_mk_symlink (devfs_handle, symlink, 0, DEVFS_FL_DEFAULT, + dirname + pos, 0, &slave, NULL); + dev->part[minor].de = + devfs_register (dir, "disc", 4, devfs_flags, dev->major, minor, + S_IFBLK | S_IRUSR | S_IWUSR, 0, 0, dev->fops,NULL); + devfs_auto_unregister (dev->part[minor].de, slave); + if (!dev->de_arr) + devfs_auto_unregister (slave, dir); +} +#endif /* CONFIG_DEVFS_FS */ + +void devfs_register_partitions (struct gendisk *dev, int minor, int unregister) +{ +#ifdef CONFIG_DEVFS_FS + int part; + + if (!unregister) + devfs_register_disc (dev, minor); + for (part = 1; part < dev->max_p; part++) { + if ( unregister || (dev->part[part + minor].nr_sects < 1) ) { + devfs_unregister (dev->part[part + minor].de); + dev->part[part + minor].de = NULL; + continue; + } + devfs_register_partition (dev, minor, part); + } + if (unregister) { + devfs_unregister (dev->part[minor].de); + dev->part[minor].de = NULL; + } +#endif /* CONFIG_DEVFS_FS */ } /* diff --git a/fs/partitions/check.h b/fs/partitions/check.h index 804d54517..c2dde3a97 100644 --- a/fs/partitions/check.h +++ b/fs/partitions/check.h @@ -8,3 +8,5 @@ void add_gd_partition(struct gendisk *hd, int minor, int start, int size); * Get the default block size for this device */ unsigned int get_ptable_blocksize(kdev_t dev); + +extern int warn_no_part; diff --git a/fs/partitions/msdos.c b/fs/partitions/msdos.c index 8ab3f071a..904b6b0ed 100644 --- a/fs/partitions/msdos.c +++ b/fs/partitions/msdos.c @@ -353,7 +353,7 @@ int msdos_partition(struct gendisk *hd, kdev_t dev, read_mbr: #endif if (!(bh = bread(dev,0,get_ptable_blocksize(dev)))) { - printk(" unable to read partition table\n"); + if (warn_no_part) printk(" unable to read partition table\n"); return -1; } data = bh->b_data; diff --git a/fs/partitions/osf.c b/fs/partitions/osf.c index 6cd1540c0..7982302a3 100644 --- a/fs/partitions/osf.c +++ b/fs/partitions/osf.c @@ -57,7 +57,7 @@ int osf_partition(struct gendisk *hd, kdev_t dev, unsigned long first_sector, struct d_partition * partition; if (!(bh = bread(dev,0,get_ptable_blocksize(dev)))) { - printk("unable to read partition table\n"); + if (warn_no_part) printk("unable to read partition table\n"); return -1; } label = (struct disklabel *) (bh->b_data+64); diff --git a/fs/partitions/sgi.c b/fs/partitions/sgi.c index db9c1ae39..8027abda9 100644 --- a/fs/partitions/sgi.c +++ b/fs/partitions/sgi.c @@ -44,7 +44,7 @@ int sgi_partition(struct gendisk *hd, kdev_t dev, unsigned long first_sector, in struct sgi_partition *p; if(!(bh = bread(dev, 0, get_ptable_blocksize(dev)))) { - printk(KERN_WARNING "Dev %s: unable to read partition table\n", kdevname(dev)); + if (warn_no_part) printk(KERN_WARNING "Dev %s: unable to read partition table\n", kdevname(dev)); return -1; } label = (struct sgi_disklabel *) bh->b_data; diff --git a/fs/partitions/sun.c b/fs/partitions/sun.c index 5f31cde4b..f48bc5cd8 100644 --- a/fs/partitions/sun.c +++ b/fs/partitions/sun.c @@ -48,7 +48,7 @@ int sun_partition(struct gendisk *hd, kdev_t dev, unsigned long first_sector, in unsigned long spc; if(!(bh = bread(dev, 0, get_ptable_blocksize(dev)))) { - printk(KERN_WARNING "Dev %s: unable to read partition table\n", + if (warn_no_part) printk(KERN_WARNING "Dev %s: unable to read partition table\n", kdevname(dev)); return -1; } diff --git a/fs/partitions/ultrix.c b/fs/partitions/ultrix.c index 4170a1bf8..aa49ba517 100644 --- a/fs/partitions/ultrix.c +++ b/fs/partitions/ultrix.c @@ -36,7 +36,7 @@ int ultrix_partition(struct gendisk *hd, kdev_t dev, bh = bread (dev, SBLOCK, get_ptable_blocksize(dev)); if (!bh) { - printk (" unable to read block 0x%lx\n", SBLOCK); + if (warn_no_part) printk (" unable to read block 0x%lx\n", SBLOCK); return -1; } diff --git a/fs/proc/inode.c b/fs/proc/inode.c index 607a7d7e9..ffe846e5c 100644 --- a/fs/proc/inode.c +++ b/fs/proc/inode.c @@ -15,6 +15,7 @@ #include <linux/limits.h> #define __NO_VERSION__ #include <linux/module.h> +#include <linux/smp_lock.h> #include <asm/system.h> #include <asm/uaccess.h> @@ -34,6 +35,7 @@ struct proc_dir_entry * de_get(struct proc_dir_entry *de) void de_put(struct proc_dir_entry *de) { if (de) { + lock_kernel(); /* FIXME: count should be atomic_t */ if (!de->count) { printk("de_put: entry %s already free!\n", de->name); return; @@ -46,6 +48,7 @@ void de_put(struct proc_dir_entry *de) free_proc_entry(de); } } + unlock_kernel(); } } @@ -66,6 +69,8 @@ static void proc_delete_inode(struct inode *inode) { struct proc_dir_entry *de = inode->u.generic_ip; + inode->i_state = I_CLEAR; + if (PROC_INODE_PROPER(inode)) { proc_pid_delete_inode(inode); return; diff --git a/fs/qnx4/bitmap.c b/fs/qnx4/bitmap.c index 56e7790bb..b185960db 100644 --- a/fs/qnx4/bitmap.c +++ b/fs/qnx4/bitmap.c @@ -61,11 +61,11 @@ void count_bits(const register char *bmPart, register int size, unsigned long qnx4_count_free_blocks(struct super_block *sb) { - int start = sb->u.qnx4_sb.BitMap->di_first_xtnt.xtnt_blk - 1; + int start = le32_to_cpu(sb->u.qnx4_sb.BitMap->di_first_xtnt.xtnt_blk) - 1; int total = 0; int total_free = 0; int offset = 0; - int size = sb->u.qnx4_sb.BitMap->di_size; + int size = le32_to_cpu(sb->u.qnx4_sb.BitMap->di_size); struct buffer_head *bh; while (total < size) { @@ -86,8 +86,8 @@ unsigned long qnx4_count_free_blocks(struct super_block *sb) int qnx4_is_free(struct super_block *sb, long block) { - int start = sb->u.qnx4_sb.BitMap->di_first_xtnt.xtnt_blk - 1; - int size = sb->u.qnx4_sb.BitMap->di_size; + int start = le32_to_cpu(sb->u.qnx4_sb.BitMap->di_first_xtnt.xtnt_blk) - 1; + int size = le32_to_cpu(sb->u.qnx4_sb.BitMap->di_size); struct buffer_head *bh; const char *g; int ret = -EIO; @@ -115,8 +115,8 @@ int qnx4_is_free(struct super_block *sb, long block) int qnx4_set_bitmap(struct super_block *sb, long block, int busy) { - int start = sb->u.qnx4_sb.BitMap->di_first_xtnt.xtnt_blk - 1; - int size = sb->u.qnx4_sb.BitMap->di_size; + int start = le32_to_cpu(sb->u.qnx4_sb.BitMap->di_first_xtnt.xtnt_blk) - 1; + int size = le32_to_cpu(sb->u.qnx4_sb.BitMap->di_size); struct buffer_head *bh; char *g; diff --git a/fs/qnx4/inode.c b/fs/qnx4/inode.c index abb6d8260..ed405897a 100644 --- a/fs/qnx4/inode.c +++ b/fs/qnx4/inode.c @@ -98,15 +98,15 @@ static void qnx4_write_inode(struct inode *inode) } raw_inode = ((struct qnx4_inode_entry *) bh->b_data) + (ino % QNX4_INODES_PER_BLOCK); - raw_inode->di_mode = inode->i_mode; - raw_inode->di_uid = fs_high2lowuid(inode->i_uid); - raw_inode->di_gid = fs_high2lowgid(inode->i_gid); - raw_inode->di_nlink = inode->i_nlink; - raw_inode->di_size = inode->i_size; - raw_inode->di_mtime = inode->i_mtime; - raw_inode->di_atime = inode->i_atime; - raw_inode->di_ctime = inode->i_ctime; - raw_inode->di_first_xtnt.xtnt_size = inode->i_blocks; + raw_inode->di_mode = cpu_to_le16(inode->i_mode); + raw_inode->di_uid = cpu_to_le16(fs_high2lowuid(inode->i_uid)); + raw_inode->di_gid = cpu_to_le16(fs_high2lowgid(inode->i_gid)); + raw_inode->di_nlink = cpu_to_le16(inode->i_nlink); + raw_inode->di_size = cpu_to_le32(inode->i_size); + raw_inode->di_mtime = cpu_to_le32(inode->i_mtime); + raw_inode->di_atime = cpu_to_le32(inode->i_atime); + raw_inode->di_ctime = cpu_to_le32(inode->i_ctime); + raw_inode->di_first_xtnt.xtnt_size = cpu_to_le32(inode->i_blocks); mark_buffer_dirty(bh, 1); brelse(bh); } @@ -224,15 +224,15 @@ unsigned long qnx4_block_map( struct inode *inode, long iblock ) struct buffer_head *bh = 0; struct qnx4_xblk *xblk = 0; struct qnx4_inode_info *qnx4_inode = &inode->u.qnx4_i; - qnx4_nxtnt_t nxtnt = qnx4_inode->i_num_xtnts; + qnx4_nxtnt_t nxtnt = le16_to_cpu(qnx4_inode->i_num_xtnts); - if ( iblock < qnx4_inode->i_first_xtnt.xtnt_size ) { + if ( iblock < le32_to_cpu(qnx4_inode->i_first_xtnt.xtnt_size) ) { // iblock is in the first extent. This is easy. - block = qnx4_inode->i_first_xtnt.xtnt_blk + iblock - 1; + block = le32_to_cpu(qnx4_inode->i_first_xtnt.xtnt_blk) + iblock - 1; } else { // iblock is beyond first extent. We have to follow the extent chain. i_xblk = qnx4_inode->i_xblk; - offset = iblock - qnx4_inode->i_first_xtnt.xtnt_size; + offset = iblock - le32_to_cpu(qnx4_inode->i_first_xtnt.xtnt_size); ix = 0; while ( --nxtnt > 0 ) { if ( ix == 0 ) { @@ -248,14 +248,14 @@ unsigned long qnx4_block_map( struct inode *inode, long iblock ) return -EIO; } } - if ( offset < xblk->xblk_xtnts[ix].xtnt_size ) { + if ( offset < le32_to_cpu(xblk->xblk_xtnts[ix].xtnt_size) ) { // got it! - block = xblk->xblk_xtnts[ix].xtnt_blk + offset - 1; + block = le32_to_cpu(xblk->xblk_xtnts[ix].xtnt_blk) + offset - 1; break; } - offset -= xblk->xblk_xtnts[ix].xtnt_size; + offset -= le32_to_cpu(xblk->xblk_xtnts[ix].xtnt_size); if ( ++ix >= xblk->xblk_num_xtnts ) { - i_xblk = xblk->xblk_next_xblk; + i_xblk = le32_to_cpu(xblk->xblk_next_xblk); ix = 0; brelse( bh ); bh = 0; @@ -304,8 +304,8 @@ static const char *qnx4_checkroot(struct super_block *sb) return "no qnx4 filesystem (no root dir)."; } else { QNX4DEBUG(("QNX4 filesystem found on dev %s.\n", kdevname(sb->s_dev))); - rd = sb->u.qnx4_sb.sb->RootDir.di_first_xtnt.xtnt_blk - 1; - rl = sb->u.qnx4_sb.sb->RootDir.di_first_xtnt.xtnt_size; + rd = le32_to_cpu(sb->u.qnx4_sb.sb->RootDir.di_first_xtnt.xtnt_blk) - 1; + rl = le32_to_cpu(sb->u.qnx4_sb.sb->RootDir.di_first_xtnt.xtnt_size); for (j = 0; j < rl; j++) { bh = bread(sb->s_dev, rd + j, QNX4_BLOCK_SIZE); /* root dir, first block */ if (bh == NULL) { @@ -347,7 +347,7 @@ static struct super_block *qnx4_read_super(struct super_block *s, lock_super(s); set_blocksize(dev, QNX4_BLOCK_SIZE); s->s_blocksize = QNX4_BLOCK_SIZE; - s->s_blocksize_bits = 9; + s->s_blocksize_bits = QNX4_BLOCK_SIZE_BITS; s->s_dev = dev; /* Check the boot signature. Since the qnx4 code is @@ -474,15 +474,15 @@ static void qnx4_read_inode(struct inode *inode) raw_inode = ((struct qnx4_inode_entry *) bh->b_data) + (ino % QNX4_INODES_PER_BLOCK); - inode->i_mode = raw_inode->di_mode; - inode->i_uid = (uid_t)raw_inode->di_uid; - inode->i_gid = (gid_t)raw_inode->di_gid; - inode->i_nlink = raw_inode->di_nlink; - inode->i_size = raw_inode->di_size; - inode->i_mtime = raw_inode->di_mtime; - inode->i_atime = raw_inode->di_atime; - inode->i_ctime = raw_inode->di_ctime; - inode->i_blocks = raw_inode->di_first_xtnt.xtnt_size; + inode->i_mode = le16_to_cpu(raw_inode->di_mode); + inode->i_uid = (uid_t)le16_to_cpu(raw_inode->di_uid); + inode->i_gid = (gid_t)le16_to_cpu(raw_inode->di_gid); + inode->i_nlink = le16_to_cpu(raw_inode->di_nlink); + inode->i_size = le32_to_cpu(raw_inode->di_size); + inode->i_mtime = le32_to_cpu(raw_inode->di_mtime); + inode->i_atime = le32_to_cpu(raw_inode->di_atime); + inode->i_ctime = le32_to_cpu(raw_inode->di_ctime); + inode->i_blocks = le32_to_cpu(raw_inode->di_first_xtnt.xtnt_size); inode->i_blksize = QNX4_DIR_ENTRY_SIZE; memcpy(&inode->u.qnx4_i, (struct qnx4_inode_info *) raw_inode, QNX4_DIR_ENTRY_SIZE); @@ -497,9 +497,7 @@ static void qnx4_read_inode(struct inode *inode) inode->i_mapping->a_ops = &qnx4_aops; inode->u.qnx4_i.mmu_private = inode->i_size; } else - /* HUH??? Where is device number? Oh, well... */ - init_special_inode(inode, inode->i_mode, 0); - + printk("qnx4: bad inode %d on dev %s\n",ino,kdevname(inode->i_dev)); brelse(bh); } @@ -513,7 +511,7 @@ static struct file_system_type qnx4_fs_type = int __init init_qnx4_fs(void) { - printk("QNX4 filesystem 0.2.1 registered.\n"); + printk("QNX4 filesystem 0.2.2 registered.\n"); return register_filesystem(&qnx4_fs_type); } diff --git a/fs/qnx4/namei.c b/fs/qnx4/namei.c index 16dde5e5f..0defa3dcb 100644 --- a/fs/qnx4/namei.c +++ b/fs/qnx4/namei.c @@ -120,7 +120,8 @@ struct dentry * qnx4_lookup(struct inode *dir, struct dentry *dentry) /* The entry is linked, let's get the real info */ if ((de->di_status & QNX4_FILE_LINK) == QNX4_FILE_LINK) { lnk = (struct qnx4_link_info *) de; - ino = (lnk->dl_inode_blk - 1) * QNX4_INODES_PER_BLOCK + + ino = (le32_to_cpu(lnk->dl_inode_blk) - 1) * + QNX4_INODES_PER_BLOCK + lnk->dl_inode_ndx; } brelse(bh); diff --git a/fs/read_write.c b/fs/read_write.c index c15ce4c73..6abc2c396 100644 --- a/fs/read_write.c +++ b/fs/read_write.c @@ -170,12 +170,14 @@ static ssize_t do_readv_writev(int type, struct file *file, unsigned long count) { typedef ssize_t (*io_fn_t)(struct file *, char *, size_t, loff_t *); + typedef ssize_t (*iov_fn_t)(struct file *, const struct iovec *, unsigned long, loff_t *); size_t tot_len; struct iovec iovstack[UIO_FASTIOV]; struct iovec *iov=iovstack; ssize_t ret, i; io_fn_t fn; + iov_fn_t fnv; struct inode *inode; /* @@ -188,6 +190,8 @@ static ssize_t do_readv_writev(int type, struct file *file, ret = -EINVAL; if (count > UIO_MAXIOV) goto out_nofree; + if (!file->f_op) + goto out_nofree; if (count > UIO_FASTIOV) { ret = -ENOMEM; iov = kmalloc(count*sizeof(struct iovec), GFP_KERNEL); @@ -209,24 +213,15 @@ static ssize_t do_readv_writev(int type, struct file *file, inode, file, file->f_pos, tot_len); if (ret) goto out; - /* - * Then do the actual IO. Note that sockets need to be handled - * specially as they have atomicity guarantees and can handle - * iovec's natively - */ - if (inode->i_sock) { - ret = sock_readv_writev(type,inode,file,iov,count,tot_len); + fnv = (type == VERIFY_WRITE ? file->f_op->readv : file->f_op->writev); + if (fnv) { + ret = fnv(file, iov, count, &file->f_pos); goto out; } - ret = -EINVAL; - if (!file->f_op) - goto out; - /* VERIFY_WRITE actually means a read, as we write to user space */ - fn = file->f_op->read; - if (type == VERIFY_READ) - fn = (io_fn_t) file->f_op->write; + fn = (type == VERIFY_WRITE ? file->f_op->read : + (io_fn_t) file->f_op->write); ret = 0; vector = iov; @@ -269,7 +264,8 @@ asmlinkage ssize_t sys_readv(unsigned long fd, const struct iovec * vector, file = fget(fd); if (!file) goto bad_file; - if (file->f_op && file->f_op->read && (file->f_mode & FMODE_READ)) + if (file->f_op && (file->f_mode & FMODE_READ) && + (file->f_op->readv || file->f_op->read)) ret = do_readv_writev(VERIFY_WRITE, file, vector, count); fput(file); @@ -288,7 +284,8 @@ asmlinkage ssize_t sys_writev(unsigned long fd, const struct iovec * vector, file = fget(fd); if (!file) goto bad_file; - if (file->f_op && file->f_op->write && (file->f_mode & FMODE_WRITE)) + if (file->f_op && (file->f_mode & FMODE_WRITE) && + (file->f_op->writev || file->f_op->write)) ret = do_readv_writev(VERIFY_READ, file, vector, count); fput(file); diff --git a/fs/smbfs/inode.c b/fs/smbfs/inode.c index c071263ff..9a7560aa4 100644 --- a/fs/smbfs/inode.c +++ b/fs/smbfs/inode.c @@ -483,15 +483,7 @@ dentry->d_parent->d_name.name, dentry->d_name.name, attr->ia_size); if (error) goto out; - /* - * We don't implement an i_op->truncate operation, - * so we have to update the page cache here. - */ - if (attr->ia_size < inode->i_size) - { - truncate_inode_pages(inode, attr->ia_size); - inode->i_size = attr->ia_size; - } + vmtruncate(inode, attr->ia_size); refresh = 1; } diff --git a/fs/super.c b/fs/super.c index 4e7146fcf..e41e1d71d 100644 --- a/fs/super.c +++ b/fs/super.c @@ -15,12 +15,14 @@ * * Added kerneld support: Jacques Gelinas and Bjorn Ekwall * Added change_root: Werner Almesberger & Hans Lermen, Feb '96 + * Added devfs support: Richard Gooch <rgooch@atnf.csiro.au>, 13-JAN-1998 */ #include <linux/config.h> #include <linux/malloc.h> #include <linux/locks.h> #include <linux/smp_lock.h> +#include <linux/devfs_fs_kernel.h> #include <linux/fd.h> #include <linux/init.h> #include <linux/quotaops.h> @@ -563,6 +565,7 @@ static struct super_block * read_super(kdev_t dev, struct block_device *bdev, s->s_flags = flags; s->s_dirt = 0; sema_init(&s->s_vfs_rename_sem,1); + sema_init(&s->s_nfsd_free_path_sem,1); /* N.B. Should lock superblock now ... */ if (!type->read_super(s, data, silent)) goto out_fail; @@ -1080,6 +1083,8 @@ asmlinkage long sys_mount(char * dev_name, char * dir_name, char * type, goto out; if (fstype->fs_flags & FS_REQUIRES_DEV) { + struct block_device_operations *bdops; + dentry = namei(dev_name); retval = PTR_ERR(dentry); if (IS_ERR(dentry)) @@ -1095,6 +1100,8 @@ asmlinkage long sys_mount(char * dev_name, char * dir_name, char * type, goto dput_and_out; bdev = inode->i_bdev; + bdops = devfs_get_ops ( devfs_get_handle_from_inode (inode) ); + if (bdops) bdev->bd_op = bdops; } page = 0; @@ -1123,6 +1130,9 @@ void __init mount_root(void) struct block_device *bdev = NULL; mode_t mode; int retval; + void *handle; + char path[64]; + int path_start = -1; #ifdef CONFIG_ROOT_NFS if (MAJOR(ROOT_DEV) == UNNAMED_MAJOR) { @@ -1133,6 +1143,7 @@ void __init mount_root(void) sb->s_bdev = NULL; sb->s_flags = root_mountflags; sema_init(&sb->s_vfs_rename_sem,1); + sema_init(&sb->s_nfsd_free_path_sem,1); vfsmnt = add_vfsmnt(sb, "/dev/root", "/"); if (vfsmnt) { if (nfs_root_mount(sb) >= 0) { @@ -1178,9 +1189,22 @@ void __init mount_root(void) } #endif + devfs_make_root (root_device_name); + handle = devfs_find_handle (NULL, ROOT_DEVICE_NAME, 0, + MAJOR (ROOT_DEV), MINOR (ROOT_DEV), + DEVFS_SPECIAL_BLK, 1); + if (handle) /* Sigh: bd*() functions only paper over the cracks */ + { + unsigned major, minor; + + devfs_get_maj_min (handle, &major, &minor); + ROOT_DEV = MKDEV (major, minor); + } bdev = bdget(kdev_t_to_nr(ROOT_DEV)); if (!bdev) panic(__FUNCTION__ ": unable to allocate root device"); + bdev->bd_op = devfs_get_ops (handle); + path_start = devfs_generate_path (handle, path + 5, sizeof (path) - 5); mode = FMODE_READ; if (!(root_mountflags & MS_RDONLY)) mode |= FMODE_WRITE; @@ -1189,13 +1213,15 @@ void __init mount_root(void) root_mountflags |= MS_RDONLY; retval = blkdev_get(bdev, FMODE_READ, 0, BDEV_FS); } - if (retval) + if (retval) { /* * Allow the user to distinguish between failed open * and bad superblock on root device. */ - printk("VFS: Cannot open root device %s\n", - kdevname(ROOT_DEV)); + printk ("VFS: Cannot open root device \"%s\" or %s\n", + root_device_name, kdevname (ROOT_DEV)); + printk ("Please append a correct \"root=\" boot option\n"); + } else for (fs_type = file_systems ; fs_type ; fs_type = fs_type->next) { if (!(fs_type->fs_flags & FS_REQUIRES_DEV)) continue; @@ -1207,7 +1233,16 @@ void __init mount_root(void) printk ("VFS: Mounted root (%s filesystem)%s.\n", fs_type->name, (sb->s_flags & MS_RDONLY) ? " readonly" : ""); - vfsmnt = add_vfsmnt(sb, "/dev/root", "/"); + if (path_start >= 0) { + devfs_mk_symlink (NULL, + "root", 0, DEVFS_FL_DEFAULT, + path + 5 + path_start, 0, + NULL, NULL); + memcpy (path + path_start, "/dev/", 5); + vfsmnt = add_vfsmnt (sb, path + path_start, + "/"); + } + else vfsmnt = add_vfsmnt (sb, "/dev/root", "/"); if (vfsmnt) { bdput(bdev); /* sb holds a reference */ return; @@ -1343,6 +1378,18 @@ int __init change_root(kdev_t new_root_dev,const char *put_old) printk(KERN_CRIT "New root is busy. Staying in initrd.\n"); return -EBUSY; } + /* First unmount devfs if mounted */ + dir_d = lookup_dentry ("/dev", NULL, 1); + if (!IS_ERR(dir_d)) { + struct super_block *sb = dir_d->d_inode->i_sb; + + if (sb && (dir_d->d_inode == sb->s_root->d_inode) && + (sb->s_magic == DEVFS_SUPER_MAGIC)) { + dput (dir_d); + do_umount (sb->s_dev, 0, 0); + } + else dput (dir_d); + } ROOT_DEV = new_root_dev; mount_root(); dput(old_root); @@ -1351,6 +1398,7 @@ int __init change_root(kdev_t new_root_dev,const char *put_old) shrink_dcache(); printk("change_root: old root has d_count=%d\n", old_root->d_count); #endif + mount_devfs_fs (); /* * Get the new mount directory */ diff --git a/fs/tunnel.c b/fs/tunnel.c new file mode 100644 index 000000000..8fee49a00 --- /dev/null +++ b/fs/tunnel.c @@ -0,0 +1,50 @@ +/* fs/tunnel.c: utility functions to support VFS tunnelling + + Copyright (C) 1999 Richard Gooch + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free + Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + Richard Gooch may be reached by email at rgooch@atnf.csiro.au + The postal address is: + Richard Gooch, c/o ATNF, P. O. Box 76, Epping, N.S.W., 2121, Australia. + + ChangeLog + + 19991121 Richard Gooch <rgooch@atnf.csiro.au> + Created. +*/ +#include <linux/types.h> +#include <linux/errno.h> +#include <linux/ctype.h> +#include <linux/module.h> +#include <linux/fs.h> + + +/*PUBLIC_FUNCTION*/ +struct dentry *vfs_tunnel_lookup (const struct dentry *dentry, + const struct dentry *parent, + const struct dentry *covered) +/* [SUMMARY] Lookup the corresponding dentry in the mounted-over FS. + <dentry> The dentry which is in the overmounting FS. + <parent> The parent of the dentry in the mounted-over FS. This may be NULL. + <covered> The dentry covered by the root dentry of the overmounting FS. + [RETURNS] A dentry on success, else NULL. +*/ +{ + struct dentry *root = dentry->d_sb->s_root; + + if (covered == root) return NULL; + if (parent) return lookup_dentry (dentry->d_name.name, parent, 0); +} /* End Function vfs_tunnel_lookup */ diff --git a/fs/udf/file.c b/fs/udf/file.c index 5cf51cd0e..546ac95b8 100644 --- a/fs/udf/file.c +++ b/fs/udf/file.c @@ -145,6 +145,9 @@ static int udf_adinicb_commit_write(struct file *file, struct page *page, unsign brelse(bh); kunmap(page); SetPageUptodate(page); + /* only one page here */ + if (to > inode->i_size) + inode->i_size = to; return 0; } diff --git a/fs/umsdos/inode.c b/fs/umsdos/inode.c index fb8dc03ff..95d6528f3 100644 --- a/fs/umsdos/inode.c +++ b/fs/umsdos/inode.c @@ -170,11 +170,19 @@ int umsdos_notify_change_locked(struct dentry *, struct iattr *); int UMSDOS_notify_change (struct dentry *dentry, struct iattr *attr) { struct inode *dir = dentry->d_parent->d_inode; + struct inode *inode = dentry->d_inode; int ret; + ret = inode_change_ok (inode, attr); + if (ret) + goto out; + down(&dir->i_sem); ret = umsdos_notify_change_locked(dentry, attr); up(&dir->i_sem); + if (ret == 0) + inode_setattr (inode, attr); +out: return ret; } @@ -185,20 +193,13 @@ int umsdos_notify_change_locked(struct dentry *dentry, struct iattr *attr) { struct inode *inode = dentry->d_inode; struct dentry *demd; - int ret; + int ret = 0; struct file filp; struct umsdos_dirent entry; Printk(("UMSDOS_notify_change: entering for %s/%s (%d)\n", dentry->d_parent->d_name.name, dentry->d_name.name, inode->u.umsdos_i.i_patched)); - ret = inode_change_ok (inode, attr); - if (ret) { -printk("UMSDOS_notify_change: %s/%s change not OK, ret=%d\n", -dentry->d_parent->d_name.name, dentry->d_name.name, ret); - goto out; - } - if (inode->i_nlink == 0) goto out; if (inode->i_ino == UMSDOS_ROOT_INO) @@ -276,8 +277,6 @@ dentry->d_parent->d_name.name, dentry->d_name.name, entry.nlink, ret); out_dput: dput(demd); out: - if (ret == 0) - inode_setattr (inode, attr); return ret; } diff --git a/fs/umsdos/namei.c b/fs/umsdos/namei.c index 415f5e3fb..08bb7b340 100644 --- a/fs/umsdos/namei.c +++ b/fs/umsdos/namei.c @@ -731,6 +731,8 @@ olddentry->d_parent->d_name.name, olddentry->d_name.name, oldinode->i_ino, oldinode->i_nlink)); newattrs.ia_valid = 0; ret = umsdos_notify_change_locked(olddentry, &newattrs); + if (ret == 0) + mark_inode_dirty(olddentry->d_inode); } if (olddir != dir) up(&olddir->i_sem); @@ -1069,6 +1071,8 @@ link->d_parent->d_name.name, link->d_name.name, ret)); inode->i_nlink--; newattrs.ia_valid = 0; ret = umsdos_notify_change_locked(link, &newattrs); + if (!ret) + mark_inode_dirty(link->d_inode); } out_cleanup: |