summaryrefslogtreecommitdiffstats
path: root/fs
diff options
context:
space:
mode:
authorRalf Baechle <ralf@linux-mips.org>2000-02-24 00:12:35 +0000
committerRalf Baechle <ralf@linux-mips.org>2000-02-24 00:12:35 +0000
commit482368b1a8e45430672c58c9a42e7d2004367126 (patch)
treece2a1a567d4d62dee7c2e71a46a99cf72cf1d606 /fs
parente4d0251c6f56ab2e191afb70f80f382793e23f74 (diff)
Merge with 2.3.47. Guys, this is buggy as shit. You've been warned.
Diffstat (limited to 'fs')
-rw-r--r--fs/Config.in79
-rw-r--r--fs/Makefile8
-rw-r--r--fs/adfs/file.c4
-rw-r--r--fs/adfs/inode.c34
-rw-r--r--fs/adfs/map.c10
-rw-r--r--fs/affs/inode.c3
-rw-r--r--fs/attr.c2
-rw-r--r--fs/bfs/file.c39
-rw-r--r--fs/binfmt_elf.c8
-rw-r--r--fs/binfmt_script.c12
-rw-r--r--fs/block_dev.c16
-rw-r--r--fs/buffer.c8
-rw-r--r--fs/coda/psdev.c16
-rw-r--r--fs/devfs/.cvsignore2
-rw-r--r--fs/devfs/Makefile39
-rw-r--r--fs/devfs/base.c3467
-rw-r--r--fs/devfs/util.c160
-rw-r--r--fs/ext2/inode.c8
-rw-r--r--fs/fat/cache.c2
-rw-r--r--fs/filesystems.c3
-rw-r--r--fs/hpfs/namei.c15
-rw-r--r--fs/inode.c92
-rw-r--r--fs/lockd/Makefile5
-rw-r--r--fs/lockd/clntproc.c2
-rw-r--r--fs/lockd/lockd_syms.c11
-rw-r--r--fs/lockd/svc.c4
-rw-r--r--fs/lockd/svc4proc.c561
-rw-r--r--fs/lockd/svclock.c20
-rw-r--r--fs/lockd/svcproc.c45
-rw-r--r--fs/lockd/svcsubs.c6
-rw-r--r--fs/lockd/xdr.c19
-rw-r--r--fs/lockd/xdr4.c601
-rw-r--r--fs/locks.c9
-rw-r--r--fs/minix/inode.c6
-rw-r--r--fs/ncpfs/inode.c2
-rw-r--r--fs/ncpfs/sock.c3
-rw-r--r--fs/nfs/file.c5
-rw-r--r--fs/nfs/inode.c6
-rw-r--r--fs/nfsd/lockd.c11
-rw-r--r--fs/nfsd/nfs3xdr.c42
-rw-r--r--fs/nfsd/nfsfh.c74
-rw-r--r--fs/nfsd/nfssvc.c105
-rw-r--r--fs/nfsd/vfs.c45
-rw-r--r--fs/open.c14
-rw-r--r--fs/openpromfs/inode.c2
-rw-r--r--fs/partitions/Config.in3
-rw-r--r--fs/partitions/acorn.c2
-rw-r--r--fs/partitions/amiga.c4
-rw-r--r--fs/partitions/atari.c2
-rw-r--r--fs/partitions/check.c118
-rw-r--r--fs/partitions/check.h2
-rw-r--r--fs/partitions/msdos.c2
-rw-r--r--fs/partitions/osf.c2
-rw-r--r--fs/partitions/sgi.c2
-rw-r--r--fs/partitions/sun.c2
-rw-r--r--fs/partitions/ultrix.c2
-rw-r--r--fs/proc/inode.c5
-rw-r--r--fs/qnx4/bitmap.c12
-rw-r--r--fs/qnx4/inode.c64
-rw-r--r--fs/qnx4/namei.c3
-rw-r--r--fs/read_write.c29
-rw-r--r--fs/smbfs/inode.c10
-rw-r--r--fs/super.c56
-rw-r--r--fs/tunnel.c50
-rw-r--r--fs/udf/file.c3
-rw-r--r--fs/umsdos/inode.c19
-rw-r--r--fs/umsdos/namei.c4
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;
}
diff --git a/fs/attr.c b/fs/attr.c
index 21adfd00e..1639308b9 100644
--- a/fs/attr.c
+++ b/fs/attr.c
@@ -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;
}
diff --git a/fs/open.c b/fs/open.c
index 159b08bf7..7896b0a2e 100644
--- a/fs/open.c
+++ b/fs/open.c
@@ -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: