summaryrefslogtreecommitdiffstats
path: root/fs
diff options
context:
space:
mode:
Diffstat (limited to 'fs')
-rw-r--r--fs/9p/mux.c2
-rw-r--r--fs/9p/v9fs_vfs.h2
-rw-r--r--fs/9p/vfs_addr.c2
-rw-r--r--fs/9p/vfs_inode.c2
-rw-r--r--fs/Kconfig42
-rw-r--r--fs/Makefile1
-rw-r--r--fs/adfs/inode.c2
-rw-r--r--fs/affs/affs.h6
-rw-r--r--fs/affs/file.c4
-rw-r--r--fs/affs/symlink.c2
-rw-r--r--fs/afs/file.c2
-rw-r--r--fs/afs/internal.h2
-rw-r--r--fs/befs/linuxvfs.c2
-rw-r--r--fs/bfs/bfs.h2
-rw-r--r--fs/bfs/file.c2
-rw-r--r--fs/block_dev.c3
-rw-r--r--fs/buffer.c5
-rw-r--r--fs/char_dev.c1
-rw-r--r--fs/cifs/CHANGES17
-rw-r--r--fs/cifs/Makefile2
-rw-r--r--fs/cifs/README39
-rw-r--r--fs/cifs/asn1.c10
-rw-r--r--fs/cifs/cifs_debug.c134
-rw-r--r--fs/cifs/cifs_debug.h4
-rw-r--r--fs/cifs/cifs_unicode.c1
-rw-r--r--fs/cifs/cifsencrypt.c140
-rw-r--r--fs/cifs/cifsfs.c6
-rw-r--r--fs/cifs/cifsfs.h5
-rw-r--r--fs/cifs/cifsglob.h71
-rw-r--r--fs/cifs/cifspdu.h98
-rw-r--r--fs/cifs/cifsproto.h14
-rw-r--r--fs/cifs/cifssmb.c287
-rw-r--r--fs/cifs/connect.c498
-rw-r--r--fs/cifs/dir.c15
-rw-r--r--fs/cifs/fcntl.c4
-rw-r--r--fs/cifs/file.c54
-rw-r--r--fs/cifs/inode.c39
-rw-r--r--fs/cifs/link.c7
-rw-r--r--fs/cifs/misc.c10
-rw-r--r--fs/cifs/netmisc.c4
-rw-r--r--fs/cifs/ntlmssp.c143
-rw-r--r--fs/cifs/readdir.c184
-rw-r--r--fs/cifs/sess.c538
-rw-r--r--fs/cifs/smbencrypt.c1
-rw-r--r--fs/cifs/transport.c3
-rw-r--r--fs/coda/psdev.c23
-rw-r--r--fs/coda/symlink.c2
-rw-r--r--fs/compat_ioctl.c1
-rw-r--r--fs/configfs/inode.c2
-rw-r--r--fs/cramfs/inode.c4
-rw-r--r--fs/devfs/Makefile8
-rw-r--r--fs/devfs/base.c2836
-rw-r--r--fs/devfs/util.c97
-rw-r--r--fs/efs/inode.c2
-rw-r--r--fs/efs/symlink.c2
-rw-r--r--fs/ext2/ext2.h6
-rw-r--r--fs/ext2/inode.c6
-rw-r--r--fs/ext3/inode.c6
-rw-r--r--fs/fat/inode.c2
-rw-r--r--fs/freevxfs/vxfs_immed.c2
-rw-r--r--fs/freevxfs/vxfs_inode.c6
-rw-r--r--fs/freevxfs/vxfs_subr.c2
-rw-r--r--fs/fuse/file.c2
-rw-r--r--fs/hfs/hfs_fs.h4
-rw-r--r--fs/hfs/inode.c4
-rw-r--r--fs/hfsplus/hfsplus_fs.h4
-rw-r--r--fs/hfsplus/inode.c4
-rw-r--r--fs/hostfs/hostfs_kern.c6
-rw-r--r--fs/hpfs/file.c2
-rw-r--r--fs/hpfs/hpfs_fn.h4
-rw-r--r--fs/hpfs/namei.c2
-rw-r--r--fs/hugetlbfs/inode.c4
-rw-r--r--fs/inode.c2
-rw-r--r--fs/isofs/compress.c2
-rw-r--r--fs/isofs/inode.c2
-rw-r--r--fs/isofs/isofs.h2
-rw-r--r--fs/isofs/rock.c2
-rw-r--r--fs/isofs/zisofs.h2
-rw-r--r--fs/jbd/journal.c3
-rw-r--r--fs/jffs/inode-v23.c4
-rw-r--r--fs/jffs2/acl.c2
-rw-r--r--fs/jffs2/erase.c19
-rw-r--r--fs/jffs2/file.c2
-rw-r--r--fs/jffs2/fs.c2
-rw-r--r--fs/jffs2/gc.c6
-rw-r--r--fs/jffs2/jffs2_fs_sb.h3
-rw-r--r--fs/jffs2/malloc.c2
-rw-r--r--fs/jffs2/nodelist.c3
-rw-r--r--fs/jffs2/nodemgmt.c21
-rw-r--r--fs/jffs2/os-linux.h2
-rw-r--r--fs/jffs2/readinode.c1
-rw-r--r--fs/jffs2/scan.c55
-rw-r--r--fs/jffs2/summary.c41
-rw-r--r--fs/jffs2/xattr.c632
-rw-r--r--fs/jffs2/xattr.h21
-rw-r--r--fs/jfs/inode.c2
-rw-r--r--fs/jfs/jfs_inode.h2
-rw-r--r--fs/jfs/jfs_metapage.c2
-rw-r--r--fs/jfs/jfs_metapage.h2
-rw-r--r--fs/minix/inode.c2
-rw-r--r--fs/ncpfs/inode.c2
-rw-r--r--fs/ncpfs/symlink.c2
-rw-r--r--fs/nfs/direct.c2
-rw-r--r--fs/nfs/file.c2
-rw-r--r--fs/nfs/inode.c2
-rw-r--r--fs/nfs/internal.h8
-rw-r--r--fs/nfs/pagelist.c2
-rw-r--r--fs/nfs/read.c2
-rw-r--r--fs/nfs/write.c2
-rw-r--r--fs/nfsd/nfs4state.c2
-rw-r--r--fs/ntfs/aops.c4
-rw-r--r--fs/ntfs/ntfs.h4
-rw-r--r--fs/ocfs2/aops.c2
-rw-r--r--fs/ocfs2/cluster/heartbeat.c2
-rw-r--r--fs/ocfs2/cluster/tcp.c2
-rw-r--r--fs/ocfs2/dlm/dlmdomain.c2
-rw-r--r--fs/ocfs2/dlm/dlmlock.c2
-rw-r--r--fs/ocfs2/dlm/dlmrecovery.c4
-rw-r--r--fs/ocfs2/dlmglue.c2
-rw-r--r--fs/ocfs2/inode.h2
-rw-r--r--fs/ocfs2/journal.c2
-rw-r--r--fs/ocfs2/vote.c8
-rw-r--r--fs/partitions/Makefile1
-rw-r--r--fs/partitions/check.c32
-rw-r--r--fs/partitions/devfs.c130
-rw-r--r--fs/partitions/devfs.h10
-rw-r--r--fs/proc/task_mmu.c30
-rw-r--r--fs/qnx4/inode.c2
-rw-r--r--fs/ramfs/file-mmu.c2
-rw-r--r--fs/ramfs/file-nommu.c2
-rw-r--r--fs/ramfs/internal.h2
-rw-r--r--fs/reiserfs/inode.c2
-rw-r--r--fs/romfs/inode.c2
-rw-r--r--fs/smbfs/file.c2
-rw-r--r--fs/smbfs/proto.h2
-rw-r--r--fs/sysfs/inode.c2
-rw-r--r--fs/sysv/itree.c2
-rw-r--r--fs/sysv/sysv.h2
-rw-r--r--fs/udf/file.c2
-rw-r--r--fs/udf/inode.c2
-rw-r--r--fs/udf/symlink.c2
-rw-r--r--fs/udf/udfdecl.h6
-rw-r--r--fs/ufs/inode.c119
-rw-r--r--fs/xfs/linux-2.6/xfs_aops.c2
-rw-r--r--fs/xfs/linux-2.6/xfs_aops.h2
-rw-r--r--fs/xfs/linux-2.6/xfs_buf.c2
-rw-r--r--fs/xfs/linux-2.6/xfs_iops.c9
-rw-r--r--fs/xfs/linux-2.6/xfs_linux.h2
-rw-r--r--fs/xfs/linux-2.6/xfs_vnode.h2
-rw-r--r--fs/xfs/xfs_behavior.h3
-rw-r--r--fs/xfs/xfs_inode.c4
-rw-r--r--fs/xfs/xfs_log.c4
-rw-r--r--fs/xfs/xfs_log_recover.c2
-rw-r--r--fs/xfs/xfs_mount.c21
-rw-r--r--fs/xfs/xfs_rtalloc.c2
-rw-r--r--fs/xfs/xfs_trans.h4
-rw-r--r--fs/xfs/xfs_vnodeops.c11
157 files changed, 2254 insertions, 4530 deletions
diff --git a/fs/9p/mux.c b/fs/9p/mux.c
index 12e1baa4508d..8d45ed668837 100644
--- a/fs/9p/mux.c
+++ b/fs/9p/mux.c
@@ -932,6 +932,8 @@ v9fs_mux_rpc(struct v9fs_mux_data *m, struct v9fs_fcall *tc,
r.rcall || r.err);
} while (!r.rcall && !r.err && err==-ERESTARTSYS &&
m->trans->status==Connected && !m->err);
+
+ err = -ERESTARTSYS;
}
sigpending = 1;
}
diff --git a/fs/9p/v9fs_vfs.h b/fs/9p/v9fs_vfs.h
index f867b8d3e973..450b0c1b385e 100644
--- a/fs/9p/v9fs_vfs.h
+++ b/fs/9p/v9fs_vfs.h
@@ -38,7 +38,7 @@
*/
extern struct file_system_type v9fs_fs_type;
-extern struct address_space_operations v9fs_addr_operations;
+extern const struct address_space_operations v9fs_addr_operations;
extern const struct file_operations v9fs_file_operations;
extern const struct file_operations v9fs_dir_operations;
extern struct dentry_operations v9fs_dentry_operations;
diff --git a/fs/9p/vfs_addr.c b/fs/9p/vfs_addr.c
index efda46fb64d9..d4f0aa3c87f2 100644
--- a/fs/9p/vfs_addr.c
+++ b/fs/9p/vfs_addr.c
@@ -103,6 +103,6 @@ UnmapAndUnlock:
return retval;
}
-struct address_space_operations v9fs_addr_operations = {
+const struct address_space_operations v9fs_addr_operations = {
.readpage = v9fs_vfs_readpage,
};
diff --git a/fs/9p/vfs_inode.c b/fs/9p/vfs_inode.c
index 5c6bdf82146c..2f580a197b8d 100644
--- a/fs/9p/vfs_inode.c
+++ b/fs/9p/vfs_inode.c
@@ -300,7 +300,7 @@ clunk_fid:
fid = V9FS_NOFID;
put_fid:
- if (fid >= 0)
+ if (fid != V9FS_NOFID)
v9fs_put_idpool(fid, &v9ses->fidpool);
kfree(fcall);
diff --git a/fs/Kconfig b/fs/Kconfig
index 6c5051802bd2..6dc8cfd6d80c 100644
--- a/fs/Kconfig
+++ b/fs/Kconfig
@@ -1116,7 +1116,7 @@ config JFFS2_SUMMARY
config JFFS2_FS_XATTR
bool "JFFS2 XATTR support (EXPERIMENTAL)"
- depends on JFFS2_FS && EXPERIMENTAL && !JFFS2_FS_WRITEBUFFER
+ depends on JFFS2_FS && EXPERIMENTAL
default n
help
Extended attributes are name:value pairs associated with inodes by
@@ -1722,7 +1722,7 @@ config CIFS_STATS
mounted by the cifs client to be displayed in /proc/fs/cifs/Stats
config CIFS_STATS2
- bool "CIFS extended statistics"
+ bool "Extended statistics"
depends on CIFS_STATS
help
Enabling this option will allow more detailed statistics on SMB
@@ -1735,6 +1735,32 @@ config CIFS_STATS2
Unless you are a developer or are doing network performance analysis
or tuning, say N.
+config CIFS_WEAK_PW_HASH
+ bool "Support legacy servers which use weaker LANMAN security"
+ depends on CIFS
+ help
+ Modern CIFS servers including Samba and most Windows versions
+ (since 1997) support stronger NTLM (and even NTLMv2 and Kerberos)
+ security mechanisms. These hash the password more securely
+ than the mechanisms used in the older LANMAN version of the
+ SMB protocol needed to establish sessions with old SMB servers.
+
+ Enabling this option allows the cifs module to mount to older
+ LANMAN based servers such as OS/2 and Windows 95, but such
+ mounts may be less secure than mounts using NTLM or more recent
+ security mechanisms if you are on a public network. Unless you
+ have a need to access old SMB servers (and are on a private
+ network) you probably want to say N. Even if this support
+ is enabled in the kernel build, they will not be used
+ automatically. At runtime LANMAN mounts are disabled but
+ can be set to required (or optional) either in
+ /proc/fs/cifs (see fs/cifs/README for more detail) or via an
+ option on the mount command. This support is disabled by
+ default in order to reduce the possibility of a downgrade
+ attack.
+
+ If unsure, say N.
+
config CIFS_XATTR
bool "CIFS extended attributes"
depends on CIFS
@@ -1763,6 +1789,16 @@ config CIFS_POSIX
(such as Samba 3.10 and later) which can negotiate
CIFS POSIX ACL support. If unsure, say N.
+config CIFS_DEBUG2
+ bool "Enable additional CIFS debugging routines"
+ help
+ Enabling this option adds a few more debugging routines
+ to the cifs code which slightly increases the size of
+ the cifs module and can cause additional logging of debug
+ messages in some error paths, slowing performance. This
+ option can be turned off unless you are debugging
+ cifs problems. If unsure, say N.
+
config CIFS_EXPERIMENTAL
bool "CIFS Experimental Features (EXPERIMENTAL)"
depends on CIFS && EXPERIMENTAL
@@ -1778,7 +1814,7 @@ config CIFS_EXPERIMENTAL
If unsure, say N.
config CIFS_UPCALL
- bool "CIFS Kerberos/SPNEGO advanced session setup (EXPERIMENTAL)"
+ bool "Kerberos/SPNEGO advanced session setup (EXPERIMENTAL)"
depends on CIFS_EXPERIMENTAL
select CONNECTOR
help
diff --git a/fs/Makefile b/fs/Makefile
index d0ea6bfccf29..89135428a539 100644
--- a/fs/Makefile
+++ b/fs/Makefile
@@ -66,7 +66,6 @@ obj-$(CONFIG_MSDOS_FS) += msdos/
obj-$(CONFIG_VFAT_FS) += vfat/
obj-$(CONFIG_BFS_FS) += bfs/
obj-$(CONFIG_ISO9660_FS) += isofs/
-obj-$(CONFIG_DEVFS_FS) += devfs/
obj-$(CONFIG_HFSPLUS_FS) += hfsplus/ # Before hfs to find wrapped HFS+
obj-$(CONFIG_HFS_FS) += hfs/
obj-$(CONFIG_VXFS_FS) += freevxfs/
diff --git a/fs/adfs/inode.c b/fs/adfs/inode.c
index a02802a30798..534f3eecc985 100644
--- a/fs/adfs/inode.c
+++ b/fs/adfs/inode.c
@@ -72,7 +72,7 @@ static sector_t _adfs_bmap(struct address_space *mapping, sector_t block)
return generic_block_bmap(mapping, block, adfs_get_block);
}
-static struct address_space_operations adfs_aops = {
+static const struct address_space_operations adfs_aops = {
.readpage = adfs_readpage,
.writepage = adfs_writepage,
.sync_page = block_sync_page,
diff --git a/fs/affs/affs.h b/fs/affs/affs.h
index a43a876742b8..0ddd4cc0d1a0 100644
--- a/fs/affs/affs.h
+++ b/fs/affs/affs.h
@@ -195,9 +195,9 @@ extern struct inode_operations affs_symlink_inode_operations;
extern const struct file_operations affs_file_operations;
extern const struct file_operations affs_file_operations_ofs;
extern const struct file_operations affs_dir_operations;
-extern struct address_space_operations affs_symlink_aops;
-extern struct address_space_operations affs_aops;
-extern struct address_space_operations affs_aops_ofs;
+extern const struct address_space_operations affs_symlink_aops;
+extern const struct address_space_operations affs_aops;
+extern const struct address_space_operations affs_aops_ofs;
extern struct dentry_operations affs_dentry_operations;
extern struct dentry_operations affs_dentry_operations_intl;
diff --git a/fs/affs/file.c b/fs/affs/file.c
index 7076262af39b..3de8590e4f6a 100644
--- a/fs/affs/file.c
+++ b/fs/affs/file.c
@@ -406,7 +406,7 @@ static sector_t _affs_bmap(struct address_space *mapping, sector_t block)
{
return generic_block_bmap(mapping,block,affs_get_block);
}
-struct address_space_operations affs_aops = {
+const struct address_space_operations affs_aops = {
.readpage = affs_readpage,
.writepage = affs_writepage,
.sync_page = block_sync_page,
@@ -759,7 +759,7 @@ out:
goto done;
}
-struct address_space_operations affs_aops_ofs = {
+const struct address_space_operations affs_aops_ofs = {
.readpage = affs_readpage_ofs,
//.writepage = affs_writepage_ofs,
//.sync_page = affs_sync_page_ofs,
diff --git a/fs/affs/symlink.c b/fs/affs/symlink.c
index 426f0f094f23..f802256a5933 100644
--- a/fs/affs/symlink.c
+++ b/fs/affs/symlink.c
@@ -66,7 +66,7 @@ fail:
return err;
}
-struct address_space_operations affs_symlink_aops = {
+const struct address_space_operations affs_symlink_aops = {
.readpage = affs_symlink_readpage,
};
diff --git a/fs/afs/file.c b/fs/afs/file.c
index 7bb716887e29..67d6634101fd 100644
--- a/fs/afs/file.c
+++ b/fs/afs/file.c
@@ -35,7 +35,7 @@ struct inode_operations afs_file_inode_operations = {
.getattr = afs_inode_getattr,
};
-struct address_space_operations afs_fs_aops = {
+const struct address_space_operations afs_fs_aops = {
.readpage = afs_file_readpage,
.sync_page = block_sync_page,
.set_page_dirty = __set_page_dirty_nobuffers,
diff --git a/fs/afs/internal.h b/fs/afs/internal.h
index 72febdf9a35a..e88b3b65ae49 100644
--- a/fs/afs/internal.h
+++ b/fs/afs/internal.h
@@ -69,7 +69,7 @@ extern const struct file_operations afs_dir_file_operations;
/*
* file.c
*/
-extern struct address_space_operations afs_fs_aops;
+extern const struct address_space_operations afs_fs_aops;
extern struct inode_operations afs_file_inode_operations;
#ifdef AFS_CACHING_SUPPORT
diff --git a/fs/befs/linuxvfs.c b/fs/befs/linuxvfs.c
index 08201fab26cd..a83e889a97cd 100644
--- a/fs/befs/linuxvfs.c
+++ b/fs/befs/linuxvfs.c
@@ -73,7 +73,7 @@ static struct inode_operations befs_dir_inode_operations = {
.lookup = befs_lookup,
};
-static struct address_space_operations befs_aops = {
+static const struct address_space_operations befs_aops = {
.readpage = befs_readpage,
.sync_page = block_sync_page,
.bmap = befs_bmap,
diff --git a/fs/bfs/bfs.h b/fs/bfs/bfs.h
index 9d791004b21c..31973bbbf057 100644
--- a/fs/bfs/bfs.h
+++ b/fs/bfs/bfs.h
@@ -50,7 +50,7 @@ static inline struct bfs_inode_info *BFS_I(struct inode *inode)
/* file.c */
extern struct inode_operations bfs_file_inops;
extern const struct file_operations bfs_file_operations;
-extern struct address_space_operations bfs_aops;
+extern const struct address_space_operations bfs_aops;
/* dir.c */
extern struct inode_operations bfs_dir_inops;
diff --git a/fs/bfs/file.c b/fs/bfs/file.c
index d83cd74a2e4e..3d5aca28a0a0 100644
--- a/fs/bfs/file.c
+++ b/fs/bfs/file.c
@@ -153,7 +153,7 @@ static sector_t bfs_bmap(struct address_space *mapping, sector_t block)
return generic_block_bmap(mapping, block, bfs_get_block);
}
-struct address_space_operations bfs_aops = {
+const struct address_space_operations bfs_aops = {
.readpage = bfs_readpage,
.writepage = bfs_writepage,
.sync_page = block_sync_page,
diff --git a/fs/block_dev.c b/fs/block_dev.c
index 028d9fb9c2d5..909cb0595b4e 100644
--- a/fs/block_dev.c
+++ b/fs/block_dev.c
@@ -12,7 +12,6 @@
#include <linux/slab.h>
#include <linux/kmod.h>
#include <linux/major.h>
-#include <linux/devfs_fs_kernel.h>
#include <linux/smp_lock.h>
#include <linux/highmem.h>
#include <linux/blkdev.h>
@@ -1095,7 +1094,7 @@ static long block_ioctl(struct file *file, unsigned cmd, unsigned long arg)
return blkdev_ioctl(file->f_mapping->host, file, cmd, arg);
}
-struct address_space_operations def_blk_aops = {
+const struct address_space_operations def_blk_aops = {
.readpage = blkdev_readpage,
.writepage = blkdev_writepage,
.sync_page = block_sync_page,
diff --git a/fs/buffer.c b/fs/buffer.c
index 373bb6292bdc..e9994722f4a3 100644
--- a/fs/buffer.c
+++ b/fs/buffer.c
@@ -564,7 +564,7 @@ still_busy:
* Completion handler for block_write_full_page() - pages which are unlocked
* during I/O, and which have PageWriteback cleared upon I/O completion.
*/
-void end_buffer_async_write(struct buffer_head *bh, int uptodate)
+static void end_buffer_async_write(struct buffer_head *bh, int uptodate)
{
char b[BDEVNAME_SIZE];
unsigned long flags;
@@ -2598,7 +2598,7 @@ int nobh_truncate_page(struct address_space *mapping, loff_t from)
unsigned offset = from & (PAGE_CACHE_SIZE-1);
unsigned to;
struct page *page;
- struct address_space_operations *a_ops = mapping->a_ops;
+ const struct address_space_operations *a_ops = mapping->a_ops;
char *kaddr;
int ret = 0;
@@ -3166,7 +3166,6 @@ EXPORT_SYMBOL(block_sync_page);
EXPORT_SYMBOL(block_truncate_page);
EXPORT_SYMBOL(block_write_full_page);
EXPORT_SYMBOL(cont_prepare_write);
-EXPORT_SYMBOL(end_buffer_async_write);
EXPORT_SYMBOL(end_buffer_read_sync);
EXPORT_SYMBOL(end_buffer_write_sync);
EXPORT_SYMBOL(file_fsync);
diff --git a/fs/char_dev.c b/fs/char_dev.c
index f3418f7a6e9d..97986635b641 100644
--- a/fs/char_dev.c
+++ b/fs/char_dev.c
@@ -14,7 +14,6 @@
#include <linux/errno.h>
#include <linux/module.h>
#include <linux/smp_lock.h>
-#include <linux/devfs_fs_kernel.h>
#include <linux/seq_file.h>
#include <linux/kobject.h>
diff --git a/fs/cifs/CHANGES b/fs/cifs/CHANGES
index 7271bb0257f6..a61d17ed1827 100644
--- a/fs/cifs/CHANGES
+++ b/fs/cifs/CHANGES
@@ -1,9 +1,24 @@
+Version 1.44
+------------
+Rewritten sessionsetup support, including support for legacy SMB
+session setup needed for OS/2 and older servers such as Windows 95 and 98.
+Fix oops on ls to OS/2 servers. Add support for level 1 FindFirst
+so we can do search (ls etc.) to OS/2. Do not send NTCreateX
+or recent levels of FindFirst unless server says it supports NT SMBs
+(instead use legacy equivalents from LANMAN dialect). Fix to allow
+NTLMv2 authentication support (now can use stronger password hashing
+on mount if corresponding /proc/fs/cifs/SecurityFlags is set (0x4004).
+Allow override of global cifs security flags on mount via "sec=" option(s).
+
Version 1.43
------------
POSIX locking to servers which support CIFS POSIX Extensions
(disabled by default controlled by proc/fs/cifs/Experimental).
Handle conversion of long share names (especially Asian languages)
-to Unicode during mount.
+to Unicode during mount. Fix memory leak in sess struct on reconnect.
+Fix rare oops after acpi suspend. Fix O_TRUNC opens to overwrite on
+cifs open which helps rare case when setpathinfo fails or server does
+not support it.
Version 1.42
------------
diff --git a/fs/cifs/Makefile b/fs/cifs/Makefile
index 58c77254a23b..a26f26ed5a17 100644
--- a/fs/cifs/Makefile
+++ b/fs/cifs/Makefile
@@ -3,4 +3,4 @@
#
obj-$(CONFIG_CIFS) += cifs.o
-cifs-objs := cifsfs.o cifssmb.o cifs_debug.o connect.o dir.o file.o inode.o link.o misc.o netmisc.o smbdes.o smbencrypt.o transport.o asn1.o md4.o md5.o cifs_unicode.o nterr.o xattr.o cifsencrypt.o fcntl.o readdir.o ioctl.o ntlmssp.o
+cifs-objs := cifsfs.o cifssmb.o cifs_debug.o connect.o dir.o file.o inode.o link.o misc.o netmisc.o smbdes.o smbencrypt.o transport.o asn1.o md4.o md5.o cifs_unicode.o nterr.o xattr.o cifsencrypt.o fcntl.o readdir.o ioctl.o sess.o
diff --git a/fs/cifs/README b/fs/cifs/README
index 0355003f4f0a..7986d0d97ace 100644
--- a/fs/cifs/README
+++ b/fs/cifs/README
@@ -443,7 +443,10 @@ A partial list of the supported mount options follows:
SFU does). In the future the bottom 9 bits of the mode
mode also will be emulated using queries of the security
descriptor (ACL).
-sec Security mode. Allowed values are:
+ sign Must use packet signing (helps avoid unwanted data modification
+ by intermediate systems in the route). Note that signing
+ does not work with lanman or plaintext authentication.
+ sec Security mode. Allowed values are:
none attempt to connection as a null user (no name)
krb5 Use Kerberos version 5 authentication
krb5i Use Kerberos authentication and packet signing
@@ -453,6 +456,8 @@ sec Security mode. Allowed values are:
server requires signing also can be the default)
ntlmv2 Use NTLMv2 password hashing
ntlmv2i Use NTLMv2 password hashing with packet signing
+ lanman (if configured in kernel config) use older
+ lanman hash
The mount.cifs mount helper also accepts a few mount options before -o
including:
@@ -485,14 +490,34 @@ PacketSigningEnabled If set to one, cifs packet signing is enabled
it. If set to two, cifs packet signing is
required even if the server considers packet
signing optional. (default 1)
+SecurityFlags Flags which control security negotiation and
+ also packet signing. Authentication (may/must)
+ flags (e.g. for NTLM and/or NTLMv2) may be combined with
+ the signing flags. Specifying two different password
+ hashing mechanisms (as "must use") on the other hand
+ does not make much sense. Default flags are
+ 0x07007
+ (NTLM, NTLMv2 and packet signing allowed). Maximum
+ allowable flags if you want to allow mounts to servers
+ using weaker password hashes is 0x37037 (lanman,
+ plaintext, ntlm, ntlmv2, signing allowed):
+
+ may use packet signing 0x00001
+ must use packet signing 0x01001
+ may use NTLM (most common password hash) 0x00002
+ must use NTLM 0x02002
+ may use NTLMv2 0x00004
+ must use NTLMv2 0x04004
+ may use Kerberos security (not implemented yet) 0x00008
+ must use Kerberos (not implemented yet) 0x08008
+ may use lanman (weak) password hash 0x00010
+ must use lanman password hash 0x10010
+ may use plaintext passwords 0x00020
+ must use plaintext passwords 0x20020
+ (reserved for future packet encryption) 0x00040
+
cifsFYI If set to one, additional debug information is
logged to the system error log. (default 0)
-ExtendedSecurity If set to one, SPNEGO session establishment
- is allowed which enables more advanced
- secure CIFS session establishment (default 0)
-NTLMV2Enabled If set to one, more secure password hashes
- are used when the server supports them and
- when kerberos is not negotiated (default 0)
traceSMB If set to one, debug information is logged to the
system error log with the start of smb requests
and responses (default 0)
diff --git a/fs/cifs/asn1.c b/fs/cifs/asn1.c
index 086ae8f4a207..031cdf293256 100644
--- a/fs/cifs/asn1.c
+++ b/fs/cifs/asn1.c
@@ -467,7 +467,7 @@ decode_negTokenInit(unsigned char *security_blob, int length,
asn1_open(&ctx, security_blob, length);
if (asn1_header_decode(&ctx, &end, &cls, &con, &tag) == 0) {
- cFYI(1, ("Error decoding negTokenInit header "));
+ cFYI(1, ("Error decoding negTokenInit header"));
return 0;
} else if ((cls != ASN1_APL) || (con != ASN1_CON)
|| (tag != ASN1_EOC)) {
@@ -495,7 +495,7 @@ decode_negTokenInit(unsigned char *security_blob, int length,
}
if (asn1_header_decode(&ctx, &end, &cls, &con, &tag) == 0) {
- cFYI(1, ("Error decoding negTokenInit "));
+ cFYI(1, ("Error decoding negTokenInit"));
return 0;
} else if ((cls != ASN1_CTX) || (con != ASN1_CON)
|| (tag != ASN1_EOC)) {
@@ -505,7 +505,7 @@ decode_negTokenInit(unsigned char *security_blob, int length,
}
if (asn1_header_decode(&ctx, &end, &cls, &con, &tag) == 0) {
- cFYI(1, ("Error decoding negTokenInit "));
+ cFYI(1, ("Error decoding negTokenInit"));
return 0;
} else if ((cls != ASN1_UNI) || (con != ASN1_CON)
|| (tag != ASN1_SEQ)) {
@@ -515,7 +515,7 @@ decode_negTokenInit(unsigned char *security_blob, int length,
}
if (asn1_header_decode(&ctx, &end, &cls, &con, &tag) == 0) {
- cFYI(1, ("Error decoding 2nd part of negTokenInit "));
+ cFYI(1, ("Error decoding 2nd part of negTokenInit"));
return 0;
} else if ((cls != ASN1_CTX) || (con != ASN1_CON)
|| (tag != ASN1_EOC)) {
@@ -527,7 +527,7 @@ decode_negTokenInit(unsigned char *security_blob, int length,
if (asn1_header_decode
(&ctx, &sequence_end, &cls, &con, &tag) == 0) {
- cFYI(1, ("Error decoding 2nd part of negTokenInit "));
+ cFYI(1, ("Error decoding 2nd part of negTokenInit"));
return 0;
} else if ((cls != ASN1_UNI) || (con != ASN1_CON)
|| (tag != ASN1_SEQ)) {
diff --git a/fs/cifs/cifs_debug.c b/fs/cifs/cifs_debug.c
index f4124a32bef8..96abeb738978 100644
--- a/fs/cifs/cifs_debug.c
+++ b/fs/cifs/cifs_debug.c
@@ -39,7 +39,7 @@ cifs_dump_mem(char *label, void *data, int length)
char *charptr = data;
char buf[10], line[80];
- printk(KERN_DEBUG "%s: dump of %d bytes of data at 0x%p\n\n",
+ printk(KERN_DEBUG "%s: dump of %d bytes of data at 0x%p\n",
label, length, data);
for (i = 0; i < length; i += 16) {
line[0] = 0;
@@ -57,6 +57,57 @@ cifs_dump_mem(char *label, void *data, int length)
}
}
+#ifdef CONFIG_CIFS_DEBUG2
+void cifs_dump_detail(struct smb_hdr * smb)
+{
+ cERROR(1,("Cmd: %d Err: 0x%x Flags: 0x%x Flgs2: 0x%x Mid: %d Pid: %d",
+ smb->Command, smb->Status.CifsError,
+ smb->Flags, smb->Flags2, smb->Mid, smb->Pid));
+ cERROR(1,("smb buf %p len %d", smb, smbCalcSize_LE(smb)));
+}
+
+
+void cifs_dump_mids(struct TCP_Server_Info * server)
+{
+ struct list_head *tmp;
+ struct mid_q_entry * mid_entry;
+
+ if(server == NULL)
+ return;
+
+ cERROR(1,("Dump pending requests:"));
+ spin_lock(&GlobalMid_Lock);
+ list_for_each(tmp, &server->pending_mid_q) {
+ mid_entry = list_entry(tmp, struct mid_q_entry, qhead);
+ if(mid_entry) {
+ cERROR(1,("State: %d Cmd: %d Pid: %d Tsk: %p Mid %d",
+ mid_entry->midState,
+ (int)mid_entry->command,
+ mid_entry->pid,
+ mid_entry->tsk,
+ mid_entry->mid));
+#ifdef CONFIG_CIFS_STATS2
+ cERROR(1,("IsLarge: %d buf: %p time rcv: %ld now: %ld",
+ mid_entry->largeBuf,
+ mid_entry->resp_buf,
+ mid_entry->when_received,
+ jiffies));
+#endif /* STATS2 */
+ cERROR(1,("IsMult: %d IsEnd: %d", mid_entry->multiRsp,
+ mid_entry->multiEnd));
+ if(mid_entry->resp_buf) {
+ cifs_dump_detail(mid_entry->resp_buf);
+ cifs_dump_mem("existing buf: ",
+ mid_entry->resp_buf,
+ 62 /* fixme */);
+ }
+
+ }
+ }
+ spin_unlock(&GlobalMid_Lock);
+}
+#endif /* CONFIG_CIFS_DEBUG2 */
+
#ifdef CONFIG_PROC_FS
static int
cifs_debug_data_read(char *buf, char **beginBuffer, off_t offset,
@@ -73,7 +124,6 @@ cifs_debug_data_read(char *buf, char **beginBuffer, off_t offset,
*beginBuffer = buf + offset;
-
length =
sprintf(buf,
"Display Internal CIFS Data Structures for Debugging\n"
@@ -395,12 +445,12 @@ static read_proc_t traceSMB_read;
static write_proc_t traceSMB_write;
static read_proc_t multiuser_mount_read;
static write_proc_t multiuser_mount_write;
-static read_proc_t extended_security_read;
-static write_proc_t extended_security_write;
-static read_proc_t ntlmv2_enabled_read;
+static read_proc_t security_flags_read;
+static write_proc_t security_flags_write;
+/* static read_proc_t ntlmv2_enabled_read;
static write_proc_t ntlmv2_enabled_write;
static read_proc_t packet_signing_enabled_read;
-static write_proc_t packet_signing_enabled_write;
+static write_proc_t packet_signing_enabled_write;*/
static read_proc_t experimEnabled_read;
static write_proc_t experimEnabled_write;
static read_proc_t linuxExtensionsEnabled_read;
@@ -458,10 +508,10 @@ cifs_proc_init(void)
pde->write_proc = multiuser_mount_write;
pde =
- create_proc_read_entry("ExtendedSecurity", 0, proc_fs_cifs,
- extended_security_read, NULL);
+ create_proc_read_entry("SecurityFlags", 0, proc_fs_cifs,
+ security_flags_read, NULL);
if (pde)
- pde->write_proc = extended_security_write;
+ pde->write_proc = security_flags_write;
pde =
create_proc_read_entry("LookupCacheEnabled", 0, proc_fs_cifs,
@@ -469,7 +519,7 @@ cifs_proc_init(void)
if (pde)
pde->write_proc = lookupFlag_write;
- pde =
+/* pde =
create_proc_read_entry("NTLMV2Enabled", 0, proc_fs_cifs,
ntlmv2_enabled_read, NULL);
if (pde)
@@ -479,7 +529,7 @@ cifs_proc_init(void)
create_proc_read_entry("PacketSigningEnabled", 0, proc_fs_cifs,
packet_signing_enabled_read, NULL);
if (pde)
- pde->write_proc = packet_signing_enabled_write;
+ pde->write_proc = packet_signing_enabled_write;*/
}
void
@@ -496,9 +546,9 @@ cifs_proc_clean(void)
#endif
remove_proc_entry("MultiuserMount", proc_fs_cifs);
remove_proc_entry("OplockEnabled", proc_fs_cifs);
- remove_proc_entry("NTLMV2Enabled",proc_fs_cifs);
- remove_proc_entry("ExtendedSecurity",proc_fs_cifs);
- remove_proc_entry("PacketSigningEnabled",proc_fs_cifs);
+/* remove_proc_entry("NTLMV2Enabled",proc_fs_cifs); */
+ remove_proc_entry("SecurityFlags",proc_fs_cifs);
+/* remove_proc_entry("PacketSigningEnabled",proc_fs_cifs); */
remove_proc_entry("LinuxExtensionsEnabled",proc_fs_cifs);
remove_proc_entry("Experimental",proc_fs_cifs);
remove_proc_entry("LookupCacheEnabled",proc_fs_cifs);
@@ -782,12 +832,12 @@ multiuser_mount_write(struct file *file, const char __user *buffer,
}
static int
-extended_security_read(char *page, char **start, off_t off,
+security_flags_read(char *page, char **start, off_t off,
int count, int *eof, void *data)
{
int len;
- len = sprintf(page, "%d\n", extended_security);
+ len = sprintf(page, "0x%x\n", extended_security);
len -= off;
*start = page + off;
@@ -803,24 +853,52 @@ extended_security_read(char *page, char **start, off_t off,
return len;
}
static int
-extended_security_write(struct file *file, const char __user *buffer,
+security_flags_write(struct file *file, const char __user *buffer,
unsigned long count, void *data)
{
+ unsigned int flags;
+ char flags_string[12];
char c;
- int rc;
- rc = get_user(c, buffer);
- if (rc)
- return rc;
- if (c == '0' || c == 'n' || c == 'N')
- extended_security = 0;
- else if (c == '1' || c == 'y' || c == 'Y')
- extended_security = 1;
+ if((count < 1) || (count > 11))
+ return -EINVAL;
+
+ memset(flags_string, 0, 12);
+
+ if(copy_from_user(flags_string, buffer, count))
+ return -EFAULT;
+
+ if(count < 3) {
+ /* single char or single char followed by null */
+ c = flags_string[0];
+ if (c == '0' || c == 'n' || c == 'N')
+ extended_security = CIFSSEC_DEF; /* default */
+ else if (c == '1' || c == 'y' || c == 'Y')
+ extended_security = CIFSSEC_MAX;
+ return count;
+ }
+ /* else we have a number */
+
+ flags = simple_strtoul(flags_string, NULL, 0);
+
+ cFYI(1,("sec flags 0x%x", flags));
+
+ if(flags <= 0) {
+ cERROR(1,("invalid security flags %s",flags_string));
+ return -EINVAL;
+ }
+ if(flags & ~CIFSSEC_MASK) {
+ cERROR(1,("attempt to set unsupported security flags 0x%x",
+ flags & ~CIFSSEC_MASK));
+ return -EINVAL;
+ }
+ /* flags look ok - update the global security flags for cifs module */
+ extended_security = flags;
return count;
}
-static int
+/* static int
ntlmv2_enabled_read(char *page, char **start, off_t off,
int count, int *eof, void *data)
{
@@ -855,6 +933,8 @@ ntlmv2_enabled_write(struct file *file, const char __user *buffer,
ntlmv2_support = 0;
else if (c == '1' || c == 'y' || c == 'Y')
ntlmv2_support = 1;
+ else if (c == '2')
+ ntlmv2_support = 2;
return count;
}
@@ -898,7 +978,7 @@ packet_signing_enabled_write(struct file *file, const char __user *buffer,
sign_CIFS_PDUs = 2;
return count;
-}
+} */
#endif
diff --git a/fs/cifs/cifs_debug.h b/fs/cifs/cifs_debug.h
index 4304d9dcfb6c..c26cd0d2c6d5 100644
--- a/fs/cifs/cifs_debug.h
+++ b/fs/cifs/cifs_debug.h
@@ -24,6 +24,10 @@
#define _H_CIFS_DEBUG
void cifs_dump_mem(char *label, void *data, int length);
+#ifdef CONFIG_CIFS_DEBUG2
+void cifs_dump_detail(struct smb_hdr *);
+void cifs_dump_mids(struct TCP_Server_Info *);
+#endif
extern int traceSMB; /* flag which enables the function below */
void dump_smb(struct smb_hdr *, int);
#define CIFS_INFO 0x01
diff --git a/fs/cifs/cifs_unicode.c b/fs/cifs/cifs_unicode.c
index d2b128255944..d2a8b2941fc2 100644
--- a/fs/cifs/cifs_unicode.c
+++ b/fs/cifs/cifs_unicode.c
@@ -22,6 +22,7 @@
#include "cifs_unicode.h"
#include "cifs_uniupr.h"
#include "cifspdu.h"
+#include "cifsglob.h"
#include "cifs_debug.h"
/*
diff --git a/fs/cifs/cifsencrypt.c b/fs/cifs/cifsencrypt.c
index e7d63737e651..a89efaf78a26 100644
--- a/fs/cifs/cifsencrypt.c
+++ b/fs/cifs/cifsencrypt.c
@@ -26,6 +26,8 @@
#include "md5.h"
#include "cifs_unicode.h"
#include "cifsproto.h"
+#include <linux/ctype.h>
+#include <linux/random.h>
/* Calculate and return the CIFS signature based on the mac key and the smb pdu */
/* the 16 byte signature must be allocated by the caller */
@@ -35,6 +37,8 @@
extern void mdfour(unsigned char *out, unsigned char *in, int n);
extern void E_md4hash(const unsigned char *passwd, unsigned char *p16);
+extern void SMBencrypt(unsigned char *passwd, unsigned char *c8,
+ unsigned char *p24);
static int cifs_calculate_signature(const struct smb_hdr * cifs_pdu,
const char * key, char * signature)
@@ -45,7 +49,7 @@ static int cifs_calculate_signature(const struct smb_hdr * cifs_pdu,
return -EINVAL;
MD5Init(&context);
- MD5Update(&context,key,CIFS_SESSION_KEY_SIZE+16);
+ MD5Update(&context,key,CIFS_SESS_KEY_SIZE+16);
MD5Update(&context,cifs_pdu->Protocol,cifs_pdu->smb_buf_length);
MD5Final(signature,&context);
return 0;
@@ -90,7 +94,7 @@ static int cifs_calc_signature2(const struct kvec * iov, int n_vec,
return -EINVAL;
MD5Init(&context);
- MD5Update(&context,key,CIFS_SESSION_KEY_SIZE+16);
+ MD5Update(&context,key,CIFS_SESS_KEY_SIZE+16);
for(i=0;i<n_vec;i++) {
if(iov[i].iov_base == NULL) {
cERROR(1,("null iovec entry"));
@@ -204,11 +208,12 @@ int cifs_calculate_mac_key(char * key, const char * rn, const char * password)
E_md4hash(password, temp_key);
mdfour(key,temp_key,16);
- memcpy(key+16,rn, CIFS_SESSION_KEY_SIZE);
+ memcpy(key+16,rn, CIFS_SESS_KEY_SIZE);
return 0;
}
-int CalcNTLMv2_partial_mac_key(struct cifsSesInfo * ses, struct nls_table * nls_info)
+int CalcNTLMv2_partial_mac_key(struct cifsSesInfo * ses,
+ const struct nls_table * nls_info)
{
char temp_hash[16];
struct HMACMD5Context ctx;
@@ -225,6 +230,8 @@ int CalcNTLMv2_partial_mac_key(struct cifsSesInfo * ses, struct nls_table * nls_
user_name_len = strlen(ses->userName);
if(user_name_len > MAX_USERNAME_SIZE)
return -EINVAL;
+ if(ses->domainName == NULL)
+ return -EINVAL; /* BB should we use CIFS_LINUX_DOM */
dom_name_len = strlen(ses->domainName);
if(dom_name_len > MAX_USERNAME_SIZE)
return -EINVAL;
@@ -259,16 +266,131 @@ int CalcNTLMv2_partial_mac_key(struct cifsSesInfo * ses, struct nls_table * nls_
kfree(unicode_buf);
return 0;
}
-void CalcNTLMv2_response(const struct cifsSesInfo * ses,char * v2_session_response)
+
+#ifdef CONFIG_CIFS_WEAK_PW_HASH
+void calc_lanman_hash(struct cifsSesInfo * ses, char * lnm_session_key)
+{
+ int i;
+ char password_with_pad[CIFS_ENCPWD_SIZE];
+
+ if(ses->server == NULL)
+ return;
+
+ memset(password_with_pad, 0, CIFS_ENCPWD_SIZE);
+ strncpy(password_with_pad, ses->password, CIFS_ENCPWD_SIZE);
+
+ if((ses->server->secMode & SECMODE_PW_ENCRYPT) == 0)
+ if(extended_security & CIFSSEC_MAY_PLNTXT) {
+ memcpy(lnm_session_key, password_with_pad, CIFS_ENCPWD_SIZE);
+ return;
+ }
+
+ /* calculate old style session key */
+ /* calling toupper is less broken than repeatedly
+ calling nls_toupper would be since that will never
+ work for UTF8, but neither handles multibyte code pages
+ but the only alternative would be converting to UCS-16 (Unicode)
+ (using a routine something like UniStrupr) then
+ uppercasing and then converting back from Unicode - which
+ would only worth doing it if we knew it were utf8. Basically
+ utf8 and other multibyte codepages each need their own strupper
+ function since a byte at a time will ont work. */
+
+ for(i = 0; i < CIFS_ENCPWD_SIZE; i++) {
+ password_with_pad[i] = toupper(password_with_pad[i]);
+ }
+
+ SMBencrypt(password_with_pad, ses->server->cryptKey, lnm_session_key);
+ /* clear password before we return/free memory */
+ memset(password_with_pad, 0, CIFS_ENCPWD_SIZE);
+}
+#endif /* CIFS_WEAK_PW_HASH */
+
+static int calc_ntlmv2_hash(struct cifsSesInfo *ses,
+ const struct nls_table * nls_cp)
+{
+ int rc = 0;
+ int len;
+ char nt_hash[16];
+ struct HMACMD5Context * pctxt;
+ wchar_t * user;
+ wchar_t * domain;
+
+ pctxt = kmalloc(sizeof(struct HMACMD5Context), GFP_KERNEL);
+
+ if(pctxt == NULL)
+ return -ENOMEM;
+
+ /* calculate md4 hash of password */
+ E_md4hash(ses->password, nt_hash);
+
+ /* convert Domainname to unicode and uppercase */
+ hmac_md5_init_limK_to_64(nt_hash, 16, pctxt);
+
+ /* convert ses->userName to unicode and uppercase */
+ len = strlen(ses->userName);
+ user = kmalloc(2 + (len * 2), GFP_KERNEL);
+ if(user == NULL)
+ goto calc_exit_2;
+ len = cifs_strtoUCS(user, ses->userName, len, nls_cp);
+ UniStrupr(user);
+ hmac_md5_update((char *)user, 2*len, pctxt);
+
+ /* convert ses->domainName to unicode and uppercase */
+ if(ses->domainName) {
+ len = strlen(ses->domainName);
+
+ domain = kmalloc(2 + (len * 2), GFP_KERNEL);
+ if(domain == NULL)
+ goto calc_exit_1;
+ len = cifs_strtoUCS(domain, ses->domainName, len, nls_cp);
+ UniStrupr(domain);
+
+ hmac_md5_update((char *)domain, 2*len, pctxt);
+
+ kfree(domain);
+ }
+calc_exit_1:
+ kfree(user);
+calc_exit_2:
+ /* BB FIXME what about bytes 24 through 40 of the signing key?
+ compare with the NTLM example */
+ hmac_md5_final(ses->server->mac_signing_key, pctxt);
+
+ return rc;
+}
+
+void setup_ntlmv2_rsp(struct cifsSesInfo * ses, char * resp_buf,
+ const struct nls_table * nls_cp)
+{
+ int rc;
+ struct ntlmv2_resp * buf = (struct ntlmv2_resp *)resp_buf;
+
+ buf->blob_signature = cpu_to_le32(0x00000101);
+ buf->reserved = 0;
+ buf->time = cpu_to_le64(cifs_UnixTimeToNT(CURRENT_TIME));
+ get_random_bytes(&buf->client_chal, sizeof(buf->client_chal));
+ buf->reserved2 = 0;
+ buf->names[0].type = 0;
+ buf->names[0].length = 0;
+
+ /* calculate buf->ntlmv2_hash */
+ rc = calc_ntlmv2_hash(ses, nls_cp);
+ if(rc)
+ cERROR(1,("could not get v2 hash rc %d",rc));
+ CalcNTLMv2_response(ses, resp_buf);
+}
+
+void CalcNTLMv2_response(const struct cifsSesInfo * ses, char * v2_session_response)
{
struct HMACMD5Context context;
+ /* rest of v2 struct already generated */
memcpy(v2_session_response + 8, ses->server->cryptKey,8);
- /* gen_blob(v2_session_response + 16); */
hmac_md5_init_limK_to_64(ses->server->mac_signing_key, 16, &context);
- hmac_md5_update(ses->server->cryptKey,8,&context);
-/* hmac_md5_update(v2_session_response+16)client thing,8,&context); */ /* BB fix */
+ hmac_md5_update(v2_session_response+8,
+ sizeof(struct ntlmv2_resp) - 8, &context);
hmac_md5_final(v2_session_response,&context);
- cifs_dump_mem("v2_sess_rsp: ", v2_session_response, 32); /* BB removeme BB */
+/* cifs_dump_mem("v2_sess_rsp: ", v2_session_response, 32); */
}
diff --git a/fs/cifs/cifsfs.c b/fs/cifs/cifsfs.c
index 8b4de6eaabd0..c28ede599946 100644
--- a/fs/cifs/cifsfs.c
+++ b/fs/cifs/cifsfs.c
@@ -56,8 +56,8 @@ unsigned int experimEnabled = 0;
unsigned int linuxExtEnabled = 1;
unsigned int lookupCacheEnabled = 1;
unsigned int multiuser_mount = 0;
-unsigned int extended_security = 0;
-unsigned int ntlmv2_support = 0;
+unsigned int extended_security = CIFSSEC_DEF;
+/* unsigned int ntlmv2_support = 0; */
unsigned int sign_CIFS_PDUs = 1;
extern struct task_struct * oplockThread; /* remove sparse warning */
struct task_struct * oplockThread = NULL;
@@ -908,7 +908,7 @@ static int cifs_dnotify_thread(void * dummyarg)
struct cifsSesInfo *ses;
do {
- if(try_to_freeze())
+ if (try_to_freeze())
continue;
set_current_state(TASK_INTERRUPTIBLE);
schedule_timeout(15*HZ);
diff --git a/fs/cifs/cifsfs.h b/fs/cifs/cifsfs.h
index d56c0577c710..8f75c6f24701 100644
--- a/fs/cifs/cifsfs.h
+++ b/fs/cifs/cifsfs.h
@@ -32,7 +32,8 @@
#define TRUE 1
#endif
-extern struct address_space_operations cifs_addr_ops;
+extern const struct address_space_operations cifs_addr_ops;
+extern const struct address_space_operations cifs_addr_ops_smallbuf;
/* Functions related to super block operations */
extern struct super_operations cifs_super_ops;
@@ -99,5 +100,5 @@ extern ssize_t cifs_getxattr(struct dentry *, const char *, void *, size_t);
extern ssize_t cifs_listxattr(struct dentry *, char *, size_t);
extern int cifs_ioctl (struct inode * inode, struct file * filep,
unsigned int command, unsigned long arg);
-#define CIFS_VERSION "1.43"
+#define CIFS_VERSION "1.44"
#endif /* _CIFSFS_H */
diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h
index 006eb33bff5f..6d7cf5f3bc0b 100644
--- a/fs/cifs/cifsglob.h
+++ b/fs/cifs/cifsglob.h
@@ -88,7 +88,8 @@ enum statusEnum {
};
enum securityEnum {
- NTLM = 0, /* Legacy NTLM012 auth with NTLM hash */
+ LANMAN = 0, /* Legacy LANMAN auth */
+ NTLM, /* Legacy NTLM012 auth with NTLM hash */
NTLMv2, /* Legacy NTLM auth with NTLMv2 hash */
RawNTLMSSP, /* NTLMSSP without SPNEGO */
NTLMSSP, /* NTLMSSP via SPNEGO */
@@ -157,7 +158,7 @@ struct TCP_Server_Info {
/* 16th byte of RFC1001 workstation name is always null */
char workstation_RFC1001_name[SERVER_NAME_LEN_WITH_NULL];
__u32 sequence_number; /* needed for CIFS PDU signature */
- char mac_signing_key[CIFS_SESSION_KEY_SIZE + 16];
+ char mac_signing_key[CIFS_SESS_KEY_SIZE + 16];
};
/*
@@ -179,10 +180,13 @@ struct cifsUidInfo {
struct cifsSesInfo {
struct list_head cifsSessionList;
struct semaphore sesSem;
+#if 0
struct cifsUidInfo *uidInfo; /* pointer to user info */
+#endif
struct TCP_Server_Info *server; /* pointer to server info */
atomic_t inUse; /* # of mounts (tree connections) on this ses */
enum statusEnum status;
+ unsigned overrideSecFlg; /* if non-zero override global sec flags */
__u16 ipc_tid; /* special tid for connection to IPC share */
__u16 flags;
char *serverOS; /* name of operating system underlying server */
@@ -194,7 +198,7 @@ struct cifsSesInfo {
char serverName[SERVER_NAME_LEN_WITH_NULL * 2]; /* BB make bigger for
TCP names - will ipv6 and sctp addresses fit? */
char userName[MAX_USERNAME_SIZE + 1];
- char domainName[MAX_USERNAME_SIZE + 1];
+ char * domainName;
char * password;
};
/* session flags */
@@ -209,12 +213,12 @@ struct cifsTconInfo {
struct list_head openFileList;
struct semaphore tconSem;
struct cifsSesInfo *ses; /* pointer to session associated with */
- char treeName[MAX_TREE_SIZE + 1]; /* UNC name of resource (in ASCII not UTF) */
+ char treeName[MAX_TREE_SIZE + 1]; /* UNC name of resource in ASCII */
char *nativeFileSystem;
__u16 tid; /* The 2 byte tree id */
__u16 Flags; /* optional support bits */
enum statusEnum tidStatus;
- atomic_t useCount; /* how many mounts (explicit or implicit) to this share */
+ atomic_t useCount; /* how many explicit/implicit mounts to share */
#ifdef CONFIG_CIFS_STATS
atomic_t num_smbs_sent;
atomic_t num_writes;
@@ -254,7 +258,7 @@ struct cifsTconInfo {
spinlock_t stat_lock;
#endif /* CONFIG_CIFS_STATS */
FILE_SYSTEM_DEVICE_INFO fsDevInfo;
- FILE_SYSTEM_ATTRIBUTE_INFO fsAttrInfo; /* ok if file system name truncated */
+ FILE_SYSTEM_ATTRIBUTE_INFO fsAttrInfo; /* ok if fs name truncated */
FILE_SYSTEM_UNIX_INFO fsUnixInfo;
unsigned retry:1;
unsigned nocase:1;
@@ -305,7 +309,6 @@ struct cifsFileInfo {
atomic_t wrtPending; /* handle in use - defer close */
struct semaphore fh_sem; /* prevents reopen race after dead ses*/
char * search_resume_name; /* BB removeme BB */
- unsigned int resume_name_length; /* BB removeme - field renamed and moved BB */
struct cifs_search_info srch_inf;
};
@@ -391,9 +394,9 @@ struct mid_q_entry {
struct smb_hdr *resp_buf; /* response buffer */
int midState; /* wish this were enum but can not pass to wait_event */
__u8 command; /* smb command code */
- unsigned multiPart:1; /* multiple responses to one SMB request */
unsigned largeBuf:1; /* if valid response, is pointer to large buf */
- unsigned multiResp:1; /* multiple trans2 responses for one request */
+ unsigned multiRsp:1; /* multiple trans2 responses for one request */
+ unsigned multiEnd:1; /* both received */
};
struct oplock_q_entry {
@@ -430,15 +433,35 @@ struct dir_notify_req {
#define CIFS_LARGE_BUFFER 2
#define CIFS_IOVEC 4 /* array of response buffers */
-/* Type of session setup needed */
-#define CIFS_PLAINTEXT 0
-#define CIFS_LANMAN 1
-#define CIFS_NTLM 2
-#define CIFS_NTLMSSP_NEG 3
-#define CIFS_NTLMSSP_AUTH 4
-#define CIFS_SPNEGO_INIT 5
-#define CIFS_SPNEGO_TARG 6
-
+/* Security Flags: indicate type of session setup needed */
+#define CIFSSEC_MAY_SIGN 0x00001
+#define CIFSSEC_MAY_NTLM 0x00002
+#define CIFSSEC_MAY_NTLMV2 0x00004
+#define CIFSSEC_MAY_KRB5 0x00008
+#ifdef CONFIG_CIFS_WEAK_PW_HASH
+#define CIFSSEC_MAY_LANMAN 0x00010
+#define CIFSSEC_MAY_PLNTXT 0x00020
+#endif /* weak passwords */
+#define CIFSSEC_MAY_SEAL 0x00040 /* not supported yet */
+
+#define CIFSSEC_MUST_SIGN 0x01001
+/* note that only one of the following can be set so the
+result of setting MUST flags more than once will be to
+require use of the stronger protocol */
+#define CIFSSEC_MUST_NTLM 0x02002
+#define CIFSSEC_MUST_NTLMV2 0x04004
+#define CIFSSEC_MUST_KRB5 0x08008
+#ifdef CONFIG_CIFS_WEAK_PW_HASH
+#define CIFSSEC_MUST_LANMAN 0x10010
+#define CIFSSEC_MUST_PLNTXT 0x20020
+#define CIFSSEC_MASK 0x37037 /* current flags supported if weak */
+#else
+#define CIFSSEC_MASK 0x07007 /* flags supported if no weak config */
+#endif /* WEAK_PW_HASH */
+#define CIFSSEC_MUST_SEAL 0x40040 /* not supported yet */
+
+#define CIFSSEC_DEF CIFSSEC_MAY_SIGN | CIFSSEC_MAY_NTLM | CIFSSEC_MAY_NTLMV2
+#define CIFSSEC_MAX CIFSSEC_MUST_SIGN | CIFSSEC_MUST_NTLMV2
/*
*****************************************************************
* All constants go here
@@ -500,16 +523,16 @@ GLOBAL_EXTERN rwlock_t GlobalSMBSeslock; /* protects list inserts on 3 above */
GLOBAL_EXTERN struct list_head GlobalOplock_Q;
GLOBAL_EXTERN struct list_head GlobalDnotifyReqList; /* Outstanding dir notify requests */
-GLOBAL_EXTERN struct list_head GlobalDnotifyRsp_Q; /* Dir notify response queue */
+GLOBAL_EXTERN struct list_head GlobalDnotifyRsp_Q;/* DirNotify response queue */
/*
* Global transaction id (XID) information
*/
GLOBAL_EXTERN unsigned int GlobalCurrentXid; /* protected by GlobalMid_Sem */
-GLOBAL_EXTERN unsigned int GlobalTotalActiveXid; /* prot by GlobalMid_Sem */
+GLOBAL_EXTERN unsigned int GlobalTotalActiveXid; /* prot by GlobalMid_Sem */
GLOBAL_EXTERN unsigned int GlobalMaxActiveXid; /* prot by GlobalMid_Sem */
-GLOBAL_EXTERN spinlock_t GlobalMid_Lock; /* protects above and list operations */
- /* on midQ entries */
+GLOBAL_EXTERN spinlock_t GlobalMid_Lock; /* protects above & list operations */
+ /* on midQ entries */
GLOBAL_EXTERN char Local_System_Name[15];
/*
@@ -531,7 +554,7 @@ GLOBAL_EXTERN atomic_t smBufAllocCount;
GLOBAL_EXTERN atomic_t midCount;
/* Misc globals */
-GLOBAL_EXTERN unsigned int multiuser_mount; /* if enabled allows new sessions
+GLOBAL_EXTERN unsigned int multiuser_mount; /* if enabled allows new sessions
to be established on existing mount if we
have the uid/password or Kerberos credential
or equivalent for current user */
@@ -540,8 +563,8 @@ GLOBAL_EXTERN unsigned int experimEnabled;
GLOBAL_EXTERN unsigned int lookupCacheEnabled;
GLOBAL_EXTERN unsigned int extended_security; /* if on, session setup sent
with more secure ntlmssp2 challenge/resp */
-GLOBAL_EXTERN unsigned int ntlmv2_support; /* better optional password hash */
GLOBAL_EXTERN unsigned int sign_CIFS_PDUs; /* enable smb packet signing */
+GLOBAL_EXTERN unsigned int secFlags;
GLOBAL_EXTERN unsigned int linuxExtEnabled;/*enable Linux/Unix CIFS extensions*/
GLOBAL_EXTERN unsigned int CIFSMaxBufSize; /* max size not including hdr */
GLOBAL_EXTERN unsigned int cifs_min_rcv; /* min size of big ntwrk buf pool */
diff --git a/fs/cifs/cifspdu.h b/fs/cifs/cifspdu.h
index b2233ac05bd2..86239023545b 100644
--- a/fs/cifs/cifspdu.h
+++ b/fs/cifs/cifspdu.h
@@ -16,7 +16,7 @@
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifndef _CIFSPDU_H
@@ -24,8 +24,14 @@
#include <net/sock.h>
+#ifdef CONFIG_CIFS_WEAK_PW_HASH
+#define LANMAN_PROT 0
+#define CIFS_PROT 1
+#else
#define CIFS_PROT 0
-#define BAD_PROT CIFS_PROT+1
+#endif
+#define POSIX_PROT CIFS_PROT+1
+#define BAD_PROT 0xFFFF
/* SMB command codes */
/* Some commands have minimal (wct=0,bcc=0), or uninteresting, responses
@@ -110,7 +116,7 @@
/*
* Size of the session key (crypto key encrypted with the password
*/
-#define CIFS_SESSION_KEY_SIZE (24)
+#define CIFS_SESS_KEY_SIZE (24)
/*
* Maximum user name length
@@ -400,6 +406,29 @@ typedef struct negotiate_req {
unsigned char DialectsArray[1];
} __attribute__((packed)) NEGOTIATE_REQ;
+/* Dialect index is 13 for LANMAN */
+
+typedef struct lanman_neg_rsp {
+ struct smb_hdr hdr; /* wct = 13 */
+ __le16 DialectIndex;
+ __le16 SecurityMode;
+ __le16 MaxBufSize;
+ __le16 MaxMpxCount;
+ __le16 MaxNumberVcs;
+ __le16 RawMode;
+ __le32 SessionKey;
+ __le32 ServerTime;
+ __le16 ServerTimeZone;
+ __le16 EncryptionKeyLength;
+ __le16 Reserved;
+ __u16 ByteCount;
+ unsigned char EncryptionKey[1];
+} __attribute__((packed)) LANMAN_NEG_RSP;
+
+#define READ_RAW_ENABLE 1
+#define WRITE_RAW_ENABLE 2
+#define RAW_ENABLE (READ_RAW_ENABLE | WRITE_RAW_ENABLE)
+
typedef struct negotiate_rsp {
struct smb_hdr hdr; /* wct = 17 */
__le16 DialectIndex;
@@ -509,7 +538,7 @@ typedef union smb_com_session_setup_andx {
/* unsigned char * NativeOS; */
/* unsigned char * NativeLanMan; */
/* unsigned char * PrimaryDomain; */
- } __attribute__((packed)) resp; /* NTLM response format (with or without extended security */
+ } __attribute__((packed)) resp; /* NTLM response with or without extended sec*/
struct { /* request format */
struct smb_hdr hdr; /* wct = 10 */
@@ -520,8 +549,8 @@ typedef union smb_com_session_setup_andx {
__le16 MaxMpxCount;
__le16 VcNumber;
__u32 SessionKey;
- __le16 PassswordLength;
- __u32 Reserved;
+ __le16 PasswordLength;
+ __u32 Reserved; /* encrypt key len and offset */
__le16 ByteCount;
unsigned char AccountPassword[1]; /* followed by */
/* STRING AccountName */
@@ -543,6 +572,26 @@ typedef union smb_com_session_setup_andx {
} __attribute__((packed)) old_resp; /* pre-NTLM (LANMAN2.1) response */
} __attribute__((packed)) SESSION_SETUP_ANDX;
+/* format of NLTMv2 Response ie "case sensitive password" hash when NTLMv2 */
+
+struct ntlmssp2_name {
+ __le16 type;
+ __le16 length;
+/* char name[length]; */
+} __attribute__((packed));
+
+struct ntlmv2_resp {
+ char ntlmv2_hash[CIFS_ENCPWD_SIZE];
+ __le32 blob_signature;
+ __u32 reserved;
+ __le64 time;
+ __u64 client_chal; /* random */
+ __u32 reserved2;
+ struct ntlmssp2_name names[1];
+ /* array of name entries could follow ending in minimum 4 byte struct */
+} __attribute__((packed));
+
+
#define CIFS_NETWORK_OPSYS "CIFS VFS Client for Linux"
/* Capabilities bits (for NTLM SessSetup request) */
@@ -573,7 +622,9 @@ typedef struct smb_com_tconx_req {
} __attribute__((packed)) TCONX_REQ;
typedef struct smb_com_tconx_rsp {
- struct smb_hdr hdr; /* wct = 3 *//* note that Win2000 has sent wct=7 in some cases on responses. Four unspecified words followed OptionalSupport */
+ struct smb_hdr hdr; /* wct = 3 note that Win2000 has sent wct = 7
+ in some cases on responses. Four unspecified
+ words followed OptionalSupport */
__u8 AndXCommand;
__u8 AndXReserved;
__le16 AndXOffset;
@@ -1323,6 +1374,9 @@ struct smb_t2_rsp {
#define SMB_FILE_MAXIMUM_INFO 0x40d
/* Find File infolevels */
+#define SMB_FIND_FILE_INFO_STANDARD 0x001
+#define SMB_FIND_FILE_QUERY_EA_SIZE 0x002
+#define SMB_FIND_FILE_QUERY_EAS_FROM_LIST 0x003
#define SMB_FIND_FILE_DIRECTORY_INFO 0x101
#define SMB_FIND_FILE_FULL_DIRECTORY_INFO 0x102
#define SMB_FIND_FILE_NAMES_INFO 0x103
@@ -1844,13 +1898,13 @@ typedef struct {
typedef struct {
__le32 DeviceType;
__le32 DeviceCharacteristics;
-} __attribute__((packed)) FILE_SYSTEM_DEVICE_INFO; /* device info, level 0x104 */
+} __attribute__((packed)) FILE_SYSTEM_DEVICE_INFO; /* device info level 0x104 */
typedef struct {
__le32 Attributes;
__le32 MaxPathNameComponentLength;
__le32 FileSystemNameLen;
- char FileSystemName[52]; /* do not really need to save this - so potentially get only subset of name */
+ char FileSystemName[52]; /* do not have to save this - get subset? */
} __attribute__((packed)) FILE_SYSTEM_ATTRIBUTE_INFO;
/******************************************************************************/
@@ -1947,7 +2001,8 @@ typedef struct {
struct file_allocation_info {
__le64 AllocationSize; /* Note old Samba srvr rounds this up too much */
-} __attribute__((packed)); /* size used on disk, level 0x103 for set, 0x105 for query */
+} __attribute__((packed)); /* size used on disk, for level 0x103 for set,
+ 0x105 for query */
struct file_end_of_file_info {
__le64 FileSize; /* offset to end of file */
@@ -2054,7 +2109,7 @@ typedef struct {
__le32 ExtFileAttributes;
__le32 FileNameLength;
char FileName[1];
-} __attribute__((packed)) FILE_DIRECTORY_INFO; /* level 0x101 FF response data area */
+} __attribute__((packed)) FILE_DIRECTORY_INFO; /* level 0x101 FF resp data */
typedef struct {
__le32 NextEntryOffset;
@@ -2069,7 +2124,7 @@ typedef struct {
__le32 FileNameLength;
__le32 EaSize; /* length of the xattrs */
char FileName[1];
-} __attribute__((packed)) FILE_FULL_DIRECTORY_INFO; /* level 0x102 FF response data area */
+} __attribute__((packed)) FILE_FULL_DIRECTORY_INFO; /* level 0x102 rsp data */
typedef struct {
__le32 NextEntryOffset;
@@ -2086,7 +2141,7 @@ typedef struct {
__le32 Reserved;
__u64 UniqueId; /* inode num - le since Samba puts ino in low 32 bit*/
char FileName[1];
-} __attribute__((packed)) SEARCH_ID_FULL_DIR_INFO; /* level 0x105 FF response data area */
+} __attribute__((packed)) SEARCH_ID_FULL_DIR_INFO; /* level 0x105 FF rsp data */
typedef struct {
__le32 NextEntryOffset;
@@ -2104,7 +2159,22 @@ typedef struct {
__u8 Reserved;
__u8 ShortName[12];
char FileName[1];
-} __attribute__((packed)) FILE_BOTH_DIRECTORY_INFO; /* level 0x104 FF response data area */
+} __attribute__((packed)) FILE_BOTH_DIRECTORY_INFO; /* level 0x104 FFrsp data */
+
+typedef struct {
+ __u32 ResumeKey;
+ __le16 CreationDate; /* SMB Date */
+ __le16 CreationTime; /* SMB Time */
+ __le16 LastAccessDate;
+ __le16 LastAccessTime;
+ __le16 LastWriteDate;
+ __le16 LastWriteTime;
+ __le32 DataSize; /* File Size (EOF) */
+ __le32 AllocationSize;
+ __le16 Attributes; /* verify not u32 */
+ __u8 FileNameLength;
+ char FileName[1];
+} __attribute__((packed)) FIND_FILE_STANDARD_INFO; /* level 0x1 FF resp data */
struct win_dev {
diff --git a/fs/cifs/cifsproto.h b/fs/cifs/cifsproto.h
index 310ea2f0e0bf..a5ddc62d6fe6 100644
--- a/fs/cifs/cifsproto.h
+++ b/fs/cifs/cifsproto.h
@@ -64,14 +64,12 @@ extern int map_smb_to_linux_error(struct smb_hdr *smb);
extern void header_assemble(struct smb_hdr *, char /* command */ ,
const struct cifsTconInfo *, int /* length of
fixed section (word count) in two byte units */);
-#ifdef CONFIG_CIFS_EXPERIMENTAL
extern int small_smb_init_no_tc(const int smb_cmd, const int wct,
struct cifsSesInfo *ses,
void ** request_buf);
extern int CIFS_SessSetup(unsigned int xid, struct cifsSesInfo *ses,
- const int stage, int * pNTLMv2_flg,
+ const int stage,
const struct nls_table *nls_cp);
-#endif
extern __u16 GetNextMid(struct TCP_Server_Info *server);
extern struct oplock_q_entry * AllocOplockQEntry(struct inode *, u16,
struct cifsTconInfo *);
@@ -285,8 +283,14 @@ extern int cifs_sign_smb2(struct kvec *iov, int n_vec, struct TCP_Server_Info *,
extern int cifs_verify_signature(struct smb_hdr *, const char * mac_key,
__u32 expected_sequence_number);
extern int cifs_calculate_mac_key(char * key,const char * rn,const char * pass);
-extern int CalcNTLMv2_partial_mac_key(struct cifsSesInfo *, struct nls_table *);
-extern void CalcNTLMv2_response(const struct cifsSesInfo *,char * );
+extern int CalcNTLMv2_partial_mac_key(struct cifsSesInfo *,
+ const struct nls_table *);
+extern void CalcNTLMv2_response(const struct cifsSesInfo *, char * );
+extern void setup_ntlmv2_rsp(struct cifsSesInfo *, char *,
+ const struct nls_table *);
+#ifdef CONFIG_CIFS_WEAK_PW_HASH
+extern void calc_lanman_hash(struct cifsSesInfo * ses, char * lnm_session_key);
+#endif /* CIFS_WEAK_PW_HASH */
extern int CIFSSMBCopy(int xid,
struct cifsTconInfo *source_tcon,
const char *fromName,
diff --git a/fs/cifs/cifssmb.c b/fs/cifs/cifssmb.c
index 925881e00ff2..19678c575dfc 100644
--- a/fs/cifs/cifssmb.c
+++ b/fs/cifs/cifssmb.c
@@ -44,8 +44,11 @@ static struct {
int index;
char *name;
} protocols[] = {
+#ifdef CONFIG_CIFS_WEAK_PW_HASH
+ {LANMAN_PROT, "\2LM1.2X002"},
+#endif /* weak password hashing for legacy clients */
{CIFS_PROT, "\2NT LM 0.12"},
- {CIFS_PROT, "\2POSIX 2"},
+ {POSIX_PROT, "\2POSIX 2"},
{BAD_PROT, "\2"}
};
#else
@@ -53,11 +56,29 @@ static struct {
int index;
char *name;
} protocols[] = {
+#ifdef CONFIG_CIFS_WEAK_PW_HASH
+ {LANMAN_PROT, "\2LM1.2X002"},
+#endif /* weak password hashing for legacy clients */
{CIFS_PROT, "\2NT LM 0.12"},
{BAD_PROT, "\2"}
};
#endif
+/* define the number of elements in the cifs dialect array */
+#ifdef CONFIG_CIFS_POSIX
+#ifdef CONFIG_CIFS_WEAK_PW_HASH
+#define CIFS_NUM_PROT 3
+#else
+#define CIFS_NUM_PROT 2
+#endif /* CIFS_WEAK_PW_HASH */
+#else /* not posix */
+#ifdef CONFIG_CIFS_WEAK_PW_HASH
+#define CIFS_NUM_PROT 2
+#else
+#define CIFS_NUM_PROT 1
+#endif /* CONFIG_CIFS_WEAK_PW_HASH */
+#endif /* CIFS_POSIX */
+
/* Mark as invalid, all open files on tree connections since they
were closed when session to server was lost */
@@ -188,7 +209,6 @@ small_smb_init(int smb_command, int wct, struct cifsTconInfo *tcon,
return rc;
}
-#ifdef CONFIG_CIFS_EXPERIMENTAL
int
small_smb_init_no_tc(const int smb_command, const int wct,
struct cifsSesInfo *ses, void **request_buf)
@@ -214,7 +234,6 @@ small_smb_init_no_tc(const int smb_command, const int wct,
return rc;
}
-#endif /* CONFIG_CIFS_EXPERIMENTAL */
/* If the return code is zero, this function must fill in request_buf pointer */
static int
@@ -322,7 +341,8 @@ smb_init(int smb_command, int wct, struct cifsTconInfo *tcon,
/* potential retries of smb operations it turns out we can determine */
/* from the mid flags when the request buffer can be resent without */
/* having to use a second distinct buffer for the response */
- *response_buf = *request_buf;
+ if(response_buf)
+ *response_buf = *request_buf;
header_assemble((struct smb_hdr *) *request_buf, smb_command, tcon,
wct /*wct */ );
@@ -373,8 +393,10 @@ CIFSSMBNegotiate(unsigned int xid, struct cifsSesInfo *ses)
NEGOTIATE_RSP *pSMBr;
int rc = 0;
int bytes_returned;
+ int i;
struct TCP_Server_Info * server;
u16 count;
+ unsigned int secFlags;
if(ses->server)
server = ses->server;
@@ -386,101 +408,200 @@ CIFSSMBNegotiate(unsigned int xid, struct cifsSesInfo *ses)
(void **) &pSMB, (void **) &pSMBr);
if (rc)
return rc;
+
+ /* if any of auth flags (ie not sign or seal) are overriden use them */
+ if(ses->overrideSecFlg & (~(CIFSSEC_MUST_SIGN | CIFSSEC_MUST_SEAL)))
+ secFlags = ses->overrideSecFlg;
+ else /* if override flags set only sign/seal OR them with global auth */
+ secFlags = extended_security | ses->overrideSecFlg;
+
+ cFYI(1,("secFlags 0x%x",secFlags));
+
pSMB->hdr.Mid = GetNextMid(server);
pSMB->hdr.Flags2 |= SMBFLG2_UNICODE;
- if (extended_security)
+ if((secFlags & CIFSSEC_MUST_KRB5) == CIFSSEC_MUST_KRB5)
pSMB->hdr.Flags2 |= SMBFLG2_EXT_SEC;
-
- count = strlen(protocols[0].name) + 1;
- strncpy(pSMB->DialectsArray, protocols[0].name, 30);
- /* null guaranteed to be at end of source and target buffers anyway */
-
+
+ count = 0;
+ for(i=0;i<CIFS_NUM_PROT;i++) {
+ strncpy(pSMB->DialectsArray+count, protocols[i].name, 16);
+ count += strlen(protocols[i].name) + 1;
+ /* null at end of source and target buffers anyway */
+ }
pSMB->hdr.smb_buf_length += count;
pSMB->ByteCount = cpu_to_le16(count);
rc = SendReceive(xid, ses, (struct smb_hdr *) pSMB,
(struct smb_hdr *) pSMBr, &bytes_returned, 0);
- if (rc == 0) {
- server->secMode = pSMBr->SecurityMode;
- if((server->secMode & SECMODE_USER) == 0)
- cFYI(1,("share mode security"));
- server->secType = NTLM; /* BB override default for
- NTLMv2 or kerberos v5 */
- /* one byte - no need to convert this or EncryptionKeyLen
- from little endian */
- server->maxReq = le16_to_cpu(pSMBr->MaxMpxCount);
- /* probably no need to store and check maxvcs */
- server->maxBuf =
- min(le32_to_cpu(pSMBr->MaxBufferSize),
+ if (rc != 0)
+ goto neg_err_exit;
+
+ cFYI(1,("Dialect: %d", pSMBr->DialectIndex));
+ /* Check wct = 1 error case */
+ if((pSMBr->hdr.WordCount < 13) || (pSMBr->DialectIndex == BAD_PROT)) {
+ /* core returns wct = 1, but we do not ask for core - otherwise
+ small wct just comes when dialect index is -1 indicating we
+ could not negotiate a common dialect */
+ rc = -EOPNOTSUPP;
+ goto neg_err_exit;
+#ifdef CONFIG_CIFS_WEAK_PW_HASH
+ } else if((pSMBr->hdr.WordCount == 13)
+ && (pSMBr->DialectIndex == LANMAN_PROT)) {
+ struct lanman_neg_rsp * rsp = (struct lanman_neg_rsp *)pSMBr;
+
+ if((secFlags & CIFSSEC_MAY_LANMAN) ||
+ (secFlags & CIFSSEC_MAY_PLNTXT))
+ server->secType = LANMAN;
+ else {
+ cERROR(1, ("mount failed weak security disabled"
+ " in /proc/fs/cifs/SecurityFlags"));
+ rc = -EOPNOTSUPP;
+ goto neg_err_exit;
+ }
+ server->secMode = (__u8)le16_to_cpu(rsp->SecurityMode);
+ server->maxReq = le16_to_cpu(rsp->MaxMpxCount);
+ server->maxBuf = min((__u32)le16_to_cpu(rsp->MaxBufSize),
+ (__u32)CIFSMaxBufSize + MAX_CIFS_HDR_SIZE);
+ GETU32(server->sessid) = le32_to_cpu(rsp->SessionKey);
+ /* even though we do not use raw we might as well set this
+ accurately, in case we ever find a need for it */
+ if((le16_to_cpu(rsp->RawMode) & RAW_ENABLE) == RAW_ENABLE) {
+ server->maxRw = 0xFF00;
+ server->capabilities = CAP_MPX_MODE | CAP_RAW_MODE;
+ } else {
+ server->maxRw = 0;/* we do not need to use raw anyway */
+ server->capabilities = CAP_MPX_MODE;
+ }
+ server->timeZone = le16_to_cpu(rsp->ServerTimeZone);
+
+ /* BB get server time for time conversions and add
+ code to use it and timezone since this is not UTC */
+
+ if (rsp->EncryptionKeyLength == CIFS_CRYPTO_KEY_SIZE) {
+ memcpy(server->cryptKey, rsp->EncryptionKey,
+ CIFS_CRYPTO_KEY_SIZE);
+ } else if (server->secMode & SECMODE_PW_ENCRYPT) {
+ rc = -EIO; /* need cryptkey unless plain text */
+ goto neg_err_exit;
+ }
+
+ cFYI(1,("LANMAN negotiated"));
+ /* we will not end up setting signing flags - as no signing
+ was in LANMAN and server did not return the flags on */
+ goto signing_check;
+#else /* weak security disabled */
+ } else if(pSMBr->hdr.WordCount == 13) {
+ cERROR(1,("mount failed, cifs module not built "
+ "with CIFS_WEAK_PW_HASH support"));
+ rc = -EOPNOTSUPP;
+#endif /* WEAK_PW_HASH */
+ goto neg_err_exit;
+ } else if(pSMBr->hdr.WordCount != 17) {
+ /* unknown wct */
+ rc = -EOPNOTSUPP;
+ goto neg_err_exit;
+ }
+ /* else wct == 17 NTLM */
+ server->secMode = pSMBr->SecurityMode;
+ if((server->secMode & SECMODE_USER) == 0)
+ cFYI(1,("share mode security"));
+
+ if((server->secMode & SECMODE_PW_ENCRYPT) == 0)
+#ifdef CONFIG_CIFS_WEAK_PW_HASH
+ if ((secFlags & CIFSSEC_MAY_PLNTXT) == 0)
+#endif /* CIFS_WEAK_PW_HASH */
+ cERROR(1,("Server requests plain text password"
+ " but client support disabled"));
+
+ if((secFlags & CIFSSEC_MUST_NTLMV2) == CIFSSEC_MUST_NTLMV2)
+ server->secType = NTLMv2;
+ else if(secFlags & CIFSSEC_MAY_NTLM)
+ server->secType = NTLM;
+ else if(secFlags & CIFSSEC_MAY_NTLMV2)
+ server->secType = NTLMv2;
+ /* else krb5 ... any others ... */
+
+ /* one byte, so no need to convert this or EncryptionKeyLen from
+ little endian */
+ server->maxReq = le16_to_cpu(pSMBr->MaxMpxCount);
+ /* probably no need to store and check maxvcs */
+ server->maxBuf = min(le32_to_cpu(pSMBr->MaxBufferSize),
(__u32) CIFSMaxBufSize + MAX_CIFS_HDR_SIZE);
- server->maxRw = le32_to_cpu(pSMBr->MaxRawSize);
- cFYI(0, ("Max buf = %d", ses->server->maxBuf));
- GETU32(ses->server->sessid) = le32_to_cpu(pSMBr->SessionKey);
- server->capabilities = le32_to_cpu(pSMBr->Capabilities);
- server->timeZone = le16_to_cpu(pSMBr->ServerTimeZone);
- /* BB with UTC do we ever need to be using srvr timezone? */
- if (pSMBr->EncryptionKeyLength == CIFS_CRYPTO_KEY_SIZE) {
- memcpy(server->cryptKey, pSMBr->u.EncryptionKey,
- CIFS_CRYPTO_KEY_SIZE);
- } else if ((pSMBr->hdr.Flags2 & SMBFLG2_EXT_SEC)
- && (pSMBr->EncryptionKeyLength == 0)) {
- /* decode security blob */
- } else
- rc = -EIO;
+ server->maxRw = le32_to_cpu(pSMBr->MaxRawSize);
+ cFYI(0, ("Max buf = %d", ses->server->maxBuf));
+ GETU32(ses->server->sessid) = le32_to_cpu(pSMBr->SessionKey);
+ server->capabilities = le32_to_cpu(pSMBr->Capabilities);
+ server->timeZone = le16_to_cpu(pSMBr->ServerTimeZone);
+ if (pSMBr->EncryptionKeyLength == CIFS_CRYPTO_KEY_SIZE) {
+ memcpy(server->cryptKey, pSMBr->u.EncryptionKey,
+ CIFS_CRYPTO_KEY_SIZE);
+ } else if ((pSMBr->hdr.Flags2 & SMBFLG2_EXT_SEC)
+ && (pSMBr->EncryptionKeyLength == 0)) {
+ /* decode security blob */
+ } else if (server->secMode & SECMODE_PW_ENCRYPT) {
+ rc = -EIO; /* no crypt key only if plain text pwd */
+ goto neg_err_exit;
+ }
- /* BB might be helpful to save off the domain of server here */
+ /* BB might be helpful to save off the domain of server here */
- if ((pSMBr->hdr.Flags2 & SMBFLG2_EXT_SEC) &&
- (server->capabilities & CAP_EXTENDED_SECURITY)) {
- count = pSMBr->ByteCount;
- if (count < 16)
- rc = -EIO;
- else if (count == 16) {
- server->secType = RawNTLMSSP;
- if (server->socketUseCount.counter > 1) {
- if (memcmp
- (server->server_GUID,
- pSMBr->u.extended_response.
- GUID, 16) != 0) {
- cFYI(1, ("server UID changed"));
- memcpy(server->
- server_GUID,
- pSMBr->u.
- extended_response.
- GUID, 16);
- }
- } else
+ if ((pSMBr->hdr.Flags2 & SMBFLG2_EXT_SEC) &&
+ (server->capabilities & CAP_EXTENDED_SECURITY)) {
+ count = pSMBr->ByteCount;
+ if (count < 16)
+ rc = -EIO;
+ else if (count == 16) {
+ server->secType = RawNTLMSSP;
+ if (server->socketUseCount.counter > 1) {
+ if (memcmp(server->server_GUID,
+ pSMBr->u.extended_response.
+ GUID, 16) != 0) {
+ cFYI(1, ("server UID changed"));
memcpy(server->server_GUID,
- pSMBr->u.extended_response.
- GUID, 16);
- } else {
- rc = decode_negTokenInit(pSMBr->u.
- extended_response.
- SecurityBlob,
- count - 16,
- &server->secType);
- if(rc == 1) {
- /* BB Need to fill struct for sessetup here */
- rc = -EOPNOTSUPP;
- } else {
- rc = -EINVAL;
+ pSMBr->u.extended_response.GUID,
+ 16);
}
+ } else
+ memcpy(server->server_GUID,
+ pSMBr->u.extended_response.GUID, 16);
+ } else {
+ rc = decode_negTokenInit(pSMBr->u.extended_response.
+ SecurityBlob,
+ count - 16,
+ &server->secType);
+ if(rc == 1) {
+ /* BB Need to fill struct for sessetup here */
+ rc = -EOPNOTSUPP;
+ } else {
+ rc = -EINVAL;
}
- } else
- server->capabilities &= ~CAP_EXTENDED_SECURITY;
- if(sign_CIFS_PDUs == FALSE) {
- if(server->secMode & SECMODE_SIGN_REQUIRED)
- cERROR(1,
- ("Server requires /proc/fs/cifs/PacketSigningEnabled"));
- server->secMode &= ~(SECMODE_SIGN_ENABLED | SECMODE_SIGN_REQUIRED);
- } else if(sign_CIFS_PDUs == 1) {
- if((server->secMode & SECMODE_SIGN_REQUIRED) == 0)
- server->secMode &= ~(SECMODE_SIGN_ENABLED | SECMODE_SIGN_REQUIRED);
}
-
+ } else
+ server->capabilities &= ~CAP_EXTENDED_SECURITY;
+
+#ifdef CONFIG_CIFS_WEAK_PW_HASH
+signing_check:
+#endif
+ if(sign_CIFS_PDUs == FALSE) {
+ if(server->secMode & SECMODE_SIGN_REQUIRED)
+ cERROR(1,("Server requires "
+ "/proc/fs/cifs/PacketSigningEnabled to be on"));
+ server->secMode &=
+ ~(SECMODE_SIGN_ENABLED | SECMODE_SIGN_REQUIRED);
+ } else if(sign_CIFS_PDUs == 1) {
+ if((server->secMode & SECMODE_SIGN_REQUIRED) == 0)
+ server->secMode &=
+ ~(SECMODE_SIGN_ENABLED | SECMODE_SIGN_REQUIRED);
+ } else if(sign_CIFS_PDUs == 2) {
+ if((server->secMode &
+ (SECMODE_SIGN_ENABLED | SECMODE_SIGN_REQUIRED)) == 0) {
+ cERROR(1,("signing required but server lacks support"));
+ }
}
-
+neg_err_exit:
cifs_buf_release(pSMB);
+
+ cFYI(1,("negprot rc %d",rc));
return rc;
}
@@ -2239,7 +2360,7 @@ CIFSSMBQueryReparseLinkInfo(const int xid, struct cifsTconInfo *tcon,
}
symlinkinfo[buflen] = 0; /* just in case so the caller
does not go off the end of the buffer */
- cFYI(1,("readlink result - %s ",symlinkinfo));
+ cFYI(1,("readlink result - %s",symlinkinfo));
}
}
qreparse_out:
diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c
index bae1479318d1..876eb9ef85fe 100644
--- a/fs/cifs/connect.c
+++ b/fs/cifs/connect.c
@@ -49,8 +49,6 @@
static DECLARE_COMPLETION(cifsd_complete);
-extern void SMBencrypt(unsigned char *passwd, unsigned char *c8,
- unsigned char *p24);
extern void SMBNTencrypt(unsigned char *passwd, unsigned char *c8,
unsigned char *p24);
@@ -70,6 +68,7 @@ struct smb_vol {
gid_t linux_gid;
mode_t file_mode;
mode_t dir_mode;
+ unsigned secFlg;
unsigned rw:1;
unsigned retry:1;
unsigned intr:1;
@@ -83,12 +82,7 @@ struct smb_vol {
unsigned remap:1; /* set to remap seven reserved chars in filenames */
unsigned posix_paths:1; /* unset to not ask for posix pathnames. */
unsigned sfu_emul:1;
- unsigned krb5:1;
- unsigned ntlm:1;
- unsigned ntlmv2:1;
unsigned nullauth:1; /* attempt to authenticate with null user */
- unsigned sign:1;
- unsigned seal:1; /* encrypt */
unsigned nocase; /* request case insensitive filenames */
unsigned nobrl; /* disable sending byte range locks to srv */
unsigned int rsize;
@@ -369,21 +363,21 @@ cifs_demultiplex_thread(struct TCP_Server_Info *server)
continue;
if (bigbuf == NULL) {
bigbuf = cifs_buf_get();
- if(bigbuf == NULL) {
- cERROR(1,("No memory for large SMB response"));
+ if (!bigbuf) {
+ cERROR(1, ("No memory for large SMB response"));
msleep(3000);
/* retry will check if exiting */
continue;
}
- } else if(isLargeBuf) {
- /* we are reusing a dirtry large buf, clear its start */
+ } else if (isLargeBuf) {
+ /* we are reusing a dirty large buf, clear its start */
memset(bigbuf, 0, sizeof (struct smb_hdr));
}
if (smallbuf == NULL) {
smallbuf = cifs_small_buf_get();
- if(smallbuf == NULL) {
- cERROR(1,("No memory for SMB response"));
+ if (!smallbuf) {
+ cERROR(1, ("No memory for SMB response"));
msleep(1000);
/* retry will check if exiting */
continue;
@@ -403,12 +397,12 @@ cifs_demultiplex_thread(struct TCP_Server_Info *server)
kernel_recvmsg(csocket, &smb_msg,
&iov, 1, 4, 0 /* BB see socket.h flags */);
- if(server->tcpStatus == CifsExiting) {
+ if (server->tcpStatus == CifsExiting) {
break;
} else if (server->tcpStatus == CifsNeedReconnect) {
- cFYI(1,("Reconnect after server stopped responding"));
+ cFYI(1, ("Reconnect after server stopped responding"));
cifs_reconnect(server);
- cFYI(1,("call to reconnect done"));
+ cFYI(1, ("call to reconnect done"));
csocket = server->ssocket;
continue;
} else if ((length == -ERESTARTSYS) || (length == -EAGAIN)) {
@@ -417,15 +411,15 @@ cifs_demultiplex_thread(struct TCP_Server_Info *server)
tcpStatus CifsNeedReconnect if server hung */
continue;
} else if (length <= 0) {
- if(server->tcpStatus == CifsNew) {
- cFYI(1,("tcp session abend after SMBnegprot"));
+ if (server->tcpStatus == CifsNew) {
+ cFYI(1, ("tcp session abend after SMBnegprot"));
/* some servers kill the TCP session rather than
returning an SMB negprot error, in which
case reconnecting here is not going to help,
and so simply return error to mount */
break;
}
- if(length == -EINTR) {
+ if (!try_to_freeze() && (length == -EINTR)) {
cFYI(1,("cifsd thread killed"));
break;
}
@@ -585,9 +579,11 @@ cifs_demultiplex_thread(struct TCP_Server_Info *server)
/* merge response - fix up 1st*/
if(coalesce_t2(smb_buffer,
mid_entry->resp_buf)) {
+ mid_entry->multiRsp = 1;
break;
} else {
/* all parts received */
+ mid_entry->multiEnd = 1;
goto multi_t2_fnd;
}
} else {
@@ -632,9 +628,14 @@ multi_t2_fnd:
wake_up_process(task_to_wake);
} else if ((is_valid_oplock_break(smb_buffer, server) == FALSE)
&& (isMultiRsp == FALSE)) {
- cERROR(1, ("No task to wake, unknown frame rcvd!"));
+ cERROR(1, ("No task to wake, unknown frame rcvd! NumMids %d", midCount.counter));
cifs_dump_mem("Received Data is: ",(char *)smb_buffer,
sizeof(struct smb_hdr));
+#ifdef CONFIG_CIFS_DEBUG2
+ cifs_dump_detail(smb_buffer);
+ cifs_dump_mids(server);
+#endif /* CIFS_DEBUG2 */
+
}
} /* end while !EXITING */
@@ -784,7 +785,6 @@ cifs_parse_mount_options(char *options, const char *devname,struct smb_vol *vol)
/* vol->retry default is 0 (i.e. "soft" limited retry not hard retry) */
vol->rw = TRUE;
- vol->ntlm = TRUE;
/* default is always to request posix paths. */
vol->posix_paths = 1;
@@ -915,30 +915,35 @@ cifs_parse_mount_options(char *options, const char *devname,struct smb_vol *vol)
cERROR(1,("no security value specified"));
continue;
} else if (strnicmp(value, "krb5i", 5) == 0) {
- vol->sign = 1;
- vol->krb5 = 1;
+ vol->secFlg |= CIFSSEC_MAY_KRB5 |
+ CIFSSEC_MUST_SIGN;
} else if (strnicmp(value, "krb5p", 5) == 0) {
- /* vol->seal = 1;
- vol->krb5 = 1; */
+ /* vol->secFlg |= CIFSSEC_MUST_SEAL |
+ CIFSSEC_MAY_KRB5; */
cERROR(1,("Krb5 cifs privacy not supported"));
return 1;
} else if (strnicmp(value, "krb5", 4) == 0) {
- vol->krb5 = 1;
+ vol->secFlg |= CIFSSEC_MAY_KRB5;
} else if (strnicmp(value, "ntlmv2i", 7) == 0) {
- vol->ntlmv2 = 1;
- vol->sign = 1;
+ vol->secFlg |= CIFSSEC_MAY_NTLMV2 |
+ CIFSSEC_MUST_SIGN;
} else if (strnicmp(value, "ntlmv2", 6) == 0) {
- vol->ntlmv2 = 1;
+ vol->secFlg |= CIFSSEC_MAY_NTLMV2;
} else if (strnicmp(value, "ntlmi", 5) == 0) {
- vol->ntlm = 1;
- vol->sign = 1;
+ vol->secFlg |= CIFSSEC_MAY_NTLM |
+ CIFSSEC_MUST_SIGN;
} else if (strnicmp(value, "ntlm", 4) == 0) {
/* ntlm is default so can be turned off too */
- vol->ntlm = 1;
+ vol->secFlg |= CIFSSEC_MAY_NTLM;
} else if (strnicmp(value, "nontlm", 6) == 0) {
- vol->ntlm = 0;
+ /* BB is there a better way to do this? */
+ vol->secFlg |= CIFSSEC_MAY_NTLMV2;
+#ifdef CONFIG_CIFS_WEAK_PW_HASH
+ } else if (strnicmp(value, "lanman", 6) == 0) {
+ vol->secFlg |= CIFSSEC_MAY_LANMAN;
+#endif
} else if (strnicmp(value, "none", 4) == 0) {
- vol->nullauth = 1;
+ vol->nullauth = 1;
} else {
cERROR(1,("bad security option: %s", value));
return 1;
@@ -976,7 +981,7 @@ cifs_parse_mount_options(char *options, const char *devname,struct smb_vol *vol)
}
/* BB are there cases in which a comma can be valid in
a domain name and need special handling? */
- if (strnlen(value, 65) < 65) {
+ if (strnlen(value, 256) < 256) {
vol->domainname = value;
cFYI(1, ("Domain name set"));
} else {
@@ -1168,6 +1173,10 @@ cifs_parse_mount_options(char *options, const char *devname,struct smb_vol *vol)
vol->no_psx_acl = 0;
} else if (strnicmp(data, "noacl",5) == 0) {
vol->no_psx_acl = 1;
+ } else if (strnicmp(data, "sign",4) == 0) {
+ vol->secFlg |= CIFSSEC_MUST_SIGN;
+/* } else if (strnicmp(data, "seal",4) == 0) {
+ vol->secFlg |= CIFSSEC_MUST_SEAL; */
} else if (strnicmp(data, "direct",6) == 0) {
vol->direct_io = 1;
} else if (strnicmp(data, "forcedirectio",13) == 0) {
@@ -1762,11 +1771,18 @@ cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb,
if (volume_info.username)
strncpy(pSesInfo->userName,
volume_info.username,MAX_USERNAME_SIZE);
- if (volume_info.domainname)
- strncpy(pSesInfo->domainName,
- volume_info.domainname,MAX_USERNAME_SIZE);
+ if (volume_info.domainname) {
+ int len = strlen(volume_info.domainname);
+ pSesInfo->domainName =
+ kmalloc(len + 1, GFP_KERNEL);
+ if(pSesInfo->domainName)
+ strcpy(pSesInfo->domainName,
+ volume_info.domainname);
+ }
pSesInfo->linux_uid = volume_info.linux_uid;
+ pSesInfo->overrideSecFlg = volume_info.secFlg;
down(&pSesInfo->sesSem);
+ /* BB FIXME need to pass vol->secFlgs BB */
rc = cifs_setup_session(xid,pSesInfo, cifs_sb->local_nls);
up(&pSesInfo->sesSem);
if(!rc)
@@ -1980,7 +1996,7 @@ cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb,
static int
CIFSSessSetup(unsigned int xid, struct cifsSesInfo *ses,
- char session_key[CIFS_SESSION_KEY_SIZE],
+ char session_key[CIFS_SESS_KEY_SIZE],
const struct nls_table *nls_codepage)
{
struct smb_hdr *smb_buffer;
@@ -2038,15 +2054,15 @@ CIFSSessSetup(unsigned int xid, struct cifsSesInfo *ses,
pSMB->req_no_secext.Capabilities = cpu_to_le32(capabilities);
pSMB->req_no_secext.CaseInsensitivePasswordLength =
- cpu_to_le16(CIFS_SESSION_KEY_SIZE);
+ cpu_to_le16(CIFS_SESS_KEY_SIZE);
pSMB->req_no_secext.CaseSensitivePasswordLength =
- cpu_to_le16(CIFS_SESSION_KEY_SIZE);
+ cpu_to_le16(CIFS_SESS_KEY_SIZE);
bcc_ptr = pByteArea(smb_buffer);
- memcpy(bcc_ptr, (char *) session_key, CIFS_SESSION_KEY_SIZE);
- bcc_ptr += CIFS_SESSION_KEY_SIZE;
- memcpy(bcc_ptr, (char *) session_key, CIFS_SESSION_KEY_SIZE);
- bcc_ptr += CIFS_SESSION_KEY_SIZE;
+ memcpy(bcc_ptr, (char *) session_key, CIFS_SESS_KEY_SIZE);
+ bcc_ptr += CIFS_SESS_KEY_SIZE;
+ memcpy(bcc_ptr, (char *) session_key, CIFS_SESS_KEY_SIZE);
+ bcc_ptr += CIFS_SESS_KEY_SIZE;
if (ses->capabilities & CAP_UNICODE) {
if ((long) bcc_ptr % 2) { /* must be word aligned for Unicode */
@@ -2054,7 +2070,7 @@ CIFSSessSetup(unsigned int xid, struct cifsSesInfo *ses,
bcc_ptr++;
}
if(user == NULL)
- bytes_returned = 0; /* skill null user */
+ bytes_returned = 0; /* skip null user */
else
bytes_returned =
cifs_strtoUCS((__le16 *) bcc_ptr, user, 100,
@@ -2162,8 +2178,7 @@ CIFSSessSetup(unsigned int xid, struct cifsSesInfo *ses,
if (remaining_words > 0) {
len = UniStrnlen((wchar_t *)bcc_ptr,
remaining_words-1);
- if(ses->serverNOS)
- kfree(ses->serverNOS);
+ kfree(ses->serverNOS);
ses->serverNOS = kzalloc(2 * (len + 1),GFP_KERNEL);
if(ses->serverNOS == NULL)
goto sesssetup_nomem;
@@ -2203,12 +2218,10 @@ CIFSSessSetup(unsigned int xid, struct cifsSesInfo *ses,
/* if these kcallocs fail not much we
can do, but better to not fail the
sesssetup itself */
- if(ses->serverDomain)
- kfree(ses->serverDomain);
+ kfree(ses->serverDomain);
ses->serverDomain =
kzalloc(2, GFP_KERNEL);
- if(ses->serverNOS)
- kfree(ses->serverNOS);
+ kfree(ses->serverNOS);
ses->serverNOS =
kzalloc(2, GFP_KERNEL);
}
@@ -2217,8 +2230,7 @@ CIFSSessSetup(unsigned int xid, struct cifsSesInfo *ses,
if (((long) bcc_ptr + len) - (long)
pByteArea(smb_buffer_response)
<= BCC(smb_buffer_response)) {
- if(ses->serverOS)
- kfree(ses->serverOS);
+ kfree(ses->serverOS);
ses->serverOS = kzalloc(len + 1,GFP_KERNEL);
if(ses->serverOS == NULL)
goto sesssetup_nomem;
@@ -2229,8 +2241,7 @@ CIFSSessSetup(unsigned int xid, struct cifsSesInfo *ses,
bcc_ptr++;
len = strnlen(bcc_ptr, 1024);
- if(ses->serverNOS)
- kfree(ses->serverNOS);
+ kfree(ses->serverNOS);
ses->serverNOS = kzalloc(len + 1,GFP_KERNEL);
if(ses->serverNOS == NULL)
goto sesssetup_nomem;
@@ -2274,292 +2285,6 @@ sesssetup_nomem: /* do not return an error on nomem for the info strings,
}
static int
-CIFSSpnegoSessSetup(unsigned int xid, struct cifsSesInfo *ses,
- char *SecurityBlob,int SecurityBlobLength,
- const struct nls_table *nls_codepage)
-{
- struct smb_hdr *smb_buffer;
- struct smb_hdr *smb_buffer_response;
- SESSION_SETUP_ANDX *pSMB;
- SESSION_SETUP_ANDX *pSMBr;
- char *bcc_ptr;
- char *user;
- char *domain;
- int rc = 0;
- int remaining_words = 0;
- int bytes_returned = 0;
- int len;
- __u32 capabilities;
- __u16 count;
-
- cFYI(1, ("In spnego sesssetup "));
- if(ses == NULL)
- return -EINVAL;
- user = ses->userName;
- domain = ses->domainName;
-
- smb_buffer = cifs_buf_get();
- if (smb_buffer == NULL) {
- return -ENOMEM;
- }
- smb_buffer_response = smb_buffer;
- pSMBr = pSMB = (SESSION_SETUP_ANDX *) smb_buffer;
-
- /* send SMBsessionSetup here */
- header_assemble(smb_buffer, SMB_COM_SESSION_SETUP_ANDX,
- NULL /* no tCon exists yet */ , 12 /* wct */ );
-
- smb_buffer->Mid = GetNextMid(ses->server);
- pSMB->req.hdr.Flags2 |= SMBFLG2_EXT_SEC;
- pSMB->req.AndXCommand = 0xFF;
- if(ses->server->maxBuf > 64*1024)
- ses->server->maxBuf = (64*1023);
- pSMB->req.MaxBufferSize = cpu_to_le16(ses->server->maxBuf);
- pSMB->req.MaxMpxCount = cpu_to_le16(ses->server->maxReq);
-
- if(ses->server->secMode & (SECMODE_SIGN_REQUIRED | SECMODE_SIGN_ENABLED))
- smb_buffer->Flags2 |= SMBFLG2_SECURITY_SIGNATURE;
-
- capabilities = CAP_LARGE_FILES | CAP_NT_SMBS | CAP_LEVEL_II_OPLOCKS |
- CAP_EXTENDED_SECURITY;
- if (ses->capabilities & CAP_UNICODE) {
- smb_buffer->Flags2 |= SMBFLG2_UNICODE;
- capabilities |= CAP_UNICODE;
- }
- if (ses->capabilities & CAP_STATUS32) {
- smb_buffer->Flags2 |= SMBFLG2_ERR_STATUS;
- capabilities |= CAP_STATUS32;
- }
- if (ses->capabilities & CAP_DFS) {
- smb_buffer->Flags2 |= SMBFLG2_DFS;
- capabilities |= CAP_DFS;
- }
- pSMB->req.Capabilities = cpu_to_le32(capabilities);
-
- pSMB->req.SecurityBlobLength = cpu_to_le16(SecurityBlobLength);
- bcc_ptr = pByteArea(smb_buffer);
- memcpy(bcc_ptr, SecurityBlob, SecurityBlobLength);
- bcc_ptr += SecurityBlobLength;
-
- if (ses->capabilities & CAP_UNICODE) {
- if ((long) bcc_ptr % 2) { /* must be word aligned for Unicode strings */
- *bcc_ptr = 0;
- bcc_ptr++;
- }
- bytes_returned =
- cifs_strtoUCS((__le16 *) bcc_ptr, user, 100, nls_codepage);
- bcc_ptr += 2 * bytes_returned; /* convert num of 16 bit words to bytes */
- bcc_ptr += 2; /* trailing null */
- if (domain == NULL)
- bytes_returned =
- cifs_strtoUCS((__le16 *) bcc_ptr,
- "CIFS_LINUX_DOM", 32, nls_codepage);
- else
- bytes_returned =
- cifs_strtoUCS((__le16 *) bcc_ptr, domain, 64,
- nls_codepage);
- bcc_ptr += 2 * bytes_returned;
- bcc_ptr += 2;
- bytes_returned =
- cifs_strtoUCS((__le16 *) bcc_ptr, "Linux version ",
- 32, nls_codepage);
- bcc_ptr += 2 * bytes_returned;
- bytes_returned =
- cifs_strtoUCS((__le16 *) bcc_ptr, system_utsname.release, 32,
- nls_codepage);
- bcc_ptr += 2 * bytes_returned;
- bcc_ptr += 2;
- bytes_returned =
- cifs_strtoUCS((__le16 *) bcc_ptr, CIFS_NETWORK_OPSYS,
- 64, nls_codepage);
- bcc_ptr += 2 * bytes_returned;
- bcc_ptr += 2;
- } else {
- strncpy(bcc_ptr, user, 200);
- bcc_ptr += strnlen(user, 200);
- *bcc_ptr = 0;
- bcc_ptr++;
- if (domain == NULL) {
- strcpy(bcc_ptr, "CIFS_LINUX_DOM");
- bcc_ptr += strlen("CIFS_LINUX_DOM") + 1;
- } else {
- strncpy(bcc_ptr, domain, 64);
- bcc_ptr += strnlen(domain, 64);
- *bcc_ptr = 0;
- bcc_ptr++;
- }
- strcpy(bcc_ptr, "Linux version ");
- bcc_ptr += strlen("Linux version ");
- strcpy(bcc_ptr, system_utsname.release);
- bcc_ptr += strlen(system_utsname.release) + 1;
- strcpy(bcc_ptr, CIFS_NETWORK_OPSYS);
- bcc_ptr += strlen(CIFS_NETWORK_OPSYS) + 1;
- }
- count = (long) bcc_ptr - (long) pByteArea(smb_buffer);
- smb_buffer->smb_buf_length += count;
- pSMB->req.ByteCount = cpu_to_le16(count);
-
- rc = SendReceive(xid, ses, smb_buffer, smb_buffer_response,
- &bytes_returned, 1);
- if (rc) {
-/* rc = map_smb_to_linux_error(smb_buffer_response); *//* done in SendReceive now */
- } else if ((smb_buffer_response->WordCount == 3)
- || (smb_buffer_response->WordCount == 4)) {
- __u16 action = le16_to_cpu(pSMBr->resp.Action);
- __u16 blob_len =
- le16_to_cpu(pSMBr->resp.SecurityBlobLength);
- if (action & GUEST_LOGIN)
- cFYI(1, (" Guest login")); /* BB do we want to set anything in SesInfo struct ? */
- if (ses) {
- ses->Suid = smb_buffer_response->Uid; /* UID left in wire format (le) */
- cFYI(1, ("UID = %d ", ses->Suid));
- bcc_ptr = pByteArea(smb_buffer_response); /* response can have either 3 or 4 word count - Samba sends 3 */
-
- /* BB Fix below to make endian neutral !! */
-
- if ((pSMBr->resp.hdr.WordCount == 3)
- || ((pSMBr->resp.hdr.WordCount == 4)
- && (blob_len <
- pSMBr->resp.ByteCount))) {
- if (pSMBr->resp.hdr.WordCount == 4) {
- bcc_ptr +=
- blob_len;
- cFYI(1,
- ("Security Blob Length %d ",
- blob_len));
- }
-
- if (smb_buffer->Flags2 & SMBFLG2_UNICODE) {
- if ((long) (bcc_ptr) % 2) {
- remaining_words =
- (BCC(smb_buffer_response)
- - 1) / 2;
- bcc_ptr++; /* Unicode strings must be word aligned */
- } else {
- remaining_words =
- BCC
- (smb_buffer_response) / 2;
- }
- len =
- UniStrnlen((wchar_t *) bcc_ptr,
- remaining_words - 1);
-/* We look for obvious messed up bcc or strings in response so we do not go off
- the end since (at least) WIN2K and Windows XP have a major bug in not null
- terminating last Unicode string in response */
- if(ses->serverOS)
- kfree(ses->serverOS);
- ses->serverOS =
- kzalloc(2 * (len + 1), GFP_KERNEL);
- cifs_strfromUCS_le(ses->serverOS,
- (__le16 *)
- bcc_ptr, len,
- nls_codepage);
- bcc_ptr += 2 * (len + 1);
- remaining_words -= len + 1;
- ses->serverOS[2 * len] = 0;
- ses->serverOS[1 + (2 * len)] = 0;
- if (remaining_words > 0) {
- len = UniStrnlen((wchar_t *)bcc_ptr,
- remaining_words
- - 1);
- if(ses->serverNOS)
- kfree(ses->serverNOS);
- ses->serverNOS =
- kzalloc(2 * (len + 1),
- GFP_KERNEL);
- cifs_strfromUCS_le(ses->serverNOS,
- (__le16 *)bcc_ptr,
- len,
- nls_codepage);
- bcc_ptr += 2 * (len + 1);
- ses->serverNOS[2 * len] = 0;
- ses->serverNOS[1 + (2 * len)] = 0;
- remaining_words -= len + 1;
- if (remaining_words > 0) {
- len = UniStrnlen((wchar_t *) bcc_ptr, remaining_words);
- /* last string not null terminated (e.g.Windows XP/2000) */
- if(ses->serverDomain)
- kfree(ses->serverDomain);
- ses->serverDomain = kzalloc(2*(len+1),GFP_KERNEL);
- cifs_strfromUCS_le(ses->serverDomain,
- (__le16 *)bcc_ptr,
- len, nls_codepage);
- bcc_ptr += 2*(len+1);
- ses->serverDomain[2*len] = 0;
- ses->serverDomain[1+(2*len)] = 0;
- } /* else no more room so create dummy domain string */
- else {
- if(ses->serverDomain)
- kfree(ses->serverDomain);
- ses->serverDomain =
- kzalloc(2,GFP_KERNEL);
- }
- } else {/* no room use dummy domain&NOS */
- if(ses->serverDomain)
- kfree(ses->serverDomain);
- ses->serverDomain = kzalloc(2, GFP_KERNEL);
- if(ses->serverNOS)
- kfree(ses->serverNOS);
- ses->serverNOS = kzalloc(2, GFP_KERNEL);
- }
- } else { /* ASCII */
-
- len = strnlen(bcc_ptr, 1024);
- if (((long) bcc_ptr + len) - (long)
- pByteArea(smb_buffer_response)
- <= BCC(smb_buffer_response)) {
- if(ses->serverOS)
- kfree(ses->serverOS);
- ses->serverOS = kzalloc(len + 1, GFP_KERNEL);
- strncpy(ses->serverOS, bcc_ptr, len);
-
- bcc_ptr += len;
- bcc_ptr[0] = 0; /* null terminate the string */
- bcc_ptr++;
-
- len = strnlen(bcc_ptr, 1024);
- if(ses->serverNOS)
- kfree(ses->serverNOS);
- ses->serverNOS = kzalloc(len + 1,GFP_KERNEL);
- strncpy(ses->serverNOS, bcc_ptr, len);
- bcc_ptr += len;
- bcc_ptr[0] = 0;
- bcc_ptr++;
-
- len = strnlen(bcc_ptr, 1024);
- if(ses->serverDomain)
- kfree(ses->serverDomain);
- ses->serverDomain = kzalloc(len + 1, GFP_KERNEL);
- strncpy(ses->serverDomain, bcc_ptr, len);
- bcc_ptr += len;
- bcc_ptr[0] = 0;
- bcc_ptr++;
- } else
- cFYI(1,
- ("Variable field of length %d extends beyond end of smb ",
- len));
- }
- } else {
- cERROR(1,
- (" Security Blob Length extends beyond end of SMB"));
- }
- } else {
- cERROR(1, ("No session structure passed in."));
- }
- } else {
- cERROR(1,
- (" Invalid Word count %d: ",
- smb_buffer_response->WordCount));
- rc = -EIO;
- }
-
- if (smb_buffer)
- cifs_buf_release(smb_buffer);
-
- return rc;
-}
-
-static int
CIFSNTLMSSPNegotiateSessSetup(unsigned int xid,
struct cifsSesInfo *ses, int * pNTLMv2_flag,
const struct nls_table *nls_codepage)
@@ -2635,8 +2360,8 @@ CIFSNTLMSSPNegotiateSessSetup(unsigned int xid,
/* NTLMSSP_NEGOTIATE_ALWAYS_SIGN | */ NTLMSSP_NEGOTIATE_128;
if(sign_CIFS_PDUs)
negotiate_flags |= NTLMSSP_NEGOTIATE_SIGN;
- if(ntlmv2_support)
- negotiate_flags |= NTLMSSP_NEGOTIATE_NTLMV2;
+/* if(ntlmv2_support)
+ negotiate_flags |= NTLMSSP_NEGOTIATE_NTLMV2;*/
/* setup pointers to domain name and workstation name */
bcc_ptr += SecurityBlobLength;
@@ -2783,8 +2508,7 @@ CIFSNTLMSSPNegotiateSessSetup(unsigned int xid,
bcc_ptr,
remaining_words
- 1);
- if(ses->serverNOS)
- kfree(ses->serverNOS);
+ kfree(ses->serverNOS);
ses->serverNOS =
kzalloc(2 * (len + 1),
GFP_KERNEL);
@@ -2802,8 +2526,7 @@ CIFSNTLMSSPNegotiateSessSetup(unsigned int xid,
if (remaining_words > 0) {
len = UniStrnlen((wchar_t *) bcc_ptr, remaining_words);
/* last string is not always null terminated (for e.g. for Windows XP & 2000) */
- if(ses->serverDomain)
- kfree(ses->serverDomain);
+ kfree(ses->serverDomain);
ses->serverDomain =
kzalloc(2 *
(len +
@@ -2822,19 +2545,16 @@ CIFSNTLMSSPNegotiateSessSetup(unsigned int xid,
= 0;
} /* else no more room so create dummy domain string */
else {
- if(ses->serverDomain)
- kfree(ses->serverDomain);
+ kfree(ses->serverDomain);
ses->serverDomain =
kzalloc(2,
GFP_KERNEL);
}
} else { /* no room so create dummy domain and NOS string */
- if(ses->serverDomain);
- kfree(ses->serverDomain);
+ kfree(ses->serverDomain);
ses->serverDomain =
kzalloc(2, GFP_KERNEL);
- if(ses->serverNOS)
- kfree(ses->serverNOS);
+ kfree(ses->serverNOS);
ses->serverNOS =
kzalloc(2, GFP_KERNEL);
}
@@ -2856,8 +2576,7 @@ CIFSNTLMSSPNegotiateSessSetup(unsigned int xid,
bcc_ptr++;
len = strnlen(bcc_ptr, 1024);
- if(ses->serverNOS)
- kfree(ses->serverNOS);
+ kfree(ses->serverNOS);
ses->serverNOS =
kzalloc(len + 1,
GFP_KERNEL);
@@ -2867,8 +2586,7 @@ CIFSNTLMSSPNegotiateSessSetup(unsigned int xid,
bcc_ptr++;
len = strnlen(bcc_ptr, 1024);
- if(ses->serverDomain)
- kfree(ses->serverDomain);
+ kfree(ses->serverDomain);
ses->serverDomain =
kzalloc(len + 1,
GFP_KERNEL);
@@ -2994,14 +2712,14 @@ CIFSNTLMSSPAuthSessSetup(unsigned int xid, struct cifsSesInfo *ses,
SecurityBlob->LmChallengeResponse.Buffer = 0;
SecurityBlob->NtChallengeResponse.Length =
- cpu_to_le16(CIFS_SESSION_KEY_SIZE);
+ cpu_to_le16(CIFS_SESS_KEY_SIZE);
SecurityBlob->NtChallengeResponse.MaximumLength =
- cpu_to_le16(CIFS_SESSION_KEY_SIZE);
- memcpy(bcc_ptr, ntlm_session_key, CIFS_SESSION_KEY_SIZE);
+ cpu_to_le16(CIFS_SESS_KEY_SIZE);
+ memcpy(bcc_ptr, ntlm_session_key, CIFS_SESS_KEY_SIZE);
SecurityBlob->NtChallengeResponse.Buffer =
cpu_to_le32(SecurityBlobLength);
- SecurityBlobLength += CIFS_SESSION_KEY_SIZE;
- bcc_ptr += CIFS_SESSION_KEY_SIZE;
+ SecurityBlobLength += CIFS_SESS_KEY_SIZE;
+ bcc_ptr += CIFS_SESS_KEY_SIZE;
if (ses->capabilities & CAP_UNICODE) {
if (domain == NULL) {
@@ -3190,8 +2908,7 @@ CIFSNTLMSSPAuthSessSetup(unsigned int xid, struct cifsSesInfo *ses,
bcc_ptr,
remaining_words
- 1);
- if(ses->serverNOS)
- kfree(ses->serverNOS);
+ kfree(ses->serverNOS);
ses->serverNOS =
kzalloc(2 * (len + 1),
GFP_KERNEL);
@@ -3244,8 +2961,7 @@ CIFSNTLMSSPAuthSessSetup(unsigned int xid, struct cifsSesInfo *ses,
if(ses->serverDomain)
kfree(ses->serverDomain);
ses->serverDomain = kzalloc(2, GFP_KERNEL);
- if(ses->serverNOS)
- kfree(ses->serverNOS);
+ kfree(ses->serverNOS);
ses->serverNOS = kzalloc(2, GFP_KERNEL);
}
} else { /* ASCII */
@@ -3263,8 +2979,7 @@ CIFSNTLMSSPAuthSessSetup(unsigned int xid, struct cifsSesInfo *ses,
bcc_ptr++;
len = strnlen(bcc_ptr, 1024);
- if(ses->serverNOS)
- kfree(ses->serverNOS);
+ kfree(ses->serverNOS);
ses->serverNOS = kzalloc(len+1,GFP_KERNEL);
strncpy(ses->serverNOS, bcc_ptr, len);
bcc_ptr += len;
@@ -3340,22 +3055,33 @@ CIFSTCon(unsigned int xid, struct cifsSesInfo *ses,
bcc_ptr = &pSMB->Password[0];
if((ses->server->secMode) & SECMODE_USER) {
pSMB->PasswordLength = cpu_to_le16(1); /* minimum */
+ *bcc_ptr = 0; /* password is null byte */
bcc_ptr++; /* skip password */
+ /* already aligned so no need to do it below */
} else {
- pSMB->PasswordLength = cpu_to_le16(CIFS_SESSION_KEY_SIZE);
+ pSMB->PasswordLength = cpu_to_le16(CIFS_SESS_KEY_SIZE);
/* BB FIXME add code to fail this if NTLMv2 or Kerberos
specified as required (when that support is added to
the vfs in the future) as only NTLM or the much
- weaker LANMAN (which we do not send) is accepted
+ weaker LANMAN (which we do not send by default) is accepted
by Samba (not sure whether other servers allow
NTLMv2 password here) */
+#ifdef CONFIG_CIFS_WEAK_PW_HASH
+ if((extended_security & CIFSSEC_MAY_LANMAN) &&
+ (ses->server->secType == LANMAN))
+ calc_lanman_hash(ses, bcc_ptr);
+ else
+#endif /* CIFS_WEAK_PW_HASH */
SMBNTencrypt(ses->password,
ses->server->cryptKey,
bcc_ptr);
- bcc_ptr += CIFS_SESSION_KEY_SIZE;
- *bcc_ptr = 0;
- bcc_ptr++; /* align */
+ bcc_ptr += CIFS_SESS_KEY_SIZE;
+ if(ses->capabilities & CAP_UNICODE) {
+ /* must align unicode strings */
+ *bcc_ptr = 0; /* null byte password */
+ bcc_ptr++;
+ }
}
if(ses->server->secMode &
@@ -3429,7 +3155,10 @@ CIFSTCon(unsigned int xid, struct cifsSesInfo *ses,
}
/* else do not bother copying these informational fields */
}
- tcon->Flags = le16_to_cpu(pSMBr->OptionalSupport);
+ if(smb_buffer_response->WordCount == 3)
+ tcon->Flags = le16_to_cpu(pSMBr->OptionalSupport);
+ else
+ tcon->Flags = 0;
cFYI(1, ("Tcon flags: 0x%x ", tcon->Flags));
} else if ((rc == 0) && tcon == NULL) {
/* all we need to save for IPC$ connection */
@@ -3494,7 +3223,7 @@ int cifs_setup_session(unsigned int xid, struct cifsSesInfo *pSesInfo,
struct nls_table * nls_info)
{
int rc = 0;
- char ntlm_session_key[CIFS_SESSION_KEY_SIZE];
+ char ntlm_session_key[CIFS_SESS_KEY_SIZE];
int ntlmv2_flag = FALSE;
int first_time = 0;
@@ -3526,20 +3255,13 @@ int cifs_setup_session(unsigned int xid, struct cifsSesInfo *pSesInfo,
pSesInfo->server->secMode,
pSesInfo->server->capabilities,
pSesInfo->server->timeZone));
-#ifdef CONFIG_CIFS_EXPERIMENTAL
- if(experimEnabled > 1)
- rc = CIFS_SessSetup(xid, pSesInfo, CIFS_NTLM /* type */,
- &ntlmv2_flag, nls_info);
- else
-#endif
- if (extended_security
+ if(experimEnabled < 2)
+ rc = CIFS_SessSetup(xid, pSesInfo,
+ first_time, nls_info);
+ else if (extended_security
&& (pSesInfo->capabilities & CAP_EXTENDED_SECURITY)
&& (pSesInfo->server->secType == NTLMSSP)) {
- cFYI(1, ("New style sesssetup"));
- rc = CIFSSpnegoSessSetup(xid, pSesInfo,
- NULL /* security blob */,
- 0 /* blob length */,
- nls_info);
+ rc = -EOPNOTSUPP;
} else if (extended_security
&& (pSesInfo->capabilities & CAP_EXTENDED_SECURITY)
&& (pSesInfo->server->secType == RawNTLMSSP)) {
diff --git a/fs/cifs/dir.c b/fs/cifs/dir.c
index 82315edc77d7..ba4cbe9b0684 100644
--- a/fs/cifs/dir.c
+++ b/fs/cifs/dir.c
@@ -113,7 +113,7 @@ cifs_bp_rename_retry:
full_path[namelen+2] = 0;
BB remove above eight lines BB */
-/* Inode operations in similar order to how they appear in the Linux file fs.h */
+/* Inode operations in similar order to how they appear in Linux file fs.h */
int
cifs_create(struct inode *inode, struct dentry *direntry, int mode,
@@ -178,11 +178,14 @@ cifs_create(struct inode *inode, struct dentry *direntry, int mode,
FreeXid(xid);
return -ENOMEM;
}
-
- rc = CIFSSMBOpen(xid, pTcon, full_path, disposition,
+ if (cifs_sb->tcon->ses->capabilities & CAP_NT_SMBS)
+ rc = CIFSSMBOpen(xid, pTcon, full_path, disposition,
desiredAccess, CREATE_NOT_DIR,
&fileHandle, &oplock, buf, cifs_sb->local_nls,
cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR);
+ else
+ rc = -EIO; /* no NT SMB support fall into legacy open below */
+
if(rc == -EIO) {
/* old server, retry the open legacy style */
rc = SMBLegacyOpen(xid, pTcon, full_path, disposition,
@@ -191,7 +194,7 @@ cifs_create(struct inode *inode, struct dentry *direntry, int mode,
cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR);
}
if (rc) {
- cFYI(1, ("cifs_create returned 0x%x ", rc));
+ cFYI(1, ("cifs_create returned 0x%x", rc));
} else {
/* If Open reported that we actually created a file
then we now have to set the mode if possible */
@@ -369,6 +372,10 @@ int cifs_mknod(struct inode *inode, struct dentry *direntry, int mode,
cifs_sb->mnt_cifs_flags &
CIFS_MOUNT_MAP_SPECIAL_CHR);
+ /* BB FIXME - add handling for backlevel servers
+ which need legacy open and check for all
+ calls to SMBOpen for fallback to
+ SMBLeagcyOpen */
if(!rc) {
/* BB Do not bother to decode buf since no
local inode yet to put timestamps in,
diff --git a/fs/cifs/fcntl.c b/fs/cifs/fcntl.c
index 633a93811328..d91a3d44e9e3 100644
--- a/fs/cifs/fcntl.c
+++ b/fs/cifs/fcntl.c
@@ -91,14 +91,14 @@ int cifs_dir_notify(struct file * file, unsigned long arg)
if(full_path == NULL) {
rc = -ENOMEM;
} else {
- cERROR(1,("cifs dir notify on file %s with arg 0x%lx",full_path,arg)); /* BB removeme BB */
+ cFYI(1,("dir notify on file %s Arg 0x%lx",full_path,arg));
rc = CIFSSMBOpen(xid, pTcon, full_path, FILE_OPEN,
GENERIC_READ | SYNCHRONIZE, 0 /* create options */,
&netfid, &oplock,NULL, cifs_sb->local_nls,
cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR);
/* BB fixme - add this handle to a notify handle list */
if(rc) {
- cERROR(1,("Could not open directory for notify")); /* BB remove BB */
+ cFYI(1,("Could not open directory for notify"));
} else {
filter = convert_to_cifs_notify_flags(arg);
if(filter != 0) {
diff --git a/fs/cifs/file.c b/fs/cifs/file.c
index b4a18c1cab0a..5861eb42e626 100644
--- a/fs/cifs/file.c
+++ b/fs/cifs/file.c
@@ -110,7 +110,6 @@ static inline int cifs_open_inode_helper(struct inode *inode, struct file *file,
&pCifsInode->openFileList);
}
write_unlock(&GlobalSMBSeslock);
- write_unlock(&file->f_owner.lock);
if (pCifsInode->clientCanCacheRead) {
/* we have the inode open somewhere else
no need to discard cache data */
@@ -201,7 +200,7 @@ int cifs_open(struct inode *inode, struct file *file)
} else {
if (file->f_flags & O_EXCL)
cERROR(1, ("could not find file instance for "
- "new file %p ", file));
+ "new file %p", file));
}
}
@@ -260,10 +259,15 @@ int cifs_open(struct inode *inode, struct file *file)
rc = -ENOMEM;
goto out;
}
- rc = CIFSSMBOpen(xid, pTcon, full_path, disposition, desiredAccess,
- CREATE_NOT_DIR, &netfid, &oplock, buf,
+
+ if (cifs_sb->tcon->ses->capabilities & CAP_NT_SMBS)
+ rc = CIFSSMBOpen(xid, pTcon, full_path, disposition,
+ desiredAccess, CREATE_NOT_DIR, &netfid, &oplock, buf,
cifs_sb->local_nls, cifs_sb->mnt_cifs_flags
& CIFS_MOUNT_MAP_SPECIAL_CHR);
+ else
+ rc = -EIO; /* no NT SMB support fall into legacy open below */
+
if (rc == -EIO) {
/* Old server, try legacy style OpenX */
rc = SMBLegacyOpen(xid, pTcon, full_path, disposition,
@@ -272,7 +276,7 @@ int cifs_open(struct inode *inode, struct file *file)
& CIFS_MOUNT_MAP_SPECIAL_CHR);
}
if (rc) {
- cFYI(1, ("cifs_open returned 0x%x ", rc));
+ cFYI(1, ("cifs_open returned 0x%x", rc));
goto out;
}
file->private_data =
@@ -282,7 +286,6 @@ int cifs_open(struct inode *inode, struct file *file)
goto out;
}
pCifsFile = cifs_init_private(file->private_data, inode, file, netfid);
- write_lock(&file->f_owner.lock);
write_lock(&GlobalSMBSeslock);
list_add(&pCifsFile->tlist, &pTcon->openFileList);
@@ -293,7 +296,6 @@ int cifs_open(struct inode *inode, struct file *file)
&oplock, buf, full_path, xid);
} else {
write_unlock(&GlobalSMBSeslock);
- write_unlock(&file->f_owner.lock);
}
if (oplock & CIFS_CREATE_ACTION) {
@@ -409,8 +411,8 @@ static int cifs_reopen_file(struct inode *inode, struct file *file,
CIFS_MOUNT_MAP_SPECIAL_CHR);
if (rc) {
up(&pCifsFile->fh_sem);
- cFYI(1, ("cifs_open returned 0x%x ", rc));
- cFYI(1, ("oplock: %d ", oplock));
+ cFYI(1, ("cifs_open returned 0x%x", rc));
+ cFYI(1, ("oplock: %d", oplock));
} else {
pCifsFile->netfid = netfid;
pCifsFile->invalidHandle = FALSE;
@@ -472,7 +474,6 @@ int cifs_close(struct inode *inode, struct file *file)
pTcon = cifs_sb->tcon;
if (pSMBFile) {
pSMBFile->closePend = TRUE;
- write_lock(&file->f_owner.lock);
if (pTcon) {
/* no sense reconnecting to close a file that is
already closed */
@@ -487,23 +488,18 @@ int cifs_close(struct inode *inode, struct file *file)
the struct would be in each open file,
but this should give enough time to
clear the socket */
- write_unlock(&file->f_owner.lock);
cERROR(1,("close with pending writes"));
msleep(timeout);
- write_lock(&file->f_owner.lock);
timeout *= 4;
}
- write_unlock(&file->f_owner.lock);
rc = CIFSSMBClose(xid, pTcon,
pSMBFile->netfid);
- write_lock(&file->f_owner.lock);
}
}
write_lock(&GlobalSMBSeslock);
list_del(&pSMBFile->flist);
list_del(&pSMBFile->tlist);
write_unlock(&GlobalSMBSeslock);
- write_unlock(&file->f_owner.lock);
kfree(pSMBFile->search_resume_name);
kfree(file->private_data);
file->private_data = NULL;
@@ -531,7 +527,7 @@ int cifs_closedir(struct inode *inode, struct file *file)
(struct cifsFileInfo *)file->private_data;
char *ptmp;
- cFYI(1, ("Closedir inode = 0x%p with ", inode));
+ cFYI(1, ("Closedir inode = 0x%p", inode));
xid = GetXid();
@@ -605,7 +601,7 @@ int cifs_lock(struct file *file, int cmd, struct file_lock *pfLock)
}
if (pfLock->fl_flags & FL_ACCESS)
cFYI(1, ("Process suspended by mandatory locking - "
- "not implemented yet "));
+ "not implemented yet"));
if (pfLock->fl_flags & FL_LEASE)
cFYI(1, ("Lease on file - not implemented yet"));
if (pfLock->fl_flags &
@@ -1375,7 +1371,7 @@ int cifs_fsync(struct file *file, struct dentry *dentry, int datasync)
xid = GetXid();
- cFYI(1, ("Sync file - name: %s datasync: 0x%x ",
+ cFYI(1, ("Sync file - name: %s datasync: 0x%x",
dentry->d_name.name, datasync));
rc = filemap_fdatawrite(inode->i_mapping);
@@ -1404,7 +1400,7 @@ int cifs_fsync(struct file *file, struct dentry *dentry, int datasync)
/* fill in rpages then
result = cifs_pagein_inode(inode, index, rpages); */ /* BB finish */
-/* cFYI(1, ("rpages is %d for sync page of Index %ld ", rpages, index));
+/* cFYI(1, ("rpages is %d for sync page of Index %ld", rpages, index));
#if 0
if (rc < 0)
@@ -1836,7 +1832,7 @@ static int cifs_readpage_worker(struct file *file, struct page *page,
if (rc < 0)
goto io_error;
else
- cFYI(1, ("Bytes read %d ",rc));
+ cFYI(1, ("Bytes read %d",rc));
file->f_dentry->d_inode->i_atime =
current_fs_time(file->f_dentry->d_inode->i_sb);
@@ -1946,7 +1942,7 @@ static int cifs_prepare_write(struct file *file, struct page *page,
return 0;
}
-struct address_space_operations cifs_addr_ops = {
+const struct address_space_operations cifs_addr_ops = {
.readpage = cifs_readpage,
.readpages = cifs_readpages,
.writepage = cifs_writepage,
@@ -1957,3 +1953,19 @@ struct address_space_operations cifs_addr_ops = {
/* .sync_page = cifs_sync_page, */
/* .direct_IO = */
};
+
+/*
+ * cifs_readpages requires the server to support a buffer large enough to
+ * contain the header plus one complete page of data. Otherwise, we need
+ * to leave cifs_readpages out of the address space operations.
+ */
+const struct address_space_operations cifs_addr_ops_smallbuf = {
+ .readpage = cifs_readpage,
+ .writepage = cifs_writepage,
+ .writepages = cifs_writepages,
+ .prepare_write = cifs_prepare_write,
+ .commit_write = cifs_commit_write,
+ .set_page_dirty = __set_page_dirty_nobuffers,
+ /* .sync_page = cifs_sync_page, */
+ /* .direct_IO = */
+};
diff --git a/fs/cifs/inode.c b/fs/cifs/inode.c
index 4093764ef461..b88147c1dc27 100644
--- a/fs/cifs/inode.c
+++ b/fs/cifs/inode.c
@@ -41,7 +41,7 @@ int cifs_get_inode_info_unix(struct inode **pinode,
char *tmp_path;
pTcon = cifs_sb->tcon;
- cFYI(1, ("Getting info on %s ", search_path));
+ cFYI(1, ("Getting info on %s", search_path));
/* could have done a find first instead but this returns more info */
rc = CIFSSMBUnixQPathInfo(xid, pTcon, search_path, &findData,
cifs_sb->local_nls, cifs_sb->mnt_cifs_flags &
@@ -97,9 +97,9 @@ int cifs_get_inode_info_unix(struct inode **pinode,
inode = *pinode;
cifsInfo = CIFS_I(inode);
- cFYI(1, ("Old time %ld ", cifsInfo->time));
+ cFYI(1, ("Old time %ld", cifsInfo->time));
cifsInfo->time = jiffies;
- cFYI(1, ("New time %ld ", cifsInfo->time));
+ cFYI(1, ("New time %ld", cifsInfo->time));
/* this is ok to set on every inode revalidate */
atomic_set(&cifsInfo->inUse,1);
@@ -180,11 +180,12 @@ int cifs_get_inode_info_unix(struct inode **pinode,
else /* not direct, send byte range locks */
inode->i_fop = &cifs_file_ops;
- inode->i_data.a_ops = &cifs_addr_ops;
/* check if server can support readpages */
if(pTcon->ses->server->maxBuf <
- 4096 + MAX_CIFS_HDR_SIZE)
- inode->i_data.a_ops->readpages = NULL;
+ PAGE_CACHE_SIZE + MAX_CIFS_HDR_SIZE)
+ inode->i_data.a_ops = &cifs_addr_ops_smallbuf;
+ else
+ inode->i_data.a_ops = &cifs_addr_ops;
} else if (S_ISDIR(inode->i_mode)) {
cFYI(1, ("Directory inode"));
inode->i_op = &cifs_dir_inode_ops;
@@ -421,23 +422,23 @@ int cifs_get_inode_info(struct inode **pinode,
inode = *pinode;
cifsInfo = CIFS_I(inode);
cifsInfo->cifsAttrs = attr;
- cFYI(1, ("Old time %ld ", cifsInfo->time));
+ cFYI(1, ("Old time %ld", cifsInfo->time));
cifsInfo->time = jiffies;
- cFYI(1, ("New time %ld ", cifsInfo->time));
+ cFYI(1, ("New time %ld", cifsInfo->time));
/* blksize needs to be multiple of two. So safer to default to
blksize and blkbits set in superblock so 2**blkbits and blksize
will match rather than setting to:
(pTcon->ses->server->maxBuf - MAX_CIFS_HDR_SIZE) & 0xFFFFFE00;*/
- /* Linux can not store file creation time unfortunately so we ignore it */
+ /* Linux can not store file creation time so ignore it */
inode->i_atime =
cifs_NTtimeToUnix(le64_to_cpu(pfindData->LastAccessTime));
inode->i_mtime =
cifs_NTtimeToUnix(le64_to_cpu(pfindData->LastWriteTime));
inode->i_ctime =
cifs_NTtimeToUnix(le64_to_cpu(pfindData->ChangeTime));
- cFYI(0, ("Attributes came in as 0x%x ", attr));
+ cFYI(0, ("Attributes came in as 0x%x", attr));
/* set default mode. will override for dirs below */
if (atomic_read(&cifsInfo->inUse) == 0)
@@ -519,10 +520,11 @@ int cifs_get_inode_info(struct inode **pinode,
else /* not direct, send byte range locks */
inode->i_fop = &cifs_file_ops;
- inode->i_data.a_ops = &cifs_addr_ops;
if(pTcon->ses->server->maxBuf <
- 4096 + MAX_CIFS_HDR_SIZE)
- inode->i_data.a_ops->readpages = NULL;
+ PAGE_CACHE_SIZE + MAX_CIFS_HDR_SIZE)
+ inode->i_data.a_ops = &cifs_addr_ops_smallbuf;
+ else
+ inode->i_data.a_ops = &cifs_addr_ops;
} else if (S_ISDIR(inode->i_mode)) {
cFYI(1, ("Directory inode"));
inode->i_op = &cifs_dir_inode_ops;
@@ -731,7 +733,7 @@ int cifs_mkdir(struct inode *inode, struct dentry *direntry, int mode)
rc = CIFSSMBMkDir(xid, pTcon, full_path, cifs_sb->local_nls,
cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR);
if (rc) {
- cFYI(1, ("cifs_mkdir returned 0x%x ", rc));
+ cFYI(1, ("cifs_mkdir returned 0x%x", rc));
d_drop(direntry);
} else {
inode->i_nlink++;
@@ -798,7 +800,7 @@ int cifs_rmdir(struct inode *inode, struct dentry *direntry)
char *full_path = NULL;
struct cifsInodeInfo *cifsInode;
- cFYI(1, ("cifs_rmdir, inode = 0x%p with ", inode));
+ cFYI(1, ("cifs_rmdir, inode = 0x%p", inode));
xid = GetXid();
@@ -1121,7 +1123,7 @@ int cifs_setattr(struct dentry *direntry, struct iattr *attrs)
xid = GetXid();
- cFYI(1, ("In cifs_setattr, name = %s attrs->iavalid 0x%x ",
+ cFYI(1, ("setattr on file %s attrs->iavalid 0x%x",
direntry->d_name.name, attrs->ia_valid));
cifs_sb = CIFS_SB(direntry->d_inode->i_sb);
@@ -1157,6 +1159,7 @@ int cifs_setattr(struct dentry *direntry, struct iattr *attrs)
when the local oplock break takes longer to flush
writebehind data than the SMB timeout for the SetPathInfo
request would allow */
+
open_file = find_writable_file(cifsInode);
if (open_file) {
__u16 nfid = open_file->netfid;
@@ -1289,7 +1292,7 @@ int cifs_setattr(struct dentry *direntry, struct iattr *attrs)
it may be useful to Windows - but we do
not want to set ctime unless some other
timestamp is changing */
- cFYI(1, ("CIFS - CTIME changed "));
+ cFYI(1, ("CIFS - CTIME changed"));
time_buf.ChangeTime =
cpu_to_le64(cifs_UnixTimeToNT(attrs->ia_ctime));
} else
@@ -1356,7 +1359,7 @@ cifs_setattr_exit:
void cifs_delete_inode(struct inode *inode)
{
- cFYI(1, ("In cifs_delete_inode, inode = 0x%p ", inode));
+ cFYI(1, ("In cifs_delete_inode, inode = 0x%p", inode));
/* may have to add back in if and when safe distributed caching of
directories added e.g. via FindNotify */
}
diff --git a/fs/cifs/link.c b/fs/cifs/link.c
index 2ec99f833142..a57f5d6e6213 100644
--- a/fs/cifs/link.c
+++ b/fs/cifs/link.c
@@ -167,7 +167,7 @@ cifs_symlink(struct inode *inode, struct dentry *direntry, const char *symname)
return -ENOMEM;
}
- cFYI(1, ("Full path: %s ", full_path));
+ cFYI(1, ("Full path: %s", full_path));
cFYI(1, ("symname is %s", symname));
/* BB what if DFS and this volume is on different share? BB */
@@ -186,8 +186,7 @@ cifs_symlink(struct inode *inode, struct dentry *direntry, const char *symname)
inode->i_sb,xid);
if (rc != 0) {
- cFYI(1,
- ("Create symlink worked but get_inode_info failed with rc = %d ",
+ cFYI(1, ("Create symlink ok, getinodeinfo fail rc = %d",
rc));
} else {
if (pTcon->nocase)
@@ -289,7 +288,7 @@ cifs_readlink(struct dentry *direntry, char __user *pBuffer, int buflen)
else {
cFYI(1,("num referral: %d",num_referrals));
if(referrals) {
- cFYI(1,("referral string: %s ",referrals));
+ cFYI(1,("referral string: %s",referrals));
strncpy(tmpbuffer, referrals, len-1);
}
}
diff --git a/fs/cifs/misc.c b/fs/cifs/misc.c
index fafd056426e4..22c937e5884f 100644
--- a/fs/cifs/misc.c
+++ b/fs/cifs/misc.c
@@ -101,6 +101,7 @@ sesInfoFree(struct cifsSesInfo *buf_to_free)
kfree(buf_to_free->serverDomain);
kfree(buf_to_free->serverNOS);
kfree(buf_to_free->password);
+ kfree(buf_to_free->domainName);
kfree(buf_to_free);
}
@@ -499,11 +500,12 @@ is_valid_oplock_break(struct smb_hdr *buf, struct TCP_Server_Info *srv)
if(pSMBr->ByteCount > sizeof(struct file_notify_information)) {
data_offset = le32_to_cpu(pSMBr->DataOffset);
- pnotify = (struct file_notify_information *)((char *)&pSMBr->hdr.Protocol
- + data_offset);
- cFYI(1,("dnotify on %s with action: 0x%x",pnotify->FileName,
+ pnotify = (struct file_notify_information *)
+ ((char *)&pSMBr->hdr.Protocol + data_offset);
+ cFYI(1,("dnotify on %s Action: 0x%x",pnotify->FileName,
pnotify->Action)); /* BB removeme BB */
- /* cifs_dump_mem("Received notify Data is: ",buf,sizeof(struct smb_hdr)+60); */
+ /* cifs_dump_mem("Rcvd notify Data: ",buf,
+ sizeof(struct smb_hdr)+60); */
return TRUE;
}
if(pSMBr->hdr.Status.CifsError) {
diff --git a/fs/cifs/netmisc.c b/fs/cifs/netmisc.c
index 5de74d216fdd..b66eff5dc624 100644
--- a/fs/cifs/netmisc.c
+++ b/fs/cifs/netmisc.c
@@ -84,11 +84,11 @@ static const struct smb_to_posix_error mapping_table_ERRDOS[] = {
static const struct smb_to_posix_error mapping_table_ERRSRV[] = {
{ERRerror, -EIO},
- {ERRbadpw, -EPERM},
+ {ERRbadpw, -EACCES}, /* was EPERM */
{ERRbadtype, -EREMOTE},
{ERRaccess, -EACCES},
{ERRinvtid, -ENXIO},
- {ERRinvnetname, -ENODEV},
+ {ERRinvnetname, -ENXIO},
{ERRinvdevice, -ENXIO},
{ERRqfull, -ENOSPC},
{ERRqtoobig, -ENOSPC},
diff --git a/fs/cifs/ntlmssp.c b/fs/cifs/ntlmssp.c
deleted file mode 100644
index 115359cc7a32..000000000000
--- a/fs/cifs/ntlmssp.c
+++ /dev/null
@@ -1,143 +0,0 @@
-/*
- * fs/cifs/ntlmssp.h
- *
- * Copyright (c) International Business Machines Corp., 2006
- * Author(s): Steve French (sfrench@us.ibm.com)
- *
- * This library is free software; you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as published
- * by the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this library; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-
-#include "cifspdu.h"
-#include "cifsglob.h"
-#include "cifsproto.h"
-#include "cifs_unicode.h"
-#include "cifs_debug.h"
-#include "ntlmssp.h"
-#include "nterr.h"
-
-#ifdef CONFIG_CIFS_EXPERIMENTAL
-static __u32 cifs_ssetup_hdr(struct cifsSesInfo *ses, SESSION_SETUP_ANDX *pSMB)
-{
- __u32 capabilities = 0;
-
- /* init fields common to all four types of SessSetup */
- /* note that header is initialized to zero in header_assemble */
- pSMB->req.AndXCommand = 0xFF;
- pSMB->req.MaxBufferSize = cpu_to_le16(ses->server->maxBuf);
- pSMB->req.MaxMpxCount = cpu_to_le16(ses->server->maxReq);
-
- /* Now no need to set SMBFLG_CASELESS or obsolete CANONICAL PATH */
-
- /* BB verify whether signing required on neg or just on auth frame
- (and NTLM case) */
-
- capabilities = CAP_LARGE_FILES | CAP_NT_SMBS | CAP_LEVEL_II_OPLOCKS |
- CAP_LARGE_WRITE_X | CAP_LARGE_READ_X;
-
- if(ses->server->secMode & (SECMODE_SIGN_REQUIRED | SECMODE_SIGN_ENABLED))
- pSMB->req.hdr.Flags2 |= SMBFLG2_SECURITY_SIGNATURE;
-
- if (ses->capabilities & CAP_UNICODE) {
- pSMB->req.hdr.Flags2 |= SMBFLG2_UNICODE;
- capabilities |= CAP_UNICODE;
- }
- if (ses->capabilities & CAP_STATUS32) {
- pSMB->req.hdr.Flags2 |= SMBFLG2_ERR_STATUS;
- capabilities |= CAP_STATUS32;
- }
- if (ses->capabilities & CAP_DFS) {
- pSMB->req.hdr.Flags2 |= SMBFLG2_DFS;
- capabilities |= CAP_DFS;
- }
-
- /* BB check whether to init vcnum BB */
- return capabilities;
-}
-int
-CIFS_SessSetup(unsigned int xid, struct cifsSesInfo *ses, const int type,
- int * pNTLMv2_flg, const struct nls_table *nls_cp)
-{
- int rc = 0;
- int wct;
- struct smb_hdr *smb_buffer;
- char *bcc_ptr;
- SESSION_SETUP_ANDX *pSMB;
- __u32 capabilities;
-
- if(ses == NULL)
- return -EINVAL;
-
- cFYI(1,("SStp type: %d",type));
- if(type < CIFS_NTLM) {
-#ifndef CONFIG_CIFS_WEAK_PW_HASH
- /* LANMAN and plaintext are less secure and off by default.
- So we make this explicitly be turned on in kconfig (in the
- build) and turned on at runtime (changed from the default)
- in proc/fs/cifs or via mount parm. Unfortunately this is
- needed for old Win (e.g. Win95), some obscure NAS and OS/2 */
- return -EOPNOTSUPP;
-#endif
- wct = 10; /* lanman 2 style sessionsetup */
- } else if(type < CIFS_NTLMSSP_NEG)
- wct = 13; /* old style NTLM sessionsetup */
- else /* same size for negotiate or auth, NTLMSSP or extended security */
- wct = 12;
-
- rc = small_smb_init_no_tc(SMB_COM_SESSION_SETUP_ANDX, wct, ses,
- (void **)&smb_buffer);
- if(rc)
- return rc;
-
- pSMB = (SESSION_SETUP_ANDX *)smb_buffer;
-
- capabilities = cifs_ssetup_hdr(ses, pSMB);
- bcc_ptr = pByteArea(smb_buffer);
- if(type > CIFS_NTLM) {
- pSMB->req.hdr.Flags2 |= SMBFLG2_EXT_SEC;
- capabilities |= CAP_EXTENDED_SECURITY;
- pSMB->req.Capabilities = cpu_to_le32(capabilities);
- /* BB set password lengths */
- } else if(type < CIFS_NTLM) /* lanman */ {
- /* no capabilities flags in old lanman negotiation */
- /* pSMB->old_req.PasswordLength = */ /* BB fixme BB */
- } else /* type CIFS_NTLM */ {
- pSMB->req_no_secext.Capabilities = cpu_to_le32(capabilities);
- pSMB->req_no_secext.CaseInsensitivePasswordLength =
- cpu_to_le16(CIFS_SESSION_KEY_SIZE);
- pSMB->req_no_secext.CaseSensitivePasswordLength =
- cpu_to_le16(CIFS_SESSION_KEY_SIZE);
- }
-
-
- /* copy session key */
-
- /* if Unicode, align strings to two byte boundary */
-
- /* copy user name */ /* BB Do we need to special case null user name? */
-
- /* copy domain name */
-
- /* copy Linux version */
-
- /* copy network operating system name */
-
- /* update bcc and smb buffer length */
-
-/* rc = SendReceive2(xid, ses, iov, num_iovecs, &resp_buf_type, 0); */
- /* SMB request buf freed in SendReceive2 */
-
- return rc;
-}
-#endif /* CONFIG_CIFS_EXPERIMENTAL */
diff --git a/fs/cifs/readdir.c b/fs/cifs/readdir.c
index b689c5035124..03bbcb377913 100644
--- a/fs/cifs/readdir.c
+++ b/fs/cifs/readdir.c
@@ -21,6 +21,7 @@
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <linux/fs.h>
+#include <linux/pagemap.h>
#include <linux/stat.h>
#include <linux/smp_lock.h>
#include "cifspdu.h"
@@ -31,8 +32,8 @@
#include "cifs_fs_sb.h"
#include "cifsfs.h"
-/* BB fixme - add debug wrappers around this function to disable it fixme BB */
-/* static void dump_cifs_file_struct(struct file *file, char *label)
+#ifdef CONFIG_CIFS_DEBUG2
+static void dump_cifs_file_struct(struct file *file, char *label)
{
struct cifsFileInfo * cf;
@@ -53,7 +54,8 @@
}
}
-} */
+}
+#endif /* DEBUG2 */
/* Returns one if new inode created (which therefore needs to be hashed) */
/* Might check in the future if inode number changed so we can rehash inode */
@@ -107,32 +109,52 @@ static int construct_dentry(struct qstr *qstring, struct file *file,
return rc;
}
-static void fill_in_inode(struct inode *tmp_inode,
- FILE_DIRECTORY_INFO *pfindData, int *pobject_type, int isNewInode)
+static void fill_in_inode(struct inode *tmp_inode, int new_buf_type,
+ char * buf, int *pobject_type, int isNewInode)
{
loff_t local_size;
struct timespec local_mtime;
struct cifsInodeInfo *cifsInfo = CIFS_I(tmp_inode);
struct cifs_sb_info *cifs_sb = CIFS_SB(tmp_inode->i_sb);
- __u32 attr = le32_to_cpu(pfindData->ExtFileAttributes);
- __u64 allocation_size = le64_to_cpu(pfindData->AllocationSize);
- __u64 end_of_file = le64_to_cpu(pfindData->EndOfFile);
-
- cifsInfo->cifsAttrs = attr;
- cifsInfo->time = jiffies;
+ __u32 attr;
+ __u64 allocation_size;
+ __u64 end_of_file;
/* save mtime and size */
local_mtime = tmp_inode->i_mtime;
local_size = tmp_inode->i_size;
+ if(new_buf_type) {
+ FILE_DIRECTORY_INFO *pfindData = (FILE_DIRECTORY_INFO *)buf;
+
+ attr = le32_to_cpu(pfindData->ExtFileAttributes);
+ allocation_size = le64_to_cpu(pfindData->AllocationSize);
+ end_of_file = le64_to_cpu(pfindData->EndOfFile);
+ tmp_inode->i_atime =
+ cifs_NTtimeToUnix(le64_to_cpu(pfindData->LastAccessTime));
+ tmp_inode->i_mtime =
+ cifs_NTtimeToUnix(le64_to_cpu(pfindData->LastWriteTime));
+ tmp_inode->i_ctime =
+ cifs_NTtimeToUnix(le64_to_cpu(pfindData->ChangeTime));
+ } else { /* legacy, OS2 and DOS style */
+ FIND_FILE_STANDARD_INFO * pfindData =
+ (FIND_FILE_STANDARD_INFO *)buf;
+
+ attr = le16_to_cpu(pfindData->Attributes);
+ allocation_size = le32_to_cpu(pfindData->AllocationSize);
+ end_of_file = le32_to_cpu(pfindData->DataSize);
+ tmp_inode->i_atime = CURRENT_TIME;
+ /* tmp_inode->i_mtime = BB FIXME - add dos time handling
+ tmp_inode->i_ctime = 0; BB FIXME */
+
+ }
+
/* Linux can not store file creation time unfortunately so ignore it */
- tmp_inode->i_atime =
- cifs_NTtimeToUnix(le64_to_cpu(pfindData->LastAccessTime));
- tmp_inode->i_mtime =
- cifs_NTtimeToUnix(le64_to_cpu(pfindData->LastWriteTime));
- tmp_inode->i_ctime =
- cifs_NTtimeToUnix(le64_to_cpu(pfindData->ChangeTime));
+
+ cifsInfo->cifsAttrs = attr;
+ cifsInfo->time = jiffies;
+
/* treat dos attribute of read-only as read-only mode bit e.g. 555? */
/* 2767 perms - indicate mandatory locking */
/* BB fill in uid and gid here? with help from winbind?
@@ -215,11 +237,13 @@ static void fill_in_inode(struct inode *tmp_inode,
else
tmp_inode->i_fop = &cifs_file_ops;
- tmp_inode->i_data.a_ops = &cifs_addr_ops;
if((cifs_sb->tcon) && (cifs_sb->tcon->ses) &&
(cifs_sb->tcon->ses->server->maxBuf <
- 4096 + MAX_CIFS_HDR_SIZE))
- tmp_inode->i_data.a_ops->readpages = NULL;
+ PAGE_CACHE_SIZE + MAX_CIFS_HDR_SIZE))
+ tmp_inode->i_data.a_ops = &cifs_addr_ops_smallbuf;
+ else
+ tmp_inode->i_data.a_ops = &cifs_addr_ops;
+
if(isNewInode)
return; /* No sense invalidating pages for new inode
since have not started caching readahead file
@@ -338,11 +362,12 @@ static void unix_fill_in_inode(struct inode *tmp_inode,
else
tmp_inode->i_fop = &cifs_file_ops;
- tmp_inode->i_data.a_ops = &cifs_addr_ops;
if((cifs_sb->tcon) && (cifs_sb->tcon->ses) &&
(cifs_sb->tcon->ses->server->maxBuf <
- 4096 + MAX_CIFS_HDR_SIZE))
- tmp_inode->i_data.a_ops->readpages = NULL;
+ PAGE_CACHE_SIZE + MAX_CIFS_HDR_SIZE))
+ tmp_inode->i_data.a_ops = &cifs_addr_ops_smallbuf;
+ else
+ tmp_inode->i_data.a_ops = &cifs_addr_ops;
if(isNewInode)
return; /* No sense invalidating pages for new inode since we
@@ -415,7 +440,10 @@ static int initiate_cifs_search(const int xid, struct file *file)
ffirst_retry:
/* test for Unix extensions */
if (pTcon->ses->capabilities & CAP_UNIX) {
- cifsFile->srch_inf.info_level = SMB_FIND_FILE_UNIX;
+ cifsFile->srch_inf.info_level = SMB_FIND_FILE_UNIX;
+ } else if ((pTcon->ses->capabilities &
+ (CAP_NT_SMBS | CAP_NT_FIND)) == 0) {
+ cifsFile->srch_inf.info_level = SMB_FIND_FILE_INFO_STANDARD;
} else if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SERVER_INUM) {
cifsFile->srch_inf.info_level = SMB_FIND_FILE_ID_FULL_DIR_INFO;
} else /* not srvinos - BB fixme add check for backlevel? */ {
@@ -451,12 +479,19 @@ static int cifs_unicode_bytelen(char *str)
return len << 1;
}
-static char *nxt_dir_entry(char *old_entry, char *end_of_smb)
+static char *nxt_dir_entry(char *old_entry, char *end_of_smb, int level)
{
char * new_entry;
FILE_DIRECTORY_INFO * pDirInfo = (FILE_DIRECTORY_INFO *)old_entry;
- new_entry = old_entry + le32_to_cpu(pDirInfo->NextEntryOffset);
+ if(level == SMB_FIND_FILE_INFO_STANDARD) {
+ FIND_FILE_STANDARD_INFO * pfData;
+ pfData = (FIND_FILE_STANDARD_INFO *)pDirInfo;
+
+ new_entry = old_entry + sizeof(FIND_FILE_STANDARD_INFO) +
+ pfData->FileNameLength;
+ } else
+ new_entry = old_entry + le32_to_cpu(pDirInfo->NextEntryOffset);
cFYI(1,("new entry %p old entry %p",new_entry,old_entry));
/* validate that new_entry is not past end of SMB */
if(new_entry >= end_of_smb) {
@@ -464,7 +499,10 @@ static char *nxt_dir_entry(char *old_entry, char *end_of_smb)
("search entry %p began after end of SMB %p old entry %p",
new_entry, end_of_smb, old_entry));
return NULL;
- } else if (new_entry + sizeof(FILE_DIRECTORY_INFO) > end_of_smb) {
+ } else if(((level == SMB_FIND_FILE_INFO_STANDARD) &&
+ (new_entry + sizeof(FIND_FILE_STANDARD_INFO) > end_of_smb)) ||
+ ((level != SMB_FIND_FILE_INFO_STANDARD) &&
+ (new_entry + sizeof(FILE_DIRECTORY_INFO) > end_of_smb))) {
cERROR(1,("search entry %p extends after end of SMB %p",
new_entry, end_of_smb));
return NULL;
@@ -482,7 +520,7 @@ static int cifs_entry_is_dot(char *current_entry, struct cifsFileInfo *cfile)
char * filename = NULL;
int len = 0;
- if(cfile->srch_inf.info_level == 0x202) {
+ if(cfile->srch_inf.info_level == SMB_FIND_FILE_UNIX) {
FILE_UNIX_INFO * pFindData = (FILE_UNIX_INFO *)current_entry;
filename = &pFindData->FileName[0];
if(cfile->srch_inf.unicode) {
@@ -491,26 +529,34 @@ static int cifs_entry_is_dot(char *current_entry, struct cifsFileInfo *cfile)
/* BB should we make this strnlen of PATH_MAX? */
len = strnlen(filename, 5);
}
- } else if(cfile->srch_inf.info_level == 0x101) {
+ } else if(cfile->srch_inf.info_level == SMB_FIND_FILE_DIRECTORY_INFO) {
FILE_DIRECTORY_INFO * pFindData =
(FILE_DIRECTORY_INFO *)current_entry;
filename = &pFindData->FileName[0];
len = le32_to_cpu(pFindData->FileNameLength);
- } else if(cfile->srch_inf.info_level == 0x102) {
+ } else if(cfile->srch_inf.info_level ==
+ SMB_FIND_FILE_FULL_DIRECTORY_INFO) {
FILE_FULL_DIRECTORY_INFO * pFindData =
(FILE_FULL_DIRECTORY_INFO *)current_entry;
filename = &pFindData->FileName[0];
len = le32_to_cpu(pFindData->FileNameLength);
- } else if(cfile->srch_inf.info_level == 0x105) {
+ } else if(cfile->srch_inf.info_level ==
+ SMB_FIND_FILE_ID_FULL_DIR_INFO) {
SEARCH_ID_FULL_DIR_INFO * pFindData =
(SEARCH_ID_FULL_DIR_INFO *)current_entry;
filename = &pFindData->FileName[0];
len = le32_to_cpu(pFindData->FileNameLength);
- } else if(cfile->srch_inf.info_level == 0x104) {
+ } else if(cfile->srch_inf.info_level ==
+ SMB_FIND_FILE_BOTH_DIRECTORY_INFO) {
FILE_BOTH_DIRECTORY_INFO * pFindData =
(FILE_BOTH_DIRECTORY_INFO *)current_entry;
filename = &pFindData->FileName[0];
len = le32_to_cpu(pFindData->FileNameLength);
+ } else if(cfile->srch_inf.info_level == SMB_FIND_FILE_INFO_STANDARD) {
+ FIND_FILE_STANDARD_INFO * pFindData =
+ (FIND_FILE_STANDARD_INFO *)current_entry;
+ filename = &pFindData->FileName[0];
+ len = le32_to_cpu(pFindData->FileNameLength);
} else {
cFYI(1,("Unknown findfirst level %d",cfile->srch_inf.info_level));
}
@@ -597,7 +643,9 @@ static int find_cifs_entry(const int xid, struct cifsTconInfo *pTcon,
. and .. for the root of a drive and for those we need
to start two entries earlier */
-/* dump_cifs_file_struct(file, "In fce ");*/
+#ifdef CONFIG_CIFS_DEBUG2
+ dump_cifs_file_struct(file, "In fce ");
+#endif
if(((index_to_find < cifsFile->srch_inf.index_of_last_entry) &&
is_dir_changed(file)) ||
(index_to_find < first_entry_in_buffer)) {
@@ -644,10 +692,12 @@ static int find_cifs_entry(const int xid, struct cifsTconInfo *pTcon,
first_entry_in_buffer = cifsFile->srch_inf.index_of_last_entry
- cifsFile->srch_inf.entries_in_buffer;
pos_in_buf = index_to_find - first_entry_in_buffer;
- cFYI(1,("found entry - pos_in_buf %d",pos_in_buf));
+ cFYI(1,("found entry - pos_in_buf %d",pos_in_buf));
+
for(i=0;(i<(pos_in_buf)) && (current_entry != NULL);i++) {
/* go entry by entry figuring out which is first */
- current_entry = nxt_dir_entry(current_entry,end_of_smb);
+ current_entry = nxt_dir_entry(current_entry,end_of_smb,
+ cifsFile->srch_inf.info_level);
}
if((current_entry == NULL) && (i < pos_in_buf)) {
/* BB fixme - check if we should flag this error */
@@ -674,7 +724,7 @@ static int find_cifs_entry(const int xid, struct cifsTconInfo *pTcon,
/* inode num, inode type and filename returned */
static int cifs_get_name_from_search_buf(struct qstr *pqst,
char *current_entry, __u16 level, unsigned int unicode,
- struct cifs_sb_info * cifs_sb, ino_t *pinum)
+ struct cifs_sb_info * cifs_sb, int max_len, ino_t *pinum)
{
int rc = 0;
unsigned int len = 0;
@@ -718,10 +768,22 @@ static int cifs_get_name_from_search_buf(struct qstr *pqst,
(FILE_BOTH_DIRECTORY_INFO *)current_entry;
filename = &pFindData->FileName[0];
len = le32_to_cpu(pFindData->FileNameLength);
+ } else if(level == SMB_FIND_FILE_INFO_STANDARD) {
+ FIND_FILE_STANDARD_INFO * pFindData =
+ (FIND_FILE_STANDARD_INFO *)current_entry;
+ filename = &pFindData->FileName[0];
+ /* one byte length, no name conversion */
+ len = (unsigned int)pFindData->FileNameLength;
} else {
cFYI(1,("Unknown findfirst level %d",level));
return -EINVAL;
}
+
+ if(len > max_len) {
+ cERROR(1,("bad search response length %d past smb end", len));
+ return -EINVAL;
+ }
+
if(unicode) {
/* BB fixme - test with long names */
/* Note converted filename can be longer than in unicode */
@@ -741,7 +803,7 @@ static int cifs_get_name_from_search_buf(struct qstr *pqst,
}
static int cifs_filldir(char *pfindEntry, struct file *file,
- filldir_t filldir, void *direntry, char *scratch_buf)
+ filldir_t filldir, void *direntry, char *scratch_buf, int max_len)
{
int rc = 0;
struct qstr qstring;
@@ -777,6 +839,7 @@ static int cifs_filldir(char *pfindEntry, struct file *file,
rc = cifs_get_name_from_search_buf(&qstring,pfindEntry,
pCifsF->srch_inf.info_level,
pCifsF->srch_inf.unicode,cifs_sb,
+ max_len,
&inum /* returned */);
if(rc)
@@ -798,13 +861,16 @@ static int cifs_filldir(char *pfindEntry, struct file *file,
/* we pass in rc below, indicating whether it is a new inode,
so we can figure out whether to invalidate the inode cached
data if the file has changed */
- if(pCifsF->srch_inf.info_level == SMB_FIND_FILE_UNIX) {
+ if(pCifsF->srch_inf.info_level == SMB_FIND_FILE_UNIX)
unix_fill_in_inode(tmp_inode,
- (FILE_UNIX_INFO *)pfindEntry,&obj_type, rc);
- } else {
- fill_in_inode(tmp_inode,
- (FILE_DIRECTORY_INFO *)pfindEntry,&obj_type, rc);
- }
+ (FILE_UNIX_INFO *)pfindEntry,
+ &obj_type, rc);
+ else if(pCifsF->srch_inf.info_level == SMB_FIND_FILE_INFO_STANDARD)
+ fill_in_inode(tmp_inode, 0 /* old level 1 buffer type */,
+ pfindEntry, &obj_type, rc);
+ else
+ fill_in_inode(tmp_inode, 1 /* NT */, pfindEntry, &obj_type, rc);
+
rc = filldir(direntry,qstring.name,qstring.len,file->f_pos,
tmp_inode->i_ino,obj_type);
@@ -864,6 +930,12 @@ static int cifs_save_resume_key(const char *current_entry,
filename = &pFindData->FileName[0];
len = le32_to_cpu(pFindData->FileNameLength);
cifsFile->srch_inf.resume_key = pFindData->FileIndex;
+ } else if(level == SMB_FIND_FILE_INFO_STANDARD) {
+ FIND_FILE_STANDARD_INFO * pFindData =
+ (FIND_FILE_STANDARD_INFO *)current_entry;
+ filename = &pFindData->FileName[0];
+ /* one byte length, no name conversion */
+ len = (unsigned int)pFindData->FileNameLength;
} else {
cFYI(1,("Unknown findfirst level %d",level));
return -EINVAL;
@@ -884,6 +956,7 @@ int cifs_readdir(struct file *file, void *direntry, filldir_t filldir)
int num_to_fill = 0;
char * tmp_buf = NULL;
char * end_of_smb;
+ int max_len;
xid = GetXid();
@@ -909,7 +982,7 @@ int cifs_readdir(struct file *file, void *direntry, filldir_t filldir)
case 1:
if (filldir(direntry, "..", 2, file->f_pos,
file->f_dentry->d_parent->d_inode->i_ino, DT_DIR) < 0) {
- cERROR(1, ("Filldir for parent dir failed "));
+ cERROR(1, ("Filldir for parent dir failed"));
rc = -ENOMEM;
break;
}
@@ -959,10 +1032,11 @@ int cifs_readdir(struct file *file, void *direntry, filldir_t filldir)
goto rddir2_exit;
}
cFYI(1,("loop through %d times filling dir for net buf %p",
- num_to_fill,cifsFile->srch_inf.ntwrk_buf_start));
- end_of_smb = cifsFile->srch_inf.ntwrk_buf_start +
- smbCalcSize((struct smb_hdr *)
- cifsFile->srch_inf.ntwrk_buf_start);
+ num_to_fill,cifsFile->srch_inf.ntwrk_buf_start));
+ max_len = smbCalcSize((struct smb_hdr *)
+ cifsFile->srch_inf.ntwrk_buf_start);
+ end_of_smb = cifsFile->srch_inf.ntwrk_buf_start + max_len;
+
/* To be safe - for UCS to UTF-8 with strings loaded
with the rare long characters alloc more to account for
such multibyte target UTF-8 characters. cifs_unicode.c,
@@ -977,17 +1051,19 @@ int cifs_readdir(struct file *file, void *direntry, filldir_t filldir)
}
/* if buggy server returns . and .. late do
we want to check for that here? */
- rc = cifs_filldir(current_entry, file,
- filldir, direntry,tmp_buf);
+ rc = cifs_filldir(current_entry, file,
+ filldir, direntry, tmp_buf, max_len);
file->f_pos++;
- if(file->f_pos == cifsFile->srch_inf.index_of_last_entry) {
+ if(file->f_pos ==
+ cifsFile->srch_inf.index_of_last_entry) {
cFYI(1,("last entry in buf at pos %lld %s",
- file->f_pos,tmp_buf)); /* BB removeme BB */
+ file->f_pos,tmp_buf));
cifs_save_resume_key(current_entry,cifsFile);
break;
} else
- current_entry = nxt_dir_entry(current_entry,
- end_of_smb);
+ current_entry =
+ nxt_dir_entry(current_entry, end_of_smb,
+ cifsFile->srch_inf.info_level);
}
kfree(tmp_buf);
break;
diff --git a/fs/cifs/sess.c b/fs/cifs/sess.c
new file mode 100644
index 000000000000..7202d534ef0b
--- /dev/null
+++ b/fs/cifs/sess.c
@@ -0,0 +1,538 @@
+/*
+ * fs/cifs/sess.c
+ *
+ * SMB/CIFS session setup handling routines
+ *
+ * Copyright (c) International Business Machines Corp., 2006
+ * Author(s): Steve French (sfrench@us.ibm.com)
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "cifspdu.h"
+#include "cifsglob.h"
+#include "cifsproto.h"
+#include "cifs_unicode.h"
+#include "cifs_debug.h"
+#include "ntlmssp.h"
+#include "nterr.h"
+#include <linux/utsname.h>
+
+extern void SMBNTencrypt(unsigned char *passwd, unsigned char *c8,
+ unsigned char *p24);
+
+static __u32 cifs_ssetup_hdr(struct cifsSesInfo *ses, SESSION_SETUP_ANDX *pSMB)
+{
+ __u32 capabilities = 0;
+
+ /* init fields common to all four types of SessSetup */
+ /* note that header is initialized to zero in header_assemble */
+ pSMB->req.AndXCommand = 0xFF;
+ pSMB->req.MaxBufferSize = cpu_to_le16(ses->server->maxBuf);
+ pSMB->req.MaxMpxCount = cpu_to_le16(ses->server->maxReq);
+
+ /* Now no need to set SMBFLG_CASELESS or obsolete CANONICAL PATH */
+
+ /* BB verify whether signing required on neg or just on auth frame
+ (and NTLM case) */
+
+ capabilities = CAP_LARGE_FILES | CAP_NT_SMBS | CAP_LEVEL_II_OPLOCKS |
+ CAP_LARGE_WRITE_X | CAP_LARGE_READ_X;
+
+ if(ses->server->secMode & (SECMODE_SIGN_REQUIRED | SECMODE_SIGN_ENABLED))
+ pSMB->req.hdr.Flags2 |= SMBFLG2_SECURITY_SIGNATURE;
+
+ if (ses->capabilities & CAP_UNICODE) {
+ pSMB->req.hdr.Flags2 |= SMBFLG2_UNICODE;
+ capabilities |= CAP_UNICODE;
+ }
+ if (ses->capabilities & CAP_STATUS32) {
+ pSMB->req.hdr.Flags2 |= SMBFLG2_ERR_STATUS;
+ capabilities |= CAP_STATUS32;
+ }
+ if (ses->capabilities & CAP_DFS) {
+ pSMB->req.hdr.Flags2 |= SMBFLG2_DFS;
+ capabilities |= CAP_DFS;
+ }
+ if (ses->capabilities & CAP_UNIX) {
+ capabilities |= CAP_UNIX;
+ }
+
+ /* BB check whether to init vcnum BB */
+ return capabilities;
+}
+
+static void unicode_ssetup_strings(char ** pbcc_area, struct cifsSesInfo *ses,
+ const struct nls_table * nls_cp)
+{
+ char * bcc_ptr = *pbcc_area;
+ int bytes_ret = 0;
+
+ /* BB FIXME add check that strings total less
+ than 335 or will need to send them as arrays */
+
+ /* unicode strings, must be word aligned before the call */
+/* if ((long) bcc_ptr % 2) {
+ *bcc_ptr = 0;
+ bcc_ptr++;
+ } */
+ /* copy user */
+ if(ses->userName == NULL) {
+ /* BB what about null user mounts - check that we do this BB */
+ } else { /* 300 should be long enough for any conceivable user name */
+ bytes_ret = cifs_strtoUCS((__le16 *) bcc_ptr, ses->userName,
+ 300, nls_cp);
+ }
+ bcc_ptr += 2 * bytes_ret;
+ bcc_ptr += 2; /* account for null termination */
+ /* copy domain */
+ if(ses->domainName == NULL)
+ bytes_ret = cifs_strtoUCS((__le16 *) bcc_ptr,
+ "CIFS_LINUX_DOM", 32, nls_cp);
+ else
+ bytes_ret = cifs_strtoUCS((__le16 *) bcc_ptr, ses->domainName,
+ 256, nls_cp);
+ bcc_ptr += 2 * bytes_ret;
+ bcc_ptr += 2; /* account for null terminator */
+
+ /* Copy OS version */
+ bytes_ret = cifs_strtoUCS((__le16 *)bcc_ptr, "Linux version ", 32,
+ nls_cp);
+ bcc_ptr += 2 * bytes_ret;
+ bytes_ret = cifs_strtoUCS((__le16 *) bcc_ptr, system_utsname.release,
+ 32, nls_cp);
+ bcc_ptr += 2 * bytes_ret;
+ bcc_ptr += 2; /* trailing null */
+
+ bytes_ret = cifs_strtoUCS((__le16 *) bcc_ptr, CIFS_NETWORK_OPSYS,
+ 32, nls_cp);
+ bcc_ptr += 2 * bytes_ret;
+ bcc_ptr += 2; /* trailing null */
+
+ *pbcc_area = bcc_ptr;
+}
+
+static void ascii_ssetup_strings(char ** pbcc_area, struct cifsSesInfo *ses,
+ const struct nls_table * nls_cp)
+{
+ char * bcc_ptr = *pbcc_area;
+
+ /* copy user */
+ /* BB what about null user mounts - check that we do this BB */
+ /* copy user */
+ if(ses->userName == NULL) {
+ /* BB what about null user mounts - check that we do this BB */
+ } else { /* 300 should be long enough for any conceivable user name */
+ strncpy(bcc_ptr, ses->userName, 300);
+ }
+ /* BB improve check for overflow */
+ bcc_ptr += strnlen(ses->userName, 300);
+ *bcc_ptr = 0;
+ bcc_ptr++; /* account for null termination */
+
+ /* copy domain */
+
+ if(ses->domainName == NULL) {
+ strcpy(bcc_ptr, "CIFS_LINUX_DOM");
+ bcc_ptr += 14; /* strlen(CIFS_LINUX_DOM) */
+ } else {
+ strncpy(bcc_ptr, ses->domainName, 256);
+ bcc_ptr += strnlen(ses->domainName, 256);
+ }
+ *bcc_ptr = 0;
+ bcc_ptr++;
+
+ /* BB check for overflow here */
+
+ strcpy(bcc_ptr, "Linux version ");
+ bcc_ptr += strlen("Linux version ");
+ strcpy(bcc_ptr, system_utsname.release);
+ bcc_ptr += strlen(system_utsname.release) + 1;
+
+ strcpy(bcc_ptr, CIFS_NETWORK_OPSYS);
+ bcc_ptr += strlen(CIFS_NETWORK_OPSYS) + 1;
+
+ *pbcc_area = bcc_ptr;
+}
+
+static int decode_unicode_ssetup(char ** pbcc_area, int bleft, struct cifsSesInfo *ses,
+ const struct nls_table * nls_cp)
+{
+ int rc = 0;
+ int words_left, len;
+ char * data = *pbcc_area;
+
+
+
+ cFYI(1,("bleft %d",bleft));
+
+
+ /* word align, if bytes remaining is not even */
+ if(bleft % 2) {
+ bleft--;
+ data++;
+ }
+ words_left = bleft / 2;
+
+ /* save off server operating system */
+ len = UniStrnlen((wchar_t *) data, words_left);
+
+/* We look for obvious messed up bcc or strings in response so we do not go off
+ the end since (at least) WIN2K and Windows XP have a major bug in not null
+ terminating last Unicode string in response */
+ if(len >= words_left)
+ return rc;
+
+ if(ses->serverOS)
+ kfree(ses->serverOS);
+ /* UTF-8 string will not grow more than four times as big as UCS-16 */
+ ses->serverOS = kzalloc(4 * len, GFP_KERNEL);
+ if(ses->serverOS != NULL) {
+ cifs_strfromUCS_le(ses->serverOS, (__le16 *)data, len,
+ nls_cp);
+ }
+ data += 2 * (len + 1);
+ words_left -= len + 1;
+
+ /* save off server network operating system */
+ len = UniStrnlen((wchar_t *) data, words_left);
+
+ if(len >= words_left)
+ return rc;
+
+ if(ses->serverNOS)
+ kfree(ses->serverNOS);
+ ses->serverNOS = kzalloc(4 * len, GFP_KERNEL); /* BB this is wrong length FIXME BB */
+ if(ses->serverNOS != NULL) {
+ cifs_strfromUCS_le(ses->serverNOS, (__le16 *)data, len,
+ nls_cp);
+ if(strncmp(ses->serverNOS, "NT LAN Manager 4",16) == 0) {
+ cFYI(1,("NT4 server"));
+ ses->flags |= CIFS_SES_NT4;
+ }
+ }
+ data += 2 * (len + 1);
+ words_left -= len + 1;
+
+ /* save off server domain */
+ len = UniStrnlen((wchar_t *) data, words_left);
+
+ if(len > words_left)
+ return rc;
+
+ if(ses->serverDomain)
+ kfree(ses->serverDomain);
+ ses->serverDomain = kzalloc(2 * (len + 1), GFP_KERNEL); /* BB FIXME wrong length */
+ if(ses->serverDomain != NULL) {
+ cifs_strfromUCS_le(ses->serverDomain, (__le16 *)data, len,
+ nls_cp);
+ ses->serverDomain[2*len] = 0;
+ ses->serverDomain[(2*len) + 1] = 0;
+ }
+ data += 2 * (len + 1);
+ words_left -= len + 1;
+
+ cFYI(1,("words left: %d",words_left));
+
+ return rc;
+}
+
+static int decode_ascii_ssetup(char ** pbcc_area, int bleft, struct cifsSesInfo *ses,
+ const struct nls_table * nls_cp)
+{
+ int rc = 0;
+ int len;
+ char * bcc_ptr = *pbcc_area;
+
+ cFYI(1,("decode sessetup ascii. bleft %d", bleft));
+
+ len = strnlen(bcc_ptr, bleft);
+ if(len >= bleft)
+ return rc;
+
+ if(ses->serverOS)
+ kfree(ses->serverOS);
+
+ ses->serverOS = kzalloc(len + 1, GFP_KERNEL);
+ if(ses->serverOS)
+ strncpy(ses->serverOS, bcc_ptr, len);
+
+ bcc_ptr += len + 1;
+ bleft -= len + 1;
+
+ len = strnlen(bcc_ptr, bleft);
+ if(len >= bleft)
+ return rc;
+
+ if(ses->serverNOS)
+ kfree(ses->serverNOS);
+
+ ses->serverNOS = kzalloc(len + 1, GFP_KERNEL);
+ if(ses->serverNOS)
+ strncpy(ses->serverNOS, bcc_ptr, len);
+
+ bcc_ptr += len + 1;
+ bleft -= len + 1;
+
+ len = strnlen(bcc_ptr, bleft);
+ if(len > bleft)
+ return rc;
+
+ if(ses->serverDomain)
+ kfree(ses->serverDomain);
+
+ ses->serverDomain = kzalloc(len + 1, GFP_KERNEL);
+ if(ses->serverOS)
+ strncpy(ses->serverOS, bcc_ptr, len);
+
+ bcc_ptr += len + 1;
+ bleft -= len + 1;
+
+ cFYI(1,("ascii: bytes left %d",bleft));
+
+ return rc;
+}
+
+int
+CIFS_SessSetup(unsigned int xid, struct cifsSesInfo *ses, int first_time,
+ const struct nls_table *nls_cp)
+{
+ int rc = 0;
+ int wct;
+ struct smb_hdr *smb_buf;
+ char *bcc_ptr;
+ char *str_area;
+ SESSION_SETUP_ANDX *pSMB;
+ __u32 capabilities;
+ int count;
+ int resp_buf_type = 0;
+ struct kvec iov[2];
+ enum securityEnum type;
+ __u16 action;
+ int bytes_remaining;
+
+ if(ses == NULL)
+ return -EINVAL;
+
+ type = ses->server->secType;
+
+ cFYI(1,("sess setup type %d",type));
+ if(type == LANMAN) {
+#ifndef CONFIG_CIFS_WEAK_PW_HASH
+ /* LANMAN and plaintext are less secure and off by default.
+ So we make this explicitly be turned on in kconfig (in the
+ build) and turned on at runtime (changed from the default)
+ in proc/fs/cifs or via mount parm. Unfortunately this is
+ needed for old Win (e.g. Win95), some obscure NAS and OS/2 */
+ return -EOPNOTSUPP;
+#endif
+ wct = 10; /* lanman 2 style sessionsetup */
+ } else if((type == NTLM) || (type == NTLMv2)) {
+ /* For NTLMv2 failures eventually may need to retry NTLM */
+ wct = 13; /* old style NTLM sessionsetup */
+ } else /* same size for negotiate or auth, NTLMSSP or extended security */
+ wct = 12;
+
+ rc = small_smb_init_no_tc(SMB_COM_SESSION_SETUP_ANDX, wct, ses,
+ (void **)&smb_buf);
+ if(rc)
+ return rc;
+
+ pSMB = (SESSION_SETUP_ANDX *)smb_buf;
+
+ capabilities = cifs_ssetup_hdr(ses, pSMB);
+
+ /* we will send the SMB in two pieces,
+ a fixed length beginning part, and a
+ second part which will include the strings
+ and rest of bcc area, in order to avoid having
+ to do a large buffer 17K allocation */
+ iov[0].iov_base = (char *)pSMB;
+ iov[0].iov_len = smb_buf->smb_buf_length + 4;
+
+ /* 2000 big enough to fit max user, domain, NOS name etc. */
+ str_area = kmalloc(2000, GFP_KERNEL);
+ bcc_ptr = str_area;
+
+ if(type == LANMAN) {
+#ifdef CONFIG_CIFS_WEAK_PW_HASH
+ char lnm_session_key[CIFS_SESS_KEY_SIZE];
+
+ /* no capabilities flags in old lanman negotiation */
+
+ pSMB->old_req.PasswordLength = CIFS_SESS_KEY_SIZE;
+ /* BB calculate hash with password */
+ /* and copy into bcc */
+
+ calc_lanman_hash(ses, lnm_session_key);
+
+/* #ifdef CONFIG_CIFS_DEBUG2
+ cifs_dump_mem("cryptkey: ",ses->server->cryptKey,
+ CIFS_SESS_KEY_SIZE);
+#endif */
+ memcpy(bcc_ptr, (char *)lnm_session_key, CIFS_SESS_KEY_SIZE);
+ bcc_ptr += CIFS_SESS_KEY_SIZE;
+
+ /* can not sign if LANMAN negotiated so no need
+ to calculate signing key? but what if server
+ changed to do higher than lanman dialect and
+ we reconnected would we ever calc signing_key? */
+
+ cFYI(1,("Negotiating LANMAN setting up strings"));
+ /* Unicode not allowed for LANMAN dialects */
+ ascii_ssetup_strings(&bcc_ptr, ses, nls_cp);
+#endif
+ } else if (type == NTLM) {
+ char ntlm_session_key[CIFS_SESS_KEY_SIZE];
+
+ pSMB->req_no_secext.Capabilities = cpu_to_le32(capabilities);
+ pSMB->req_no_secext.CaseInsensitivePasswordLength =
+ cpu_to_le16(CIFS_SESS_KEY_SIZE);
+ pSMB->req_no_secext.CaseSensitivePasswordLength =
+ cpu_to_le16(CIFS_SESS_KEY_SIZE);
+
+ /* calculate session key */
+ SMBNTencrypt(ses->password, ses->server->cryptKey,
+ ntlm_session_key);
+
+ if(first_time) /* should this be moved into common code
+ with similar ntlmv2 path? */
+ cifs_calculate_mac_key(ses->server->mac_signing_key,
+ ntlm_session_key, ses->password);
+ /* copy session key */
+
+ memcpy(bcc_ptr, (char *)ntlm_session_key,CIFS_SESS_KEY_SIZE);
+ bcc_ptr += CIFS_SESS_KEY_SIZE;
+ memcpy(bcc_ptr, (char *)ntlm_session_key,CIFS_SESS_KEY_SIZE);
+ bcc_ptr += CIFS_SESS_KEY_SIZE;
+ if(ses->capabilities & CAP_UNICODE) {
+ /* unicode strings must be word aligned */
+ if (iov[0].iov_len % 2) {
+ *bcc_ptr = 0;
+ bcc_ptr++;
+ }
+ unicode_ssetup_strings(&bcc_ptr, ses, nls_cp);
+ } else
+ ascii_ssetup_strings(&bcc_ptr, ses, nls_cp);
+ } else if (type == NTLMv2) {
+ char * v2_sess_key =
+ kmalloc(sizeof(struct ntlmv2_resp), GFP_KERNEL);
+
+ /* BB FIXME change all users of v2_sess_key to
+ struct ntlmv2_resp */
+
+ if(v2_sess_key == NULL) {
+ cifs_small_buf_release(smb_buf);
+ return -ENOMEM;
+ }
+
+ pSMB->req_no_secext.Capabilities = cpu_to_le32(capabilities);
+
+ /* LM2 password would be here if we supported it */
+ pSMB->req_no_secext.CaseInsensitivePasswordLength = 0;
+ /* cpu_to_le16(LM2_SESS_KEY_SIZE); */
+
+ pSMB->req_no_secext.CaseSensitivePasswordLength =
+ cpu_to_le16(sizeof(struct ntlmv2_resp));
+
+ /* calculate session key */
+ setup_ntlmv2_rsp(ses, v2_sess_key, nls_cp);
+ if(first_time) /* should this be moved into common code
+ with similar ntlmv2 path? */
+ /* cifs_calculate_ntlmv2_mac_key(ses->server->mac_signing_key,
+ response BB FIXME, v2_sess_key); */
+
+ /* copy session key */
+
+ /* memcpy(bcc_ptr, (char *)ntlm_session_key,LM2_SESS_KEY_SIZE);
+ bcc_ptr += LM2_SESS_KEY_SIZE; */
+ memcpy(bcc_ptr, (char *)v2_sess_key, sizeof(struct ntlmv2_resp));
+ bcc_ptr += sizeof(struct ntlmv2_resp);
+ kfree(v2_sess_key);
+ if(ses->capabilities & CAP_UNICODE) {
+ if(iov[0].iov_len % 2) {
+ *bcc_ptr = 0;
+ } bcc_ptr++;
+ unicode_ssetup_strings(&bcc_ptr, ses, nls_cp);
+ } else
+ ascii_ssetup_strings(&bcc_ptr, ses, nls_cp);
+ } else /* NTLMSSP or SPNEGO */ {
+ pSMB->req.hdr.Flags2 |= SMBFLG2_EXT_SEC;
+ capabilities |= CAP_EXTENDED_SECURITY;
+ pSMB->req.Capabilities = cpu_to_le32(capabilities);
+ /* BB set password lengths */
+ }
+
+ count = (long) bcc_ptr - (long) str_area;
+ smb_buf->smb_buf_length += count;
+
+ BCC_LE(smb_buf) = cpu_to_le16(count);
+
+ iov[1].iov_base = str_area;
+ iov[1].iov_len = count;
+ rc = SendReceive2(xid, ses, iov, 2 /* num_iovecs */, &resp_buf_type, 0);
+ /* SMB request buf freed in SendReceive2 */
+
+ cFYI(1,("ssetup rc from sendrecv2 is %d",rc));
+ if(rc)
+ goto ssetup_exit;
+
+ pSMB = (SESSION_SETUP_ANDX *)iov[0].iov_base;
+ smb_buf = (struct smb_hdr *)iov[0].iov_base;
+
+ if((smb_buf->WordCount != 3) && (smb_buf->WordCount != 4)) {
+ rc = -EIO;
+ cERROR(1,("bad word count %d", smb_buf->WordCount));
+ goto ssetup_exit;
+ }
+ action = le16_to_cpu(pSMB->resp.Action);
+ if (action & GUEST_LOGIN)
+ cFYI(1, ("Guest login")); /* BB mark SesInfo struct? */
+ ses->Suid = smb_buf->Uid; /* UID left in wire format (le) */
+ cFYI(1, ("UID = %d ", ses->Suid));
+ /* response can have either 3 or 4 word count - Samba sends 3 */
+ /* and lanman response is 3 */
+ bytes_remaining = BCC(smb_buf);
+ bcc_ptr = pByteArea(smb_buf);
+
+ if(smb_buf->WordCount == 4) {
+ __u16 blob_len;
+ blob_len = le16_to_cpu(pSMB->resp.SecurityBlobLength);
+ bcc_ptr += blob_len;
+ if(blob_len > bytes_remaining) {
+ cERROR(1,("bad security blob length %d", blob_len));
+ rc = -EINVAL;
+ goto ssetup_exit;
+ }
+ bytes_remaining -= blob_len;
+ }
+
+ /* BB check if Unicode and decode strings */
+ if(smb_buf->Flags2 & SMBFLG2_UNICODE)
+ rc = decode_unicode_ssetup(&bcc_ptr, bytes_remaining,
+ ses, nls_cp);
+ else
+ rc = decode_ascii_ssetup(&bcc_ptr, bytes_remaining, ses,nls_cp);
+
+ssetup_exit:
+ kfree(str_area);
+ if(resp_buf_type == CIFS_SMALL_BUFFER) {
+ cFYI(1,("ssetup freeing small buf %p", iov[0].iov_base));
+ cifs_small_buf_release(iov[0].iov_base);
+ } else if(resp_buf_type == CIFS_LARGE_BUFFER)
+ cifs_buf_release(iov[0].iov_base);
+
+ return rc;
+}
diff --git a/fs/cifs/smbencrypt.c b/fs/cifs/smbencrypt.c
index 6103bcdfb16d..f518c5e45035 100644
--- a/fs/cifs/smbencrypt.c
+++ b/fs/cifs/smbencrypt.c
@@ -30,6 +30,7 @@
#include <linux/random.h>
#include "cifs_unicode.h"
#include "cifspdu.h"
+#include "cifsglob.h"
#include "md5.h"
#include "cifs_debug.h"
#include "cifsencrypt.h"
diff --git a/fs/cifs/transport.c b/fs/cifs/transport.c
index 3da80409466c..17ba329e2b3d 100644
--- a/fs/cifs/transport.c
+++ b/fs/cifs/transport.c
@@ -654,8 +654,7 @@ SendReceive(const unsigned int xid, struct cifsSesInfo *ses,
if (in_buf->smb_buf_length > CIFSMaxBufSize + MAX_CIFS_HDR_SIZE - 4) {
up(&ses->server->tcpSem);
- cERROR(1,
- ("Illegal length, greater than maximum frame, %d ",
+ cERROR(1, ("Illegal length, greater than maximum frame, %d",
in_buf->smb_buf_length));
DeleteMidQEntry(midQ);
/* If not lock req, update # of requests on wire to server */
diff --git a/fs/coda/psdev.c b/fs/coda/psdev.c
index 7caee8d8ea3b..803aacf0d49c 100644
--- a/fs/coda/psdev.c
+++ b/fs/coda/psdev.c
@@ -28,7 +28,6 @@
#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/file.h>
@@ -365,22 +364,12 @@ static int init_coda_psdev(void)
err = PTR_ERR(coda_psdev_class);
goto out_chrdev;
}
- devfs_mk_dir ("coda");
- for (i = 0; i < MAX_CODADEVS; i++) {
+ for (i = 0; i < MAX_CODADEVS; i++)
class_device_create(coda_psdev_class, NULL,
MKDEV(CODA_PSDEV_MAJOR,i), NULL, "cfs%d", i);
- err = devfs_mk_cdev(MKDEV(CODA_PSDEV_MAJOR, i),
- S_IFCHR|S_IRUSR|S_IWUSR, "coda/%d", i);
- if (err)
- goto out_class;
- }
coda_sysctl_init();
goto out;
-out_class:
- for (i = 0; i < MAX_CODADEVS; i++)
- class_device_destroy(coda_psdev_class, MKDEV(CODA_PSDEV_MAJOR, i));
- class_destroy(coda_psdev_class);
out_chrdev:
unregister_chrdev(CODA_PSDEV_MAJOR, "coda");
out:
@@ -419,12 +408,9 @@ static int __init init_coda(void)
}
return 0;
out:
- for (i = 0; i < MAX_CODADEVS; i++) {
+ for (i = 0; i < MAX_CODADEVS; i++)
class_device_destroy(coda_psdev_class, MKDEV(CODA_PSDEV_MAJOR, i));
- devfs_remove("coda/%d", i);
- }
class_destroy(coda_psdev_class);
- devfs_remove("coda");
unregister_chrdev(CODA_PSDEV_MAJOR, "coda");
coda_sysctl_clean();
out1:
@@ -441,12 +427,9 @@ static void __exit exit_coda(void)
if ( err != 0 ) {
printk("coda: failed to unregister filesystem\n");
}
- for (i = 0; i < MAX_CODADEVS; i++) {
+ for (i = 0; i < MAX_CODADEVS; i++)
class_device_destroy(coda_psdev_class, MKDEV(CODA_PSDEV_MAJOR, i));
- devfs_remove("coda/%d", i);
- }
class_destroy(coda_psdev_class);
- devfs_remove("coda");
unregister_chrdev(CODA_PSDEV_MAJOR, "coda");
coda_sysctl_clean();
coda_destroy_inodecache();
diff --git a/fs/coda/symlink.c b/fs/coda/symlink.c
index b35e5bbd9c99..76e00a65a75b 100644
--- a/fs/coda/symlink.c
+++ b/fs/coda/symlink.c
@@ -50,6 +50,6 @@ fail:
return error;
}
-struct address_space_operations coda_symlink_aops = {
+const struct address_space_operations coda_symlink_aops = {
.readpage = coda_symlink_filler,
};
diff --git a/fs/compat_ioctl.c b/fs/compat_ioctl.c
index d8ecfedef189..d8d50a70c58d 100644
--- a/fs/compat_ioctl.c
+++ b/fs/compat_ioctl.c
@@ -44,7 +44,6 @@
#include <linux/loop.h>
#include <linux/auto_fs.h>
#include <linux/auto_fs4.h>
-#include <linux/devfs_fs.h>
#include <linux/tty.h>
#include <linux/vt_kern.h>
#include <linux/fb.h>
diff --git a/fs/configfs/inode.c b/fs/configfs/inode.c
index c153bd9534cb..e14488ca6411 100644
--- a/fs/configfs/inode.c
+++ b/fs/configfs/inode.c
@@ -38,7 +38,7 @@
extern struct super_block * configfs_sb;
-static struct address_space_operations configfs_aops = {
+static const struct address_space_operations configfs_aops = {
.readpage = simple_readpage,
.prepare_write = simple_prepare_write,
.commit_write = simple_commit_write
diff --git a/fs/cramfs/inode.c b/fs/cramfs/inode.c
index c45d73860803..223c0431042d 100644
--- a/fs/cramfs/inode.c
+++ b/fs/cramfs/inode.c
@@ -30,7 +30,7 @@
static struct super_operations cramfs_ops;
static struct inode_operations cramfs_dir_inode_operations;
static const struct file_operations cramfs_directory_operations;
-static struct address_space_operations cramfs_aops;
+static const struct address_space_operations cramfs_aops;
static DEFINE_MUTEX(read_mutex);
@@ -501,7 +501,7 @@ static int cramfs_readpage(struct file *file, struct page * page)
return 0;
}
-static struct address_space_operations cramfs_aops = {
+static const struct address_space_operations cramfs_aops = {
.readpage = cramfs_readpage
};
diff --git a/fs/devfs/Makefile b/fs/devfs/Makefile
deleted file mode 100644
index 6dd8d1245e2c..000000000000
--- a/fs/devfs/Makefile
+++ /dev/null
@@ -1,8 +0,0 @@
-#
-# Makefile for the linux devfs-filesystem routines.
-#
-
-obj-$(CONFIG_DEVFS_FS) += devfs.o
-
-devfs-objs := base.o util.o
-
diff --git a/fs/devfs/base.c b/fs/devfs/base.c
deleted file mode 100644
index 51a97f132745..000000000000
--- a/fs/devfs/base.c
+++ /dev/null
@@ -1,2836 +0,0 @@
-/* devfs (Device FileSystem) driver.
-
- Copyright (C) 1998-2002 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
- 20000306 Richard Gooch <rgooch@atnf.csiro.au>
- Added DEVFS_ FL_NO_PERSISTENCE flag.
- Removed unnecessary call to <update_devfs_inode_from_entry> in
- <devfs_readdir>.
- Work sponsored by SGI.
- v0.93
- 20000413 Richard Gooch <rgooch@atnf.csiro.au>
- Set inode->i_size to correct size for symlinks.
- 20000414 Richard Gooch <rgooch@atnf.csiro.au>
- Only give lookup() method to directories to comply with new VFS
- assumptions.
- Work sponsored by SGI.
- 20000415 Richard Gooch <rgooch@atnf.csiro.au>
- Remove unnecessary tests in symlink methods.
- Don't kill existing block ops in <devfs_read_inode>.
- Work sponsored by SGI.
- v0.94
- 20000424 Richard Gooch <rgooch@atnf.csiro.au>
- Don't create missing directories in <devfs_find_handle>.
- Work sponsored by SGI.
- v0.95
- 20000430 Richard Gooch <rgooch@atnf.csiro.au>
- Added CONFIG_DEVFS_MOUNT.
- Work sponsored by SGI.
- v0.96
- 20000608 Richard Gooch <rgooch@atnf.csiro.au>
- Disabled multi-mount capability (use VFS bindings instead).
- Work sponsored by SGI.
- v0.97
- 20000610 Richard Gooch <rgooch@atnf.csiro.au>
- Switched to FS_SINGLE to disable multi-mounts.
- 20000612 Richard Gooch <rgooch@atnf.csiro.au>
- Removed module support.
- Removed multi-mount code.
- Removed compatibility macros: VFS has changed too much.
- Work sponsored by SGI.
- v0.98
- 20000614 Richard Gooch <rgooch@atnf.csiro.au>
- Merged devfs inode into devfs entry.
- Work sponsored by SGI.
- v0.99
- 20000619 Richard Gooch <rgooch@atnf.csiro.au>
- Removed dead code in <devfs_register> which used to call
- <free_dentries>.
- Work sponsored by SGI.
- v0.100
- 20000621 Richard Gooch <rgooch@atnf.csiro.au>
- Changed interface to <devfs_register>.
- Work sponsored by SGI.
- v0.101
- 20000622 Richard Gooch <rgooch@atnf.csiro.au>
- Simplified interface to <devfs_mk_symlink> and <devfs_mk_dir>.
- Simplified interface to <devfs_find_handle>.
- Work sponsored by SGI.
- v0.102
- 20010519 Richard Gooch <rgooch@atnf.csiro.au>
- Ensure <devfs_generate_path> terminates string for root entry.
- Exported <devfs_get_name> to modules.
- 20010520 Richard Gooch <rgooch@atnf.csiro.au>
- Make <devfs_mk_symlink> send events to devfsd.
- Cleaned up option processing in <devfs_setup>.
- 20010521 Richard Gooch <rgooch@atnf.csiro.au>
- Fixed bugs in handling symlinks: could leak or cause Oops.
- 20010522 Richard Gooch <rgooch@atnf.csiro.au>
- Cleaned up directory handling by separating fops.
- v0.103
- 20010601 Richard Gooch <rgooch@atnf.csiro.au>
- Fixed handling of inverted options in <devfs_setup>.
- v0.104
- 20010604 Richard Gooch <rgooch@atnf.csiro.au>
- Adjusted <try_modload> to account for <devfs_generate_path> fix.
- v0.105
- 20010617 Richard Gooch <rgooch@atnf.csiro.au>
- Answered question posed by Al Viro and removed his comments.
- Moved setting of registered flag after other fields are changed.
- Fixed race between <devfsd_close> and <devfsd_notify_one>.
- Global VFS changes added bogus BKL to <devfsd_close>: removed.
- Widened locking in <devfs_readlink> and <devfs_follow_link>.
- Replaced <devfsd_read> stack usage with <devfsd_ioctl> kmalloc.
- Simplified locking in <devfsd_ioctl> and fixed memory leak.
- v0.106
- 20010709 Richard Gooch <rgooch@atnf.csiro.au>
- Removed broken devnum allocation and use <devfs_alloc_devnum>.
- Fixed old devnum leak by calling new <devfs_dealloc_devnum>.
- v0.107
- 20010712 Richard Gooch <rgooch@atnf.csiro.au>
- Fixed bug in <devfs_setup> which could hang boot process.
- v0.108
- 20010730 Richard Gooch <rgooch@atnf.csiro.au>
- Added DEVFSD_NOTIFY_DELETE event.
- 20010801 Richard Gooch <rgooch@atnf.csiro.au>
- Removed #include <asm/segment.h>.
- v0.109
- 20010807 Richard Gooch <rgooch@atnf.csiro.au>
- Fixed inode table races by removing it and using
- inode->u.generic_ip instead.
- Moved <devfs_read_inode> into <get_vfs_inode>.
- Moved <devfs_write_inode> into <devfs_notify_change>.
- v0.110
- 20010808 Richard Gooch <rgooch@atnf.csiro.au>
- Fixed race in <devfs_do_symlink> for uni-processor.
- v0.111
- 20010818 Richard Gooch <rgooch@atnf.csiro.au>
- Removed remnant of multi-mount support in <devfs_mknod>.
- Removed unused DEVFS_FL_SHOW_UNREG flag.
- v0.112
- 20010820 Richard Gooch <rgooch@atnf.csiro.au>
- Removed nlink field from struct devfs_inode.
- v0.113
- 20010823 Richard Gooch <rgooch@atnf.csiro.au>
- Replaced BKL with global rwsem to protect symlink data (quick
- and dirty hack).
- v0.114
- 20010827 Richard Gooch <rgooch@atnf.csiro.au>
- Replaced global rwsem for symlink with per-link refcount.
- v0.115
- 20010919 Richard Gooch <rgooch@atnf.csiro.au>
- Set inode->i_mapping->a_ops for block nodes in <get_vfs_inode>.
- v0.116
- 20011008 Richard Gooch <rgooch@atnf.csiro.au>
- Fixed overrun in <devfs_link> by removing function (not needed).
- 20011009 Richard Gooch <rgooch@atnf.csiro.au>
- Fixed buffer underrun in <try_modload>.
- 20011029 Richard Gooch <rgooch@atnf.csiro.au>
- Fixed race in <devfsd_ioctl> when setting event mask.
- 20011114 Richard Gooch <rgooch@atnf.csiro.au>
- First release of new locking code.
- v1.0
- 20011117 Richard Gooch <rgooch@atnf.csiro.au>
- Discard temporary buffer, now use "%s" for dentry names.
- 20011118 Richard Gooch <rgooch@atnf.csiro.au>
- Don't generate path in <try_modload>: use fake entry instead.
- Use "existing" directory in <_devfs_make_parent_for_leaf>.
- 20011122 Richard Gooch <rgooch@atnf.csiro.au>
- Use slab cache rather than fixed buffer for devfsd events.
- v1.1
- 20011125 Richard Gooch <rgooch@atnf.csiro.au>
- Send DEVFSD_NOTIFY_REGISTERED events in <devfs_mk_dir>.
- 20011127 Richard Gooch <rgooch@atnf.csiro.au>
- Fixed locking bug in <devfs_d_revalidate_wait> due to typo.
- Do not send CREATE, CHANGE, ASYNC_OPEN or DELETE events from
- devfsd or children.
- v1.2
- 20011202 Richard Gooch <rgooch@atnf.csiro.au>
- Fixed bug in <devfsd_read>: was dereferencing freed pointer.
- v1.3
- 20011203 Richard Gooch <rgooch@atnf.csiro.au>
- Fixed bug in <devfsd_close>: was dereferencing freed pointer.
- Added process group check for devfsd privileges.
- v1.4
- 20011204 Richard Gooch <rgooch@atnf.csiro.au>
- Use SLAB_ATOMIC in <devfsd_notify_de> from <devfs_d_delete>.
- v1.5
- 20011211 Richard Gooch <rgooch@atnf.csiro.au>
- Return old entry in <devfs_mk_dir> for 2.4.x kernels.
- 20011212 Richard Gooch <rgooch@atnf.csiro.au>
- Increment refcount on module in <check_disc_changed>.
- 20011215 Richard Gooch <rgooch@atnf.csiro.au>
- Created <devfs_get_handle> and exported <devfs_put>.
- Increment refcount on module in <devfs_get_ops>.
- Created <devfs_put_ops>.
- v1.6
- 20011216 Richard Gooch <rgooch@atnf.csiro.au>
- Added poisoning to <devfs_put>.
- Improved debugging messages.
- v1.7
- 20011221 Richard Gooch <rgooch@atnf.csiro.au>
- Corrected (made useful) debugging message in <unregister>.
- Moved <kmem_cache_create> in <mount_devfs_fs> to <init_devfs_fs>
- 20011224 Richard Gooch <rgooch@atnf.csiro.au>
- Added magic number to guard against scribbling drivers.
- 20011226 Richard Gooch <rgooch@atnf.csiro.au>
- Only return old entry in <devfs_mk_dir> if a directory.
- Defined macros for error and debug messages.
- v1.8
- 20020113 Richard Gooch <rgooch@atnf.csiro.au>
- Fixed (rare, old) race in <devfs_lookup>.
- v1.9
- 20020120 Richard Gooch <rgooch@atnf.csiro.au>
- Fixed deadlock bug in <devfs_d_revalidate_wait>.
- Tag VFS deletable in <devfs_mk_symlink> if handle ignored.
- v1.10
- 20020129 Richard Gooch <rgooch@atnf.csiro.au>
- Added KERN_* to remaining messages.
- Cleaned up declaration of <stat_read>.
- v1.11
- 20020219 Richard Gooch <rgooch@atnf.csiro.au>
- Changed <devfs_rmdir> to allow later additions if not yet empty.
- v1.12
- 20020406 Richard Gooch <rgooch@atnf.csiro.au>
- Removed silently introduced calls to lock_kernel() and
- unlock_kernel() due to recent VFS locking changes. BKL isn't
- required in devfs.
- v1.13
- 20020428 Richard Gooch <rgooch@atnf.csiro.au>
- Removed 2.4.x compatibility code.
- v1.14
- 20020510 Richard Gooch <rgooch@atnf.csiro.au>
- Added BKL to <devfs_open> because drivers still need it.
- v1.15
- 20020512 Richard Gooch <rgooch@atnf.csiro.au>
- Protected <scan_dir_for_removable> and <get_removable_partition>
- from changing directory contents.
- v1.16
- 20020514 Richard Gooch <rgooch@atnf.csiro.au>
- Minor cleanup of <scan_dir_for_removable>.
- v1.17
- 20020721 Richard Gooch <rgooch@atnf.csiro.au>
- Switched to ISO C structure field initialisers.
- Switch to set_current_state() and move before add_wait_queue().
- 20020722 Richard Gooch <rgooch@atnf.csiro.au>
- Fixed devfs entry leak in <devfs_readdir> when *readdir fails.
- v1.18
- 20020725 Richard Gooch <rgooch@atnf.csiro.au>
- Created <devfs_find_and_unregister>.
- v1.19
- 20020728 Richard Gooch <rgooch@atnf.csiro.au>
- Removed deprecated <devfs_find_handle>.
- v1.20
- 20020820 Richard Gooch <rgooch@atnf.csiro.au>
- Fixed module unload race in <devfs_open>.
- v1.21
- 20021013 Richard Gooch <rgooch@atnf.csiro.au>
- Removed DEVFS_ FL_AUTO_OWNER.
- Switched lingering structure field initialiser to ISO C.
- Added locking when updating FCB flags.
- v1.22
-*/
-#include <linux/types.h>
-#include <linux/errno.h>
-#include <linux/time.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/slab.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/devfs_fs.h>
-#include <linux/devfs_fs_kernel.h>
-#include <linux/smp_lock.h>
-#include <linux/smp.h>
-#include <linux/rwsem.h>
-#include <linux/sched.h>
-#include <linux/namei.h>
-#include <linux/bitops.h>
-
-#include <asm/uaccess.h>
-#include <asm/io.h>
-#include <asm/processor.h>
-#include <asm/system.h>
-#include <asm/pgtable.h>
-#include <asm/atomic.h>
-
-#define DEVFS_VERSION "2004-01-31"
-
-#define DEVFS_NAME "devfs"
-
-#define FIRST_INODE 1
-
-#define STRING_LENGTH 256
-#define FAKE_BLOCK_SIZE 1024
-#define POISON_PTR ( *(void **) poison_array )
-#define MAGIC_VALUE 0x327db823
-
-#ifndef TRUE
-# define TRUE 1
-# define FALSE 0
-#endif
-
-#define MODE_DIR (S_IFDIR | S_IWUSR | S_IRUGO | S_IXUGO)
-
-#define DEBUG_NONE 0x0000000
-#define DEBUG_MODULE_LOAD 0x0000001
-#define DEBUG_REGISTER 0x0000002
-#define DEBUG_UNREGISTER 0x0000004
-#define DEBUG_FREE 0x0000008
-#define DEBUG_SET_FLAGS 0x0000010
-#define DEBUG_S_READ 0x0000100 /* Break */
-#define DEBUG_I_LOOKUP 0x0001000 /* Break */
-#define DEBUG_I_CREATE 0x0002000
-#define DEBUG_I_GET 0x0004000
-#define DEBUG_I_CHANGE 0x0008000
-#define DEBUG_I_UNLINK 0x0010000
-#define DEBUG_I_RLINK 0x0020000
-#define DEBUG_I_FLINK 0x0040000
-#define DEBUG_I_MKNOD 0x0080000
-#define DEBUG_F_READDIR 0x0100000 /* Break */
-#define DEBUG_D_DELETE 0x1000000 /* Break */
-#define DEBUG_D_RELEASE 0x2000000
-#define DEBUG_D_IPUT 0x4000000
-#define DEBUG_ALL 0xfffffff
-#define DEBUG_DISABLED DEBUG_NONE
-
-#define OPTION_NONE 0x00
-#define OPTION_MOUNT 0x01
-
-#define PRINTK(format, args...) \
- {printk (KERN_ERR "%s" format, __FUNCTION__ , ## args);}
-
-#define OOPS(format, args...) \
- {printk (KERN_CRIT "%s" format, __FUNCTION__ , ## args); \
- printk ("Forcing Oops\n"); \
- BUG();}
-
-#ifdef CONFIG_DEVFS_DEBUG
-# define VERIFY_ENTRY(de) \
- {if ((de) && (de)->magic_number != MAGIC_VALUE) \
- OOPS ("(%p): bad magic value: %x\n", (de), (de)->magic_number);}
-# define WRITE_ENTRY_MAGIC(de,magic) (de)->magic_number = (magic)
-# define DPRINTK(flag, format, args...) \
- {if (devfs_debug & flag) \
- printk (KERN_INFO "%s" format, __FUNCTION__ , ## args);}
-#else
-# define VERIFY_ENTRY(de)
-# define WRITE_ENTRY_MAGIC(de,magic)
-# define DPRINTK(flag, format, args...)
-#endif
-
-typedef struct devfs_entry *devfs_handle_t;
-
-struct directory_type {
- rwlock_t lock; /* Lock for searching(R)/updating(W) */
- struct devfs_entry *first;
- struct devfs_entry *last;
- unsigned char no_more_additions:1;
-};
-
-struct symlink_type {
- unsigned int length; /* Not including the NULL-termimator */
- char *linkname; /* This is NULL-terminated */
-};
-
-struct devfs_inode { /* This structure is for "persistent" inode storage */
- struct dentry *dentry;
- struct timespec atime;
- struct timespec mtime;
- struct timespec ctime;
- unsigned int ino; /* Inode number as seen in the VFS */
- uid_t uid;
- gid_t gid;
-};
-
-struct devfs_entry {
-#ifdef CONFIG_DEVFS_DEBUG
- unsigned int magic_number;
-#endif
- void *info;
- atomic_t refcount; /* When this drops to zero, it's unused */
- union {
- struct directory_type dir;
- dev_t dev;
- struct symlink_type symlink;
- const char *name; /* Only used for (mode == 0) */
- } 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_inode inode;
- umode_t mode;
- unsigned short namelen; /* I think 64k+ filenames are a way off... */
- unsigned char vfs:1; /* Whether the VFS may delete the entry */
- 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;
-
-struct devfsd_buf_entry {
- struct devfs_entry *de; /* The name is generated with this */
- unsigned short type; /* The type of event */
- umode_t mode;
- uid_t uid;
- gid_t gid;
- struct devfsd_buf_entry *next;
-};
-
-struct fs_info { /* This structure is for the mounted devfs */
- struct super_block *sb;
- spinlock_t devfsd_buffer_lock; /* Lock when inserting/deleting events */
- struct devfsd_buf_entry *devfsd_first_event;
- struct devfsd_buf_entry *devfsd_last_event;
- volatile int devfsd_sleeping;
- volatile struct task_struct *devfsd_task;
- volatile pid_t devfsd_pgrp;
- volatile struct file *devfsd_file;
- struct devfsd_notify_struct *devfsd_info;
- volatile unsigned long devfsd_event_mask;
- atomic_t devfsd_overrun_count;
- wait_queue_head_t devfsd_wait_queue; /* Wake devfsd on input */
- wait_queue_head_t revalidate_wait_queue; /* Wake when devfsd sleeps */
-};
-
-static struct fs_info fs_info = {.devfsd_buffer_lock = SPIN_LOCK_UNLOCKED };
-static kmem_cache_t *devfsd_buf_cache;
-#ifdef CONFIG_DEVFS_DEBUG
-static unsigned int devfs_debug_init __initdata = DEBUG_NONE;
-static unsigned int devfs_debug = DEBUG_NONE;
-static DEFINE_SPINLOCK(stat_lock);
-static unsigned int stat_num_entries;
-static unsigned int stat_num_bytes;
-#endif
-static unsigned char poison_array[8] =
- { 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a };
-
-#ifdef CONFIG_DEVFS_MOUNT
-static unsigned int boot_options = OPTION_MOUNT;
-#else
-static unsigned int boot_options = OPTION_NONE;
-#endif
-
-/* Forward function declarations */
-static devfs_handle_t _devfs_walk_path(struct devfs_entry *dir,
- const char *name, int namelen,
- int traverse_symlink);
-static ssize_t devfsd_read(struct file *file, char __user *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);
-#ifdef CONFIG_DEVFS_DEBUG
-static ssize_t stat_read(struct file *file, char __user *buf, size_t len,
- loff_t * ppos);
-static const struct file_operations stat_fops = {
- .open = nonseekable_open,
- .read = stat_read,
-};
-#endif
-
-/* Devfs daemon file operations */
-static const struct file_operations devfsd_fops = {
- .open = nonseekable_open,
- .read = devfsd_read,
- .ioctl = devfsd_ioctl,
- .release = devfsd_close,
-};
-
-/* Support functions follow */
-
-/**
- * devfs_get - Get a reference to a devfs entry.
- * @de: The devfs entry.
- */
-
-static struct devfs_entry *devfs_get(struct devfs_entry *de)
-{
- VERIFY_ENTRY(de);
- if (de)
- atomic_inc(&de->refcount);
- return de;
-} /* End Function devfs_get */
-
-/**
- * devfs_put - Put (release) a reference to a devfs entry.
- * @de: The handle to the devfs entry.
- */
-
-static void devfs_put(devfs_handle_t de)
-{
- if (!de)
- return;
- VERIFY_ENTRY(de);
- if (de->info == POISON_PTR)
- OOPS("(%p): poisoned pointer\n", de);
- if (!atomic_dec_and_test(&de->refcount))
- return;
- if (de == root_entry)
- OOPS("(%p): root entry being freed\n", de);
- DPRINTK(DEBUG_FREE, "(%s): de: %p, parent: %p \"%s\"\n",
- de->name, de, de->parent,
- de->parent ? de->parent->name : "no parent");
- if (S_ISLNK(de->mode))
- kfree(de->u.symlink.linkname);
- WRITE_ENTRY_MAGIC(de, 0);
-#ifdef CONFIG_DEVFS_DEBUG
- spin_lock(&stat_lock);
- --stat_num_entries;
- stat_num_bytes -= sizeof *de + de->namelen;
- if (S_ISLNK(de->mode))
- stat_num_bytes -= de->u.symlink.length + 1;
- spin_unlock(&stat_lock);
-#endif
- de->info = POISON_PTR;
- kfree(de);
-} /* End Function devfs_put */
-
-/**
- * _devfs_search_dir - Search for a devfs entry in a directory.
- * @dir: The directory to search.
- * @name: The name of the entry to search for.
- * @namelen: The number of characters in @name.
- *
- * Search for a devfs entry in a directory and returns a pointer to the entry
- * on success, else %NULL. The directory must be locked already.
- * An implicit devfs_get() is performed on the returned entry.
- */
-
-static struct devfs_entry *_devfs_search_dir(struct devfs_entry *dir,
- const char *name,
- unsigned int namelen)
-{
- struct devfs_entry *curr;
-
- if (!S_ISDIR(dir->mode)) {
- PRINTK("(%s): not a directory\n", dir->name);
- return NULL;
- }
- for (curr = dir->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 */
- }
- return devfs_get(curr);
-} /* End Function _devfs_search_dir */
-
-/**
- * _devfs_alloc_entry - Allocate a devfs entry.
- * @name: the name of the entry
- * @namelen: the number of characters in @name
- * @mode: the mode for the entry
- *
- * Allocate a devfs entry and returns a pointer to the entry on success, else
- * %NULL.
- */
-
-static struct devfs_entry *_devfs_alloc_entry(const char *name,
- unsigned int namelen,
- umode_t mode)
-{
- struct devfs_entry *new;
- static unsigned long inode_counter = FIRST_INODE;
- static DEFINE_SPINLOCK(counter_lock);
-
- if (name && (namelen < 1))
- namelen = strlen(name);
- if ((new = kmalloc(sizeof *new + namelen, GFP_KERNEL)) == NULL)
- return NULL;
- memset(new, 0, sizeof *new + namelen); /* Will set '\0' on name */
- new->mode = mode;
- if (S_ISDIR(mode))
- rwlock_init(&new->u.dir.lock);
- atomic_set(&new->refcount, 1);
- spin_lock(&counter_lock);
- new->inode.ino = inode_counter++;
- spin_unlock(&counter_lock);
- if (name)
- memcpy(new->name, name, namelen);
- new->namelen = namelen;
- WRITE_ENTRY_MAGIC(new, MAGIC_VALUE);
-#ifdef CONFIG_DEVFS_DEBUG
- spin_lock(&stat_lock);
- ++stat_num_entries;
- stat_num_bytes += sizeof *new + namelen;
- spin_unlock(&stat_lock);
-#endif
- return new;
-} /* End Function _devfs_alloc_entry */
-
-/**
- * _devfs_append_entry - Append a devfs entry to a directory's child list.
- * @dir: The directory to add to.
- * @de: The devfs entry to append.
- * @old_de: If an existing entry exists, it will be written here. This may
- * be %NULL. An implicit devfs_get() is performed on this entry.
- *
- * Append a devfs entry to a directory's list of children, checking first to
- * see if an entry of the same name exists. The directory will be locked.
- * The value 0 is returned on success, else a negative error code.
- * On failure, an implicit devfs_put() is performed on %de.
- */
-
-static int _devfs_append_entry(devfs_handle_t dir, devfs_handle_t de,
- devfs_handle_t * old_de)
-{
- int retval;
-
- if (old_de)
- *old_de = NULL;
- if (!S_ISDIR(dir->mode)) {
- PRINTK("(%s): dir: \"%s\" is not a directory\n", de->name,
- dir->name);
- devfs_put(de);
- return -ENOTDIR;
- }
- write_lock(&dir->u.dir.lock);
- if (dir->u.dir.no_more_additions)
- retval = -ENOENT;
- else {
- struct devfs_entry *old;
-
- old = _devfs_search_dir(dir, de->name, de->namelen);
- if (old_de)
- *old_de = old;
- else
- devfs_put(old);
- if (old == NULL) {
- de->parent = dir;
- de->prev = dir->u.dir.last;
- /* Append to the directory's list of children */
- if (dir->u.dir.first == NULL)
- dir->u.dir.first = de;
- else
- dir->u.dir.last->next = de;
- dir->u.dir.last = de;
- retval = 0;
- } else
- retval = -EEXIST;
- }
- write_unlock(&dir->u.dir.lock);
- if (retval)
- devfs_put(de);
- return retval;
-} /* End Function _devfs_append_entry */
-
-/**
- * _devfs_get_root_entry - Get the root devfs entry.
- *
- * Returns the root devfs entry on success, else %NULL.
- *
- * TODO it must be called asynchronously due to the fact
- * that devfs is initialized relatively late. Proper way
- * is to remove module_init from init_devfs_fs and manually
- * call it early enough during system init
- */
-
-static struct devfs_entry *_devfs_get_root_entry(void)
-{
- struct devfs_entry *new;
- static DEFINE_SPINLOCK(root_lock);
-
- if (root_entry)
- return root_entry;
-
- new = _devfs_alloc_entry(NULL, 0, MODE_DIR);
- if (new == NULL)
- return NULL;
-
- spin_lock(&root_lock);
- if (root_entry) {
- spin_unlock(&root_lock);
- devfs_put(new);
- return root_entry;
- }
- root_entry = new;
- spin_unlock(&root_lock);
-
- return root_entry;
-} /* End Function _devfs_get_root_entry */
-
-/**
- * _devfs_descend - Descend down a tree using the next component name.
- * @dir: The directory to search.
- * @name: The component name to search for.
- * @namelen: The length of %name.
- * @next_pos: The position of the next '/' or '\0' is written here.
- *
- * Descend into a directory, searching for a component. This function forms
- * the core of a tree-walking algorithm. The directory will be locked.
- * The devfs entry corresponding to the component is returned. If there is
- * no matching entry, %NULL is returned.
- * An implicit devfs_get() is performed on the returned entry.
- */
-
-static struct devfs_entry *_devfs_descend(struct devfs_entry *dir,
- const char *name, int namelen,
- int *next_pos)
-{
- const char *stop, *ptr;
- struct devfs_entry *entry;
-
- if ((namelen >= 3) && (strncmp(name, "../", 3) == 0)) { /* Special-case going to parent directory */
- *next_pos = 3;
- return devfs_get(dir->parent);
- }
- stop = name + namelen;
- /* Search for a possible '/' */
- for (ptr = name; (ptr < stop) && (*ptr != '/'); ++ptr) ;
- *next_pos = ptr - name;
- read_lock(&dir->u.dir.lock);
- entry = _devfs_search_dir(dir, name, *next_pos);
- read_unlock(&dir->u.dir.lock);
- return entry;
-} /* End Function _devfs_descend */
-
-static devfs_handle_t _devfs_make_parent_for_leaf(struct devfs_entry *dir,
- const char *name,
- int namelen, int *leaf_pos)
-{
- int next_pos = 0;
-
- if (dir == NULL)
- dir = _devfs_get_root_entry();
- if (dir == NULL)
- return NULL;
- devfs_get(dir);
- /* Search for possible trailing component and ignore it */
- for (--namelen; (namelen > 0) && (name[namelen] != '/'); --namelen) ;
- *leaf_pos = (name[namelen] == '/') ? (namelen + 1) : 0;
- for (; namelen > 0; name += next_pos, namelen -= next_pos) {
- struct devfs_entry *de, *old = NULL;
-
- if ((de =
- _devfs_descend(dir, name, namelen, &next_pos)) == NULL) {
- de = _devfs_alloc_entry(name, next_pos, MODE_DIR);
- devfs_get(de);
- if (!de || _devfs_append_entry(dir, de, &old)) {
- devfs_put(de);
- if (!old || !S_ISDIR(old->mode)) {
- devfs_put(old);
- devfs_put(dir);
- return NULL;
- }
- de = old; /* Use the existing directory */
- }
- }
- if (de == dir->parent) {
- devfs_put(dir);
- devfs_put(de);
- return NULL;
- }
- devfs_put(dir);
- dir = de;
- if (name[next_pos] == '/')
- ++next_pos;
- }
- return dir;
-} /* End Function _devfs_make_parent_for_leaf */
-
-static devfs_handle_t _devfs_prepare_leaf(devfs_handle_t * dir,
- const char *name, umode_t mode)
-{
- int namelen, leaf_pos;
- struct devfs_entry *de;
-
- namelen = strlen(name);
- if ((*dir = _devfs_make_parent_for_leaf(*dir, name, namelen,
- &leaf_pos)) == NULL) {
- PRINTK("(%s): could not create parent path\n", name);
- return NULL;
- }
- if ((de = _devfs_alloc_entry(name + leaf_pos, namelen - leaf_pos, mode))
- == NULL) {
- PRINTK("(%s): could not allocate entry\n", name);
- devfs_put(*dir);
- return NULL;
- }
- return de;
-} /* End Function _devfs_prepare_leaf */
-
-static devfs_handle_t _devfs_walk_path(struct devfs_entry *dir,
- const char *name, int namelen,
- int traverse_symlink)
-{
- int next_pos = 0;
-
- if (dir == NULL)
- dir = _devfs_get_root_entry();
- if (dir == NULL)
- return NULL;
- devfs_get(dir);
- for (; namelen > 0; name += next_pos, namelen -= next_pos) {
- struct devfs_entry *de, *link;
-
- if (!S_ISDIR(dir->mode)) {
- devfs_put(dir);
- return NULL;
- }
-
- if ((de =
- _devfs_descend(dir, name, namelen, &next_pos)) == NULL) {
- devfs_put(dir);
- return NULL;
- }
- if (S_ISLNK(de->mode) && traverse_symlink) { /* Need to follow the link: this is a stack chomper */
- /* FIXME what if it puts outside of mounted tree? */
- link = _devfs_walk_path(dir, de->u.symlink.linkname,
- de->u.symlink.length, TRUE);
- devfs_put(de);
- if (!link) {
- devfs_put(dir);
- return NULL;
- }
- de = link;
- }
- devfs_put(dir);
- dir = de;
- if (name[next_pos] == '/')
- ++next_pos;
- }
- return dir;
-} /* End Function _devfs_walk_path */
-
-/**
- * _devfs_find_entry - 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 may be %NULL.
- * @traverse_symlink: If %TRUE then symbolic links are traversed.
- *
- * Returns the devfs_entry pointer on success, else %NULL. An implicit
- * devfs_get() is performed.
- */
-
-static struct devfs_entry *_devfs_find_entry(devfs_handle_t dir,
- const char *name,
- int traverse_symlink)
-{
- unsigned int namelen = strlen(name);
-
- if (name[0] == '/') {
- /* Skip leading pathname component */
- if (namelen < 2) {
- PRINTK("(%s): too short\n", name);
- return NULL;
- }
- for (++name, --namelen; (*name != '/') && (namelen > 0);
- ++name, --namelen) ;
- if (namelen < 2) {
- PRINTK("(%s): too short\n", name);
- return NULL;
- }
- ++name;
- --namelen;
- }
- return _devfs_walk_path(dir, name, namelen, traverse_symlink);
-} /* End Function _devfs_find_entry */
-
-static struct devfs_entry *get_devfs_entry_from_vfs_inode(struct inode *inode)
-{
- if (inode == NULL)
- return NULL;
- VERIFY_ENTRY((struct devfs_entry *)inode->u.generic_ip);
- return inode->u.generic_ip;
-} /* End Function get_devfs_entry_from_vfs_inode */
-
-/**
- * free_dentry - Free the dentry for a device entry and invalidate inode.
- * @de: The entry.
- *
- * This must only be called after the entry has been unhooked from its
- * parent directory.
- */
-
-static void free_dentry(struct devfs_entry *de)
-{
- struct dentry *dentry = de->inode.dentry;
-
- if (!dentry)
- return;
- spin_lock(&dcache_lock);
- dget_locked(dentry);
- spin_unlock(&dcache_lock);
- /* Forcefully remove the inode */
- if (dentry->d_inode != NULL)
- dentry->d_inode->i_nlink = 0;
- d_drop(dentry);
- dput(dentry);
-} /* End Function free_dentry */
-
-/**
- * is_devfsd_or_child - 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.
- */
-
-static int is_devfsd_or_child(struct fs_info *fs_info)
-{
- struct task_struct *p = current;
-
- if (p == fs_info->devfsd_task)
- return (TRUE);
- if (process_group(p) == fs_info->devfsd_pgrp)
- return (TRUE);
- read_lock(&tasklist_lock);
- for (; p != &init_task; p = p->real_parent) {
- if (p == fs_info->devfsd_task) {
- read_unlock(&tasklist_lock);
- return (TRUE);
- }
- }
- read_unlock(&tasklist_lock);
- return (FALSE);
-} /* End Function is_devfsd_or_child */
-
-/**
- * devfsd_queue_empty - Test if devfsd has work pending in its event queue.
- * @fs_info: The filesystem information.
- *
- * Returns %TRUE if the queue is empty, else %FALSE.
- */
-
-static inline int devfsd_queue_empty(struct fs_info *fs_info)
-{
- return (fs_info->devfsd_last_event) ? FALSE : TRUE;
-} /* End Function devfsd_queue_empty */
-
-/**
- * wait_for_devfsd_finished - 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.
- */
-
-static int wait_for_devfsd_finished(struct fs_info *fs_info)
-{
- 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);
- set_current_state(TASK_UNINTERRUPTIBLE);
- add_wait_queue(&fs_info->revalidate_wait_queue, &wait);
- 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);
- __set_current_state(TASK_RUNNING);
- return (TRUE);
-} /* End Function wait_for_devfsd_finished */
-
-/**
- * devfsd_notify_de - Notify the devfsd daemon of a change.
- * @de: The devfs entry that has changed. This and all parent entries will
- * have their reference counts incremented if the event was queued.
- * @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.
- */
-
-static int devfsd_notify_de(struct devfs_entry *de,
- unsigned short type, umode_t mode,
- uid_t uid, gid_t gid, struct fs_info *fs_info)
-{
- struct devfsd_buf_entry *entry;
- struct devfs_entry *curr;
-
- if (!(fs_info->devfsd_event_mask & (1 << type)))
- return (FALSE);
- if ((entry = kmem_cache_alloc(devfsd_buf_cache, SLAB_KERNEL)) == NULL) {
- atomic_inc(&fs_info->devfsd_overrun_count);
- return (FALSE);
- }
- for (curr = de; curr != NULL; curr = curr->parent)
- devfs_get(curr);
- entry->de = de;
- entry->type = type;
- entry->mode = mode;
- entry->uid = uid;
- entry->gid = gid;
- entry->next = NULL;
- spin_lock(&fs_info->devfsd_buffer_lock);
- if (!fs_info->devfsd_first_event)
- fs_info->devfsd_first_event = entry;
- if (fs_info->devfsd_last_event)
- fs_info->devfsd_last_event->next = entry;
- fs_info->devfsd_last_event = entry;
- spin_unlock(&fs_info->devfsd_buffer_lock);
- wake_up_interruptible(&fs_info->devfsd_wait_queue);
- return (TRUE);
-} /* End Function devfsd_notify_de */
-
-/**
- * devfsd_notify - Notify the devfsd daemon of a change.
- * @de: The devfs entry that has changed.
- * @type: The type of change event.
- * @wait: If TRUE, the function waits for the daemon to finish processing
- * the event.
- */
-
-static void devfsd_notify(struct devfs_entry *de, unsigned short type)
-{
- devfsd_notify_de(de, type, de->mode, current->euid,
- current->egid, &fs_info);
-}
-
-static int devfs_mk_dev(dev_t dev, umode_t mode, const char *fmt, va_list args)
-{
- struct devfs_entry *dir = NULL, *de;
- char buf[64];
- int error, n;
-
- n = vsnprintf(buf, sizeof(buf), fmt, args);
- if (n >= sizeof(buf) || !buf[0]) {
- printk(KERN_WARNING "%s: invalid format string %s\n",
- __FUNCTION__, fmt);
- return -EINVAL;
- }
-
- de = _devfs_prepare_leaf(&dir, buf, mode);
- if (!de) {
- printk(KERN_WARNING "%s: could not prepare leaf for %s\n",
- __FUNCTION__, buf);
- return -ENOMEM; /* could be more accurate... */
- }
-
- de->u.dev = dev;
-
- error = _devfs_append_entry(dir, de, NULL);
- if (error) {
- printk(KERN_WARNING "%s: could not append to parent for %s\n",
- __FUNCTION__, buf);
- goto out;
- }
-
- devfsd_notify(de, DEVFSD_NOTIFY_REGISTERED);
- out:
- devfs_put(dir);
- return error;
-}
-
-int devfs_mk_bdev(dev_t dev, umode_t mode, const char *fmt, ...)
-{
- va_list args;
-
- if (!S_ISBLK(mode)) {
- printk(KERN_WARNING "%s: invalide mode (%u) for %s\n",
- __FUNCTION__, mode, fmt);
- return -EINVAL;
- }
-
- va_start(args, fmt);
- return devfs_mk_dev(dev, mode, fmt, args);
-}
-
-EXPORT_SYMBOL(devfs_mk_bdev);
-
-int devfs_mk_cdev(dev_t dev, umode_t mode, const char *fmt, ...)
-{
- va_list args;
-
- if (!S_ISCHR(mode)) {
- printk(KERN_WARNING "%s: invalide mode (%u) for %s\n",
- __FUNCTION__, mode, fmt);
- return -EINVAL;
- }
-
- va_start(args, fmt);
- return devfs_mk_dev(dev, mode, fmt, args);
-}
-
-EXPORT_SYMBOL(devfs_mk_cdev);
-
-/**
- * _devfs_unhook - Unhook a device entry from its parents list
- * @de: The entry to unhook.
- *
- * Returns %TRUE if the entry was unhooked, else %FALSE if it was
- * previously unhooked.
- * The caller must have a write lock on the parent directory.
- */
-
-static int _devfs_unhook(struct devfs_entry *de)
-{
- struct devfs_entry *parent;
-
- if (!de || (de->prev == de))
- return FALSE;
- parent = de->parent;
- 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;
- de->prev = de; /* Indicate we're unhooked */
- de->next = NULL; /* Force early termination for <devfs_readdir> */
- return TRUE;
-} /* End Function _devfs_unhook */
-
-/**
- * _devfs_unregister - Unregister a device entry from its parent.
- * @dir: The parent directory.
- * @de: The entry to unregister.
- *
- * The caller must have a write lock on the parent directory, which is
- * unlocked by this function.
- */
-
-static void _devfs_unregister(struct devfs_entry *dir, struct devfs_entry *de)
-{
- int unhooked = _devfs_unhook(de);
-
- write_unlock(&dir->u.dir.lock);
- if (!unhooked)
- return;
- devfs_get(dir);
- devfsd_notify(de, DEVFSD_NOTIFY_UNREGISTERED);
- free_dentry(de);
- devfs_put(dir);
- if (!S_ISDIR(de->mode))
- return;
- while (TRUE) { /* Recursively unregister: this is a stack chomper */
- struct devfs_entry *child;
-
- write_lock(&de->u.dir.lock);
- de->u.dir.no_more_additions = TRUE;
- child = de->u.dir.first;
- VERIFY_ENTRY(child);
- _devfs_unregister(de, child);
- if (!child)
- break;
- DPRINTK(DEBUG_UNREGISTER, "(%s): child: %p refcount: %d\n",
- child->name, child, atomic_read(&child->refcount));
- devfs_put(child);
- }
-} /* End Function _devfs_unregister */
-
-static int devfs_do_symlink(devfs_handle_t dir, const char *name,
- const char *link, devfs_handle_t * handle)
-{
- int err;
- unsigned int linklength;
- char *newlink;
- struct devfs_entry *de;
-
- if (handle != NULL)
- *handle = NULL;
- if (name == NULL) {
- PRINTK("(): NULL name pointer\n");
- return -EINVAL;
- }
- if (link == NULL) {
- PRINTK("(%s): NULL link pointer\n", name);
- return -EINVAL;
- }
- linklength = strlen(link);
- if ((newlink = kmalloc(linklength + 1, GFP_KERNEL)) == NULL)
- return -ENOMEM;
- memcpy(newlink, link, linklength);
- newlink[linklength] = '\0';
- if ((de = _devfs_prepare_leaf(&dir, name, S_IFLNK | S_IRUGO | S_IXUGO))
- == NULL) {
- PRINTK("(%s): could not prepare leaf\n", name);
- kfree(newlink);
- return -ENOTDIR;
- }
- de->info = NULL;
- de->u.symlink.linkname = newlink;
- de->u.symlink.length = linklength;
- if ((err = _devfs_append_entry(dir, de, NULL)) != 0) {
- PRINTK("(%s): could not append to parent, err: %d\n", name,
- err);
- devfs_put(dir);
- return err;
- }
- devfs_put(dir);
-#ifdef CONFIG_DEVFS_DEBUG
- spin_lock(&stat_lock);
- stat_num_bytes += linklength + 1;
- spin_unlock(&stat_lock);
-#endif
- if (handle != NULL)
- *handle = de;
- return 0;
-} /* End Function devfs_do_symlink */
-
-/**
- * devfs_mk_symlink Create a symbolic link in the devfs namespace.
- * @from: The name of the entry.
- * @to: Name of the destination
- *
- * Returns 0 on success, else a negative error code is returned.
- */
-
-int devfs_mk_symlink(const char *from, const char *to)
-{
- devfs_handle_t de;
- int err;
-
- err = devfs_do_symlink(NULL, from, to, &de);
- if (!err) {
- de->vfs = TRUE;
- devfsd_notify(de, DEVFSD_NOTIFY_REGISTERED);
- }
-
- return err;
-}
-
-/**
- * devfs_mk_dir - Create a directory in the devfs namespace.
- * new name is relative to the root of the devfs.
- * @fmt: The name of the entry.
- *
- * 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.
- * On failure %NULL is returned.
- */
-
-int devfs_mk_dir(const char *fmt, ...)
-{
- struct devfs_entry *dir = NULL, *de = NULL, *old;
- char buf[64];
- va_list args;
- int error, n;
-
- va_start(args, fmt);
- n = vsnprintf(buf, 64, fmt, args);
- if (n >= 64 || !buf[0]) {
- printk(KERN_WARNING "%s: invalid argument.", __FUNCTION__);
- return -EINVAL;
- }
-
- de = _devfs_prepare_leaf(&dir, buf, MODE_DIR);
- if (!de) {
- PRINTK("(%s): could not prepare leaf\n", buf);
- return -EINVAL;
- }
-
- error = _devfs_append_entry(dir, de, &old);
- if (error == -EEXIST && S_ISDIR(old->mode)) {
- /*
- * devfs_mk_dir() of an already-existing directory will
- * return success.
- */
- error = 0;
- goto out_put;
- } else if (error) {
- PRINTK("(%s): could not append to dir: %p \"%s\"\n",
- buf, dir, dir->name);
- devfs_put(old);
- goto out_put;
- }
-
- devfsd_notify(de, DEVFSD_NOTIFY_REGISTERED);
-
- out_put:
- devfs_put(dir);
- return error;
-}
-
-void devfs_remove(const char *fmt, ...)
-{
- char buf[64];
- va_list args;
- int n;
-
- va_start(args, fmt);
- n = vsnprintf(buf, sizeof(buf), fmt, args);
- if (n < sizeof(buf) && buf[0]) {
- devfs_handle_t de = _devfs_find_entry(NULL, buf, 0);
-
- if (!de) {
- printk(KERN_ERR "%s: %s not found, cannot remove\n",
- __FUNCTION__, buf);
- dump_stack();
- return;
- }
-
- write_lock(&de->parent->u.dir.lock);
- _devfs_unregister(de->parent, de);
- devfs_put(de);
- devfs_put(de);
- }
-}
-
-/**
- * devfs_generate_path - 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.
- */
-
-static int devfs_generate_path(devfs_handle_t de, char *path, int buflen)
-{
- int pos;
-#define NAMEOF(de) ( (de)->mode ? (de)->name : (de)->u.name )
-
- if (de == NULL)
- return -EINVAL;
- VERIFY_ENTRY(de);
- if (de->namelen >= buflen)
- return -ENAMETOOLONG; /* Must be first */
- path[buflen - 1] = '\0';
- if (de->parent == NULL)
- return buflen - 1; /* Don't prepend root */
- pos = buflen - de->namelen - 1;
- memcpy(path + pos, NAMEOF(de), de->namelen);
- 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, NAMEOF(de), de->namelen);
- }
- return pos;
-} /* End Function devfs_generate_path */
-
-/**
- * devfs_setup - Process kernel boot options.
- * @str: The boot options after the "devfs=".
- */
-
-static int __init devfs_setup(char *str)
-{
- static struct {
- char *name;
- unsigned int mask;
- unsigned int *opt;
- } devfs_options_tab[] __initdata = {
-#ifdef CONFIG_DEVFS_DEBUG
- {
- "dall", DEBUG_ALL, &devfs_debug_init}, {
- "dmod", DEBUG_MODULE_LOAD, &devfs_debug_init}, {
- "dreg", DEBUG_REGISTER, &devfs_debug_init}, {
- "dunreg", DEBUG_UNREGISTER, &devfs_debug_init}, {
- "dfree", DEBUG_FREE, &devfs_debug_init}, {
- "diget", DEBUG_I_GET, &devfs_debug_init}, {
- "dchange", DEBUG_SET_FLAGS, &devfs_debug_init}, {
- "dsread", DEBUG_S_READ, &devfs_debug_init}, {
- "dichange", DEBUG_I_CHANGE, &devfs_debug_init}, {
- "dimknod", DEBUG_I_MKNOD, &devfs_debug_init}, {
- "dilookup", DEBUG_I_LOOKUP, &devfs_debug_init}, {
- "diunlink", DEBUG_I_UNLINK, &devfs_debug_init},
-#endif /* CONFIG_DEVFS_DEBUG */
- {
- "mount", OPTION_MOUNT, &boot_options}, {
- NULL, 0, NULL}
- };
-
- while ((*str != '\0') && !isspace(*str)) {
- int i, found = 0, invert = 0;
-
- if (strncmp(str, "no", 2) == 0) {
- invert = 1;
- str += 2;
- }
- for (i = 0; devfs_options_tab[i].name != NULL; i++) {
- int len = strlen(devfs_options_tab[i].name);
-
- if (strncmp(str, devfs_options_tab[i].name, len) == 0) {
- if (invert)
- *devfs_options_tab[i].opt &=
- ~devfs_options_tab[i].mask;
- else
- *devfs_options_tab[i].opt |=
- devfs_options_tab[i].mask;
- str += len;
- found = 1;
- break;
- }
- }
- if (!found)
- return 0; /* No match */
- if (*str != ',')
- return 0; /* No more options */
- ++str;
- }
- return 1;
-} /* End Function devfs_setup */
-
-__setup("devfs=", devfs_setup);
-
-EXPORT_SYMBOL(devfs_mk_dir);
-EXPORT_SYMBOL(devfs_remove);
-
-/**
- * try_modload - Notify devfsd of an inode lookup by a non-devfsd process.
- * @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 (event was queued), else a negative error code.
- */
-
-static int try_modload(struct devfs_entry *parent, struct fs_info *fs_info,
- const char *name, unsigned namelen,
- struct devfs_entry *buf)
-{
- if (!(fs_info->devfsd_event_mask & (1 << DEVFSD_NOTIFY_LOOKUP)))
- return -ENOENT;
- if (is_devfsd_or_child(fs_info))
- return -ENOENT;
- memset(buf, 0, sizeof *buf);
- atomic_set(&buf->refcount, 1);
- buf->parent = parent;
- buf->namelen = namelen;
- buf->u.name = name;
- WRITE_ENTRY_MAGIC(buf, MAGIC_VALUE);
- if (!devfsd_notify_de(buf, DEVFSD_NOTIFY_LOOKUP, 0,
- current->euid, current->egid, fs_info))
- return -ENOENT;
- /* Possible success: event has been queued */
- return 0;
-} /* End Function try_modload */
-
-/* Superblock operations follow */
-
-static struct inode_operations devfs_iops;
-static struct inode_operations devfs_dir_iops;
-static const struct file_operations devfs_fops;
-static const struct file_operations devfs_dir_fops;
-static struct inode_operations devfs_symlink_iops;
-
-static int devfs_notify_change(struct dentry *dentry, struct iattr *iattr)
-{
- int retval;
- struct devfs_entry *de;
- struct inode *inode = dentry->d_inode;
- struct fs_info *fs_info = inode->i_sb->s_fs_info;
-
- de = get_devfs_entry_from_vfs_inode(inode);
- if (de == NULL)
- return -ENODEV;
- retval = inode_change_ok(inode, iattr);
- if (retval != 0)
- return retval;
- retval = inode_setattr(inode, iattr);
- if (retval != 0)
- return retval;
- DPRINTK(DEBUG_I_CHANGE, "(%d): VFS inode: %p devfs_entry: %p\n",
- (int)inode->i_ino, inode, de);
- DPRINTK(DEBUG_I_CHANGE, "(): mode: 0%o uid: %d gid: %d\n",
- (int)inode->i_mode, (int)inode->i_uid, (int)inode->i_gid);
- /* Inode is not on hash chains, thus must save permissions here rather
- than in a write_inode() method */
- de->mode = inode->i_mode;
- de->inode.uid = inode->i_uid;
- de->inode.gid = inode->i_gid;
- de->inode.atime = inode->i_atime;
- de->inode.mtime = inode->i_mtime;
- de->inode.ctime = inode->i_ctime;
- if ((iattr->ia_valid & (ATTR_MODE | ATTR_UID | ATTR_GID)) &&
- !is_devfsd_or_child(fs_info))
- devfsd_notify_de(de, DEVFSD_NOTIFY_CHANGE, inode->i_mode,
- inode->i_uid, inode->i_gid, fs_info);
- return 0;
-} /* End Function devfs_notify_change */
-
-static struct super_operations devfs_sops = {
- .drop_inode = generic_delete_inode,
- .statfs = simple_statfs,
-};
-
-/**
- * _devfs_get_vfs_inode - Get a VFS inode.
- * @sb: The super block.
- * @de: The devfs inode.
- * @dentry: The dentry to register with the devfs inode.
- *
- * Returns the inode on success, else %NULL. An implicit devfs_get() is
- * performed if the inode is created.
- */
-
-static struct inode *_devfs_get_vfs_inode(struct super_block *sb,
- struct devfs_entry *de,
- struct dentry *dentry)
-{
- struct inode *inode;
-
- if (de->prev == de)
- return NULL; /* Quick check to see if unhooked */
- if ((inode = new_inode(sb)) == NULL) {
- PRINTK("(%s): new_inode() failed, de: %p\n", de->name, de);
- return NULL;
- }
- if (de->parent) {
- read_lock(&de->parent->u.dir.lock);
- if (de->prev != de)
- de->inode.dentry = dentry; /* Not unhooked */
- read_unlock(&de->parent->u.dir.lock);
- } else
- de->inode.dentry = dentry; /* Root: no locking needed */
- if (de->inode.dentry != dentry) { /* Must have been unhooked */
- iput(inode);
- return NULL;
- }
- /* FIXME where is devfs_put? */
- inode->u.generic_ip = devfs_get(de);
- inode->i_ino = de->inode.ino;
- DPRINTK(DEBUG_I_GET, "(%d): VFS inode: %p devfs_entry: %p\n",
- (int)inode->i_ino, inode, de);
- inode->i_blocks = 0;
- inode->i_blksize = FAKE_BLOCK_SIZE;
- inode->i_op = &devfs_iops;
- inode->i_mode = de->mode;
- if (S_ISDIR(de->mode)) {
- inode->i_op = &devfs_dir_iops;
- inode->i_fop = &devfs_dir_fops;
- } else if (S_ISLNK(de->mode)) {
- inode->i_op = &devfs_symlink_iops;
- inode->i_size = de->u.symlink.length;
- } else if (S_ISCHR(de->mode) || S_ISBLK(de->mode)) {
- init_special_inode(inode, de->mode, de->u.dev);
- } else if (S_ISFIFO(de->mode) || S_ISSOCK(de->mode)) {
- init_special_inode(inode, de->mode, 0);
- } else {
- PRINTK("(%s): unknown mode %o de: %p\n",
- de->name, de->mode, de);
- iput(inode);
- devfs_put(de);
- return NULL;
- }
-
- inode->i_uid = de->inode.uid;
- inode->i_gid = de->inode.gid;
- inode->i_atime = de->inode.atime;
- inode->i_mtime = de->inode.mtime;
- inode->i_ctime = de->inode.ctime;
- DPRINTK(DEBUG_I_GET, "(): mode: 0%o uid: %d gid: %d\n",
- (int)inode->i_mode, (int)inode->i_uid, (int)inode->i_gid);
- return inode;
-} /* End Function _devfs_get_vfs_inode */
-
-/* File operations for device entries follow */
-
-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_entry *parent, *de, *next = NULL;
- struct inode *inode = file->f_dentry->d_inode;
-
- fs_info = inode->i_sb->s_fs_info;
- parent = get_devfs_entry_from_vfs_inode(file->f_dentry->d_inode);
- if ((long)file->f_pos < 0)
- return -EINVAL;
- DPRINTK(DEBUG_F_READDIR, "(%s): fs_info: %p pos: %ld\n",
- parent->name, fs_info, (long)file->f_pos);
- switch ((long)file->f_pos) {
- case 0:
- err = (*filldir) (dirent, "..", 2, file->f_pos,
- parent_ino(file->f_dentry), DT_DIR);
- 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,
- DT_DIR);
- if (err == -EINVAL)
- break;
- if (err < 0)
- return err;
- file->f_pos++;
- ++stored;
- /* Fall through */
- default:
- /* Skip entries */
- count = file->f_pos - 2;
- read_lock(&parent->u.dir.lock);
- for (de = parent->u.dir.first; de && (count > 0); de = de->next)
- --count;
- devfs_get(de);
- read_unlock(&parent->u.dir.lock);
- /* Now add all remaining entries */
- while (de) {
- err = (*filldir) (dirent, de->name, de->namelen,
- file->f_pos, de->inode.ino,
- de->mode >> 12);
- if (err < 0)
- devfs_put(de);
- else {
- file->f_pos++;
- ++stored;
- }
- if (err == -EINVAL)
- break;
- if (err < 0)
- return err;
- read_lock(&parent->u.dir.lock);
- next = devfs_get(de->next);
- read_unlock(&parent->u.dir.lock);
- devfs_put(de);
- de = next;
- }
- break;
- }
- return stored;
-} /* End Function devfs_readdir */
-
-/* Open devfs specific special files */
-static int devfs_open(struct inode *inode, struct file *file)
-{
- int err;
- int minor = MINOR(inode->i_rdev);
- struct file_operations *old_fops, *new_fops;
-
- switch (minor) {
- case 0: /* /dev/.devfsd */
- new_fops = fops_get(&devfsd_fops);
- break;
-#ifdef CONFIG_DEVFS_DEBUG
- case 1: /* /dev/.stat */
- new_fops = fops_get(&stat_fops);
- break;
-#endif
- default:
- return -ENODEV;
- }
-
- if (new_fops == NULL)
- return -ENODEV;
- old_fops = file->f_op;
- file->f_op = new_fops;
- err = new_fops->open ? new_fops->open(inode, file) : 0;
- if (err) {
- file->f_op = old_fops;
- fops_put(new_fops);
- } else
- fops_put(old_fops);
- return err;
-} /* End Function devfs_open */
-
-static const struct file_operations devfs_fops = {
- .open = devfs_open,
-};
-
-static const struct file_operations devfs_dir_fops = {
- .read = generic_read_dir,
- .readdir = devfs_readdir,
-};
-
-/* Dentry operations for device entries follow */
-
-/**
- * devfs_d_release - Callback for when a dentry is freed.
- * @dentry: The dentry.
- */
-
-static void devfs_d_release(struct dentry *dentry)
-{
- DPRINTK(DEBUG_D_RELEASE, "(%p): inode: %p\n", dentry, dentry->d_inode);
-} /* End Function devfs_d_release */
-
-/**
- * devfs_d_iput - Callback for when a dentry loses its inode.
- * @dentry: The dentry.
- * @inode: The inode.
- */
-
-static void devfs_d_iput(struct dentry *dentry, struct inode *inode)
-{
- struct devfs_entry *de;
-
- de = get_devfs_entry_from_vfs_inode(inode);
- DPRINTK(DEBUG_D_IPUT,
- "(%s): dentry: %p inode: %p de: %p de->dentry: %p\n", de->name,
- dentry, inode, de, de->inode.dentry);
- if (de->inode.dentry && (de->inode.dentry != dentry))
- OOPS("(%s): de: %p dentry: %p de->dentry: %p\n",
- de->name, de, dentry, de->inode.dentry);
- de->inode.dentry = NULL;
- iput(inode);
- devfs_put(de);
-} /* End Function devfs_d_iput */
-
-static int 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, struct nameidata *);
-
-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,
-};
-
-/**
- * devfs_d_delete - Callback for when all files for a dentry are closed.
- * @dentry: The dentry.
- */
-
-static int devfs_d_delete(struct dentry *dentry)
-{
- struct inode *inode = dentry->d_inode;
-
- if (dentry->d_op == &devfs_wait_dops)
- dentry->d_op = &devfs_dops;
- /* Unhash dentry if negative (has no inode) */
- if (inode == NULL) {
- DPRINTK(DEBUG_D_DELETE, "(%p): dropping negative dentry\n",
- dentry);
- return 1;
- }
- return 0;
-} /* End Function devfs_d_delete */
-
-struct devfs_lookup_struct {
- devfs_handle_t de;
- wait_queue_head_t wait_queue;
-};
-
-/* XXX: this doesn't handle the case where we got a negative dentry
- but a devfs entry has been registered in the meanwhile */
-static int devfs_d_revalidate_wait(struct dentry *dentry, struct nameidata *nd)
-{
- struct inode *dir = dentry->d_parent->d_inode;
- struct fs_info *fs_info = dir->i_sb->s_fs_info;
- devfs_handle_t parent = get_devfs_entry_from_vfs_inode(dir);
- struct devfs_lookup_struct *lookup_info = dentry->d_fsdata;
- DECLARE_WAITQUEUE(wait, current);
- int need_lock;
-
- /*
- * FIXME HACK
- *
- * make sure that
- * d_instantiate always runs under lock
- * we release i_mutex lock before going to sleep
- *
- * unfortunately sometimes d_revalidate is called with
- * and sometimes without i_mutex lock held. The following checks
- * attempt to deduce when we need to add (and drop resp.) lock
- * here. This relies on current (2.6.2) calling coventions:
- *
- * lookup_hash is always run under i_mutex and is passing NULL
- * as nd
- *
- * open(...,O_CREATE,...) calls _lookup_hash under i_mutex
- * and sets flags to LOOKUP_OPEN|LOOKUP_CREATE
- *
- * all other invocations of ->d_revalidate seem to happen
- * outside of i_mutex
- */
- need_lock = nd &&
- (!(nd->flags & LOOKUP_CREATE) || (nd->flags & LOOKUP_PARENT));
-
- if (need_lock)
- mutex_lock(&dir->i_mutex);
-
- if (is_devfsd_or_child(fs_info)) {
- devfs_handle_t de = lookup_info->de;
- struct inode *inode;
-
- DPRINTK(DEBUG_I_LOOKUP,
- "(%s): dentry: %p inode: %p de: %p by: \"%s\"\n",
- dentry->d_name.name, dentry, dentry->d_inode, de,
- current->comm);
- if (dentry->d_inode)
- goto out;
- if (de == NULL) {
- read_lock(&parent->u.dir.lock);
- de = _devfs_search_dir(parent, dentry->d_name.name,
- dentry->d_name.len);
- read_unlock(&parent->u.dir.lock);
- if (de == NULL)
- goto out;
- lookup_info->de = de;
- }
- /* Create an inode, now that the driver information is available */
- inode = _devfs_get_vfs_inode(dir->i_sb, de, dentry);
- if (!inode)
- goto out;
- DPRINTK(DEBUG_I_LOOKUP,
- "(%s): new VFS inode(%u): %p de: %p by: \"%s\"\n",
- de->name, de->inode.ino, inode, de, current->comm);
- d_instantiate(dentry, inode);
- goto out;
- }
- if (lookup_info == NULL)
- goto out; /* Early termination */
- read_lock(&parent->u.dir.lock);
- if (dentry->d_fsdata) {
- set_current_state(TASK_UNINTERRUPTIBLE);
- add_wait_queue(&lookup_info->wait_queue, &wait);
- read_unlock(&parent->u.dir.lock);
- /* at this point it is always (hopefully) locked */
- mutex_unlock(&dir->i_mutex);
- schedule();
- mutex_lock(&dir->i_mutex);
- /*
- * This does not need nor should remove wait from wait_queue.
- * Wait queue head is never reused - nothing is ever added to it
- * after all waiters have been waked up and head itself disappears
- * very soon after it. Moreover it is local variable on stack that
- * is likely to have already disappeared so any reference to it
- * at this point is buggy.
- */
-
- } else
- read_unlock(&parent->u.dir.lock);
-
- out:
- if (need_lock)
- mutex_unlock(&dir->i_mutex);
- 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 nameidata *nd)
-{
- struct devfs_entry tmp; /* Must stay in scope until devfsd idle again */
- struct devfs_lookup_struct lookup_info;
- struct fs_info *fs_info = dir->i_sb->s_fs_info;
- struct devfs_entry *parent, *de;
- struct inode *inode;
- struct dentry *retval = NULL;
-
- /* Set up the dentry operations before anything else, to ensure cleaning
- up on any error */
- dentry->d_op = &devfs_dops;
- /* First try to get the devfs entry for this directory */
- parent = get_devfs_entry_from_vfs_inode(dir);
- DPRINTK(DEBUG_I_LOOKUP, "(%s): dentry: %p parent: %p by: \"%s\"\n",
- dentry->d_name.name, dentry, parent, current->comm);
- if (parent == NULL)
- return ERR_PTR(-ENOENT);
- read_lock(&parent->u.dir.lock);
- de = _devfs_search_dir(parent, dentry->d_name.name, dentry->d_name.len);
- read_unlock(&parent->u.dir.lock);
- lookup_info.de = de;
- init_waitqueue_head(&lookup_info.wait_queue);
- dentry->d_fsdata = &lookup_info;
- if (de == NULL) { /* 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, &tmp) < 0) { /* Lookup event was not queued to devfsd */
- d_add(dentry, NULL);
- return NULL;
- }
- }
- dentry->d_op = &devfs_wait_dops;
- 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 */
- mutex_unlock(&dir->i_mutex);
- wait_for_devfsd_finished(fs_info); /* If I'm not devfsd, must wait */
- mutex_lock(&dir->i_mutex); /* Grab it again because them's the rules */
- de = lookup_info.de;
- /* If someone else has been so kind as to make the inode, we go home
- early */
- if (dentry->d_inode)
- goto out;
- if (de == NULL) {
- read_lock(&parent->u.dir.lock);
- de = _devfs_search_dir(parent, dentry->d_name.name,
- dentry->d_name.len);
- read_unlock(&parent->u.dir.lock);
- if (de == NULL)
- goto out;
- /* OK, there's an entry now, but no VFS inode yet */
- }
- /* Create an inode, now that the driver information is available */
- inode = _devfs_get_vfs_inode(dir->i_sb, de, dentry);
- if (!inode) {
- retval = ERR_PTR(-ENOMEM);
- goto out;
- }
- DPRINTK(DEBUG_I_LOOKUP,
- "(%s): new VFS inode(%u): %p de: %p by: \"%s\"\n", de->name,
- de->inode.ino, inode, de, current->comm);
- d_instantiate(dentry, inode);
- out:
- write_lock(&parent->u.dir.lock);
- dentry->d_op = &devfs_dops;
- dentry->d_fsdata = NULL;
- wake_up(&lookup_info.wait_queue);
- write_unlock(&parent->u.dir.lock);
- devfs_put(de);
- return retval;
-} /* End Function devfs_lookup */
-
-static int devfs_unlink(struct inode *dir, struct dentry *dentry)
-{
- int unhooked;
- struct devfs_entry *de;
- struct inode *inode = dentry->d_inode;
- struct fs_info *fs_info = dir->i_sb->s_fs_info;
-
- de = get_devfs_entry_from_vfs_inode(inode);
- DPRINTK(DEBUG_I_UNLINK, "(%s): de: %p\n", dentry->d_name.name, de);
- if (de == NULL)
- return -ENOENT;
- if (!de->vfs)
- return -EPERM;
- write_lock(&de->parent->u.dir.lock);
- unhooked = _devfs_unhook(de);
- write_unlock(&de->parent->u.dir.lock);
- if (!unhooked)
- return -ENOENT;
- if (!is_devfsd_or_child(fs_info))
- devfsd_notify_de(de, DEVFSD_NOTIFY_DELETE, inode->i_mode,
- inode->i_uid, inode->i_gid, fs_info);
- free_dentry(de);
- devfs_put(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 = dir->i_sb->s_fs_info;
- struct devfs_entry *parent, *de;
- struct inode *inode;
-
- /* First try to get the devfs entry for this directory */
- parent = get_devfs_entry_from_vfs_inode(dir);
- if (parent == NULL)
- return -ENOENT;
- err = devfs_do_symlink(parent, dentry->d_name.name, symname, &de);
- DPRINTK(DEBUG_DISABLED, "(%s): errcode from <devfs_do_symlink>: %d\n",
- dentry->d_name.name, err);
- if (err < 0)
- return err;
- de->vfs = TRUE;
- de->inode.uid = current->euid;
- de->inode.gid = current->egid;
- de->inode.atime = CURRENT_TIME;
- de->inode.mtime = CURRENT_TIME;
- de->inode.ctime = CURRENT_TIME;
- if ((inode = _devfs_get_vfs_inode(dir->i_sb, de, dentry)) == NULL)
- return -ENOMEM;
- DPRINTK(DEBUG_DISABLED, "(%s): new VFS inode(%u): %p dentry: %p\n",
- dentry->d_name.name, de->inode.ino, inode, dentry);
- d_instantiate(dentry, inode);
- if (!is_devfsd_or_child(fs_info))
- devfsd_notify_de(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 err;
- struct fs_info *fs_info = dir->i_sb->s_fs_info;
- struct devfs_entry *parent, *de;
- struct inode *inode;
-
- mode = (mode & ~S_IFMT) | S_IFDIR; /* VFS doesn't pass S_IFMT part */
- parent = get_devfs_entry_from_vfs_inode(dir);
- if (parent == NULL)
- return -ENOENT;
- de = _devfs_alloc_entry(dentry->d_name.name, dentry->d_name.len, mode);
- if (!de)
- return -ENOMEM;
- de->vfs = TRUE;
- if ((err = _devfs_append_entry(parent, de, NULL)) != 0)
- return err;
- de->inode.uid = current->euid;
- de->inode.gid = current->egid;
- de->inode.atime = CURRENT_TIME;
- de->inode.mtime = CURRENT_TIME;
- de->inode.ctime = CURRENT_TIME;
- if ((inode = _devfs_get_vfs_inode(dir->i_sb, de, dentry)) == NULL)
- return -ENOMEM;
- DPRINTK(DEBUG_DISABLED, "(%s): new VFS inode(%u): %p dentry: %p\n",
- dentry->d_name.name, de->inode.ino, inode, dentry);
- d_instantiate(dentry, inode);
- if (!is_devfsd_or_child(fs_info))
- devfsd_notify_de(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 err = 0;
- struct devfs_entry *de;
- struct fs_info *fs_info = dir->i_sb->s_fs_info;
- struct inode *inode = dentry->d_inode;
-
- if (dir->i_sb->s_fs_info != inode->i_sb->s_fs_info)
- return -EINVAL;
- de = get_devfs_entry_from_vfs_inode(inode);
- if (de == NULL)
- return -ENOENT;
- if (!S_ISDIR(de->mode))
- return -ENOTDIR;
- if (!de->vfs)
- return -EPERM;
- /* First ensure the directory is empty and will stay that way */
- write_lock(&de->u.dir.lock);
- if (de->u.dir.first)
- err = -ENOTEMPTY;
- else
- de->u.dir.no_more_additions = TRUE;
- write_unlock(&de->u.dir.lock);
- if (err)
- return err;
- /* Now unhook the directory from its parent */
- write_lock(&de->parent->u.dir.lock);
- if (!_devfs_unhook(de))
- err = -ENOENT;
- write_unlock(&de->parent->u.dir.lock);
- if (err)
- return err;
- if (!is_devfsd_or_child(fs_info))
- devfsd_notify_de(de, DEVFSD_NOTIFY_DELETE, inode->i_mode,
- inode->i_uid, inode->i_gid, fs_info);
- free_dentry(de);
- devfs_put(de);
- return 0;
-} /* End Function devfs_rmdir */
-
-static int devfs_mknod(struct inode *dir, struct dentry *dentry, int mode,
- dev_t rdev)
-{
- int err;
- struct fs_info *fs_info = dir->i_sb->s_fs_info;
- struct devfs_entry *parent, *de;
- struct inode *inode;
-
- DPRINTK(DEBUG_I_MKNOD, "(%s): mode: 0%o dev: %u:%u\n",
- dentry->d_name.name, mode, MAJOR(rdev), MINOR(rdev));
- parent = get_devfs_entry_from_vfs_inode(dir);
- if (parent == NULL)
- return -ENOENT;
- de = _devfs_alloc_entry(dentry->d_name.name, dentry->d_name.len, mode);
- if (!de)
- return -ENOMEM;
- de->vfs = TRUE;
- if (S_ISCHR(mode) || S_ISBLK(mode))
- de->u.dev = rdev;
- if ((err = _devfs_append_entry(parent, de, NULL)) != 0)
- return err;
- de->inode.uid = current->euid;
- de->inode.gid = current->egid;
- de->inode.atime = CURRENT_TIME;
- de->inode.mtime = CURRENT_TIME;
- de->inode.ctime = CURRENT_TIME;
- if ((inode = _devfs_get_vfs_inode(dir->i_sb, de, dentry)) == NULL)
- return -ENOMEM;
- DPRINTK(DEBUG_I_MKNOD, ": new VFS inode(%u): %p dentry: %p\n",
- de->inode.ino, inode, dentry);
- d_instantiate(dentry, inode);
- if (!is_devfsd_or_child(fs_info))
- devfsd_notify_de(de, DEVFSD_NOTIFY_CREATE, inode->i_mode,
- inode->i_uid, inode->i_gid, fs_info);
- return 0;
-} /* End Function devfs_mknod */
-
-static void *devfs_follow_link(struct dentry *dentry, struct nameidata *nd)
-{
- struct devfs_entry *p = get_devfs_entry_from_vfs_inode(dentry->d_inode);
- nd_set_link(nd, p ? p->u.symlink.linkname : ERR_PTR(-ENODEV));
- return NULL;
-} /* End Function devfs_follow_link */
-
-static struct inode_operations devfs_iops = {
- .setattr = devfs_notify_change,
-};
-
-static struct inode_operations devfs_dir_iops = {
- .lookup = devfs_lookup,
- .unlink = devfs_unlink,
- .symlink = devfs_symlink,
- .mkdir = devfs_mkdir,
- .rmdir = devfs_rmdir,
- .mknod = devfs_mknod,
- .setattr = devfs_notify_change,
-};
-
-static struct inode_operations devfs_symlink_iops = {
- .readlink = generic_readlink,
- .follow_link = devfs_follow_link,
- .setattr = devfs_notify_change,
-};
-
-static int devfs_fill_super(struct super_block *sb, void *data, int silent)
-{
- struct inode *root_inode = NULL;
-
- if (_devfs_get_root_entry() == NULL)
- goto out_no_root;
- 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.sb = sb;
- sb->s_fs_info = &fs_info;
- sb->s_blocksize = 1024;
- sb->s_blocksize_bits = 10;
- sb->s_magic = DEVFS_SUPER_MAGIC;
- sb->s_op = &devfs_sops;
- sb->s_time_gran = 1;
- if ((root_inode = _devfs_get_vfs_inode(sb, root_entry, NULL)) == NULL)
- goto out_no_root;
- sb->s_root = d_alloc_root(root_inode);
- if (!sb->s_root)
- goto out_no_root;
- DPRINTK(DEBUG_S_READ, "(): made devfs ptr: %p\n", sb->s_fs_info);
- return 0;
-
- out_no_root:
- PRINTK("(): get root inode failed\n");
- if (root_inode)
- iput(root_inode);
- return -EINVAL;
-} /* End Function devfs_fill_super */
-
-static int devfs_get_sb(struct file_system_type *fs_type,
- int flags, const char *dev_name,
- void *data, struct vfsmount *mnt)
-{
- return get_sb_single(fs_type, flags, data, devfs_fill_super, mnt);
-}
-
-static struct file_system_type devfs_fs_type = {
- .name = DEVFS_NAME,
- .get_sb = devfs_get_sb,
- .kill_sb = kill_anon_super,
-};
-
-/* File operations for devfsd follow */
-
-static ssize_t devfsd_read(struct file *file, char __user *buf, size_t len,
- loff_t * ppos)
-{
- int done = FALSE;
- int ival;
- loff_t pos, devname_offset, tlen, rpos;
- devfs_handle_t de;
- struct devfsd_buf_entry *entry;
- struct fs_info *fs_info = file->f_dentry->d_inode->i_sb->s_fs_info;
- struct devfsd_notify_struct *info = fs_info->devfsd_info;
- DECLARE_WAITQUEUE(wait, current);
-
- /* 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 */
- set_current_state(TASK_INTERRUPTIBLE);
- add_wait_queue(&fs_info->devfsd_wait_queue, &wait);
- 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);
- __set_current_state(TASK_RUNNING);
- return -EINTR;
- }
- set_current_state(TASK_INTERRUPTIBLE);
- }
- remove_wait_queue(&fs_info->devfsd_wait_queue, &wait);
- __set_current_state(TASK_RUNNING);
- /* Now play with the data */
- ival = atomic_read(&fs_info->devfsd_overrun_count);
- info->overrun_count = ival;
- entry = fs_info->devfsd_first_event;
- info->type = entry->type;
- info->mode = entry->mode;
- info->uid = entry->uid;
- info->gid = entry->gid;
- de = entry->de;
- if (S_ISCHR(de->mode) || S_ISBLK(de->mode)) {
- info->major = MAJOR(de->u.dev);
- info->minor = MINOR(de->u.dev);
- }
- 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) {
- devfs_handle_t parent;
-
- spin_lock(&fs_info->devfsd_buffer_lock);
- fs_info->devfsd_first_event = entry->next;
- if (entry->next == NULL)
- fs_info->devfsd_last_event = NULL;
- spin_unlock(&fs_info->devfsd_buffer_lock);
- for (; de != NULL; de = parent) {
- parent = de->parent;
- devfs_put(de);
- }
- kmem_cache_free(devfsd_buf_cache, entry);
- if (ival > 0)
- atomic_sub(ival, &fs_info->devfsd_overrun_count);
- *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->s_fs_info;
-
- switch (cmd) {
- case DEVFSDIOC_GET_PROTO_REV:
- ival = DEVFSD_PROTOCOL_REVISION_KERNEL;
- if (copy_to_user((void __user *)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) {
- static DEFINE_SPINLOCK(lock);
-
- if (!spin_trylock(&lock))
- return -EBUSY;
- if (fs_info->devfsd_task != NULL) { /* We lost the race... */
- spin_unlock(&lock);
- return -EBUSY;
- }
- fs_info->devfsd_task = current;
- spin_unlock(&lock);
- fs_info->devfsd_pgrp =
- (process_group(current) ==
- current->pid) ? process_group(current) : 0;
- fs_info->devfsd_file = file;
- fs_info->devfsd_info =
- kmalloc(sizeof *fs_info->devfsd_info, GFP_KERNEL);
- if (!fs_info->devfsd_info) {
- devfsd_close(inode, file);
- return -ENOMEM;
- }
- } else if (fs_info->devfsd_task != current)
- return -EBUSY;
- 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 __user *)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 devfsd_buf_entry *entry, *next;
- struct fs_info *fs_info = inode->i_sb->s_fs_info;
-
- if (fs_info->devfsd_file != file)
- return 0;
- fs_info->devfsd_event_mask = 0;
- fs_info->devfsd_file = NULL;
- spin_lock(&fs_info->devfsd_buffer_lock);
- entry = fs_info->devfsd_first_event;
- fs_info->devfsd_first_event = NULL;
- fs_info->devfsd_last_event = NULL;
- kfree(fs_info->devfsd_info);
- fs_info->devfsd_info = NULL;
- spin_unlock(&fs_info->devfsd_buffer_lock);
- fs_info->devfsd_pgrp = 0;
- fs_info->devfsd_task = NULL;
- wake_up(&fs_info->revalidate_wait_queue);
- for (; entry; entry = next) {
- next = entry->next;
- kmem_cache_free(devfsd_buf_cache, entry);
- }
- return 0;
-} /* End Function devfsd_close */
-
-#ifdef CONFIG_DEVFS_DEBUG
-static ssize_t stat_read(struct file *file, char __user *buf, size_t len,
- loff_t * ppos)
-{
- ssize_t num;
- char txt[80];
-
- num = sprintf(txt, "Number of entries: %u number of bytes: %u\n",
- stat_num_entries, stat_num_bytes) + 1;
- if (*ppos >= num)
- return 0;
- if (*ppos + len > num)
- len = num - *ppos;
- if (copy_to_user(buf, txt + *ppos, len))
- return -EFAULT;
- *ppos += len;
- return len;
-} /* End Function stat_read */
-#endif
-
-static int __init init_devfs_fs(void)
-{
- int err;
- int major;
- struct devfs_entry *devfsd;
-#ifdef CONFIG_DEVFS_DEBUG
- struct devfs_entry *stat;
-#endif
-
- if (_devfs_get_root_entry() == NULL)
- return -ENOMEM;
-
- printk(KERN_INFO "%s: %s Richard Gooch (rgooch@atnf.csiro.au)\n",
- DEVFS_NAME, DEVFS_VERSION);
- devfsd_buf_cache = kmem_cache_create("devfsd_event",
- sizeof(struct devfsd_buf_entry),
- 0, 0, NULL, NULL);
- if (!devfsd_buf_cache)
- OOPS("(): unable to allocate event slab\n");
-#ifdef CONFIG_DEVFS_DEBUG
- devfs_debug = devfs_debug_init;
- printk(KERN_INFO "%s: devfs_debug: 0x%0x\n", DEVFS_NAME, devfs_debug);
-#endif
- printk(KERN_INFO "%s: boot_options: 0x%0x\n", DEVFS_NAME, boot_options);
-
- /* register special device for devfsd communication */
- major = register_chrdev(0, "devfs", &devfs_fops);
- if (major < 0)
- return major;
-
- /* And create the entry for ".devfsd" */
- devfsd = _devfs_alloc_entry(".devfsd", 0, S_IFCHR | S_IRUSR | S_IWUSR);
- if (devfsd == NULL)
- return -ENOMEM;
- devfsd->u.dev = MKDEV(major, 0);
- _devfs_append_entry(root_entry, devfsd, NULL);
-
-#ifdef CONFIG_DEVFS_DEBUG
- stat = _devfs_alloc_entry(".stat", 0, S_IFCHR | S_IRUGO);
- if (stat == NULL)
- return -ENOMEM;
- stat->u.dev = MKDEV(major, 1);
- _devfs_append_entry(root_entry, stat, NULL);
-#endif
-
- err = register_filesystem(&devfs_fs_type);
- return err;
-} /* End Function init_devfs_fs */
-
-void __init mount_devfs_fs(void)
-{
- int err;
-
- if (!(boot_options & OPTION_MOUNT))
- return;
- err = do_mount("none", "/dev", "devfs", 0, NULL);
- if (err == 0)
- printk(KERN_INFO "Mounted devfs on /dev\n");
- else
- PRINTK("(): unable to mount devfs, err: %d\n", err);
-} /* End Function mount_devfs_fs */
-
-module_init(init_devfs_fs)
diff --git a/fs/devfs/util.c b/fs/devfs/util.c
deleted file mode 100644
index db06d388c9ac..000000000000
--- a/fs/devfs/util.c
+++ /dev/null
@@ -1,97 +0,0 @@
-/* devfs (Device FileSystem) utilities.
-
- Copyright (C) 1999-2002 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 *.
- 20000621 Richard Gooch <rgooch@atnf.csiro.au>
- Changed interface to <devfs_register_series>.
- 20000622 Richard Gooch <rgooch@atnf.csiro.au>
- Took account of interface change to <devfs_mk_symlink>.
- Took account of interface change to <devfs_mk_dir>.
- 20010519 Richard Gooch <rgooch@atnf.csiro.au>
- Documentation cleanup.
- 20010709 Richard Gooch <rgooch@atnf.csiro.au>
- Created <devfs_*alloc_major> and <devfs_*alloc_devnum>.
- 20010710 Richard Gooch <rgooch@atnf.csiro.au>
- Created <devfs_*alloc_unique_number>.
- 20010730 Richard Gooch <rgooch@atnf.csiro.au>
- Documentation typo fix.
- 20010806 Richard Gooch <rgooch@atnf.csiro.au>
- Made <block_semaphore> and <char_semaphore> private.
- 20010813 Richard Gooch <rgooch@atnf.csiro.au>
- Fixed bug in <devfs_alloc_unique_number>: limited to 128 numbers
- 20010818 Richard Gooch <rgooch@atnf.csiro.au>
- Updated major masks up to Linus' "no new majors" proclamation.
- Block: were 126 now 122 free, char: were 26 now 19 free.
- 20020324 Richard Gooch <rgooch@atnf.csiro.au>
- Fixed bug in <devfs_alloc_unique_number>: was clearing beyond
- bitfield.
- 20020326 Richard Gooch <rgooch@atnf.csiro.au>
- Fixed bitfield data type for <devfs_*alloc_devnum>.
- Made major bitfield type and initialiser 64 bit safe.
- 20020413 Richard Gooch <rgooch@atnf.csiro.au>
- Fixed shift warning on 64 bit machines.
- 20020428 Richard Gooch <rgooch@atnf.csiro.au>
- Copied and used macro for error messages from fs/devfs/base.c
- 20021013 Richard Gooch <rgooch@atnf.csiro.au>
- Documentation fix.
- 20030101 Adam J. Richter <adam@yggdrasil.com>
- Eliminate DEVFS_SPECIAL_{CHR,BLK}. Use mode_t instead.
- 20030106 Christoph Hellwig <hch@infradead.org>
- Rewrite devfs_{,de}alloc_devnum to look like C code.
-*/
-#include <linux/module.h>
-#include <linux/init.h>
-#include <linux/devfs_fs_kernel.h>
-#include <linux/slab.h>
-#include <linux/vmalloc.h>
-#include <linux/genhd.h>
-#include <linux/bitops.h>
-
-int devfs_register_tape(const char *name)
-{
- char tname[32], dest[64];
- static unsigned int tape_counter;
- unsigned int n = tape_counter++;
-
- sprintf(dest, "../%s", name);
- sprintf(tname, "tapes/tape%u", n);
- devfs_mk_symlink(tname, dest);
-
- return n;
-}
-
-EXPORT_SYMBOL(devfs_register_tape);
-
-void devfs_unregister_tape(int num)
-{
- if (num >= 0)
- devfs_remove("tapes/tape%u", num);
-}
-
-EXPORT_SYMBOL(devfs_unregister_tape);
diff --git a/fs/efs/inode.c b/fs/efs/inode.c
index 180607f9314d..174696f9bf14 100644
--- a/fs/efs/inode.c
+++ b/fs/efs/inode.c
@@ -21,7 +21,7 @@ static sector_t _efs_bmap(struct address_space *mapping, sector_t block)
{
return generic_block_bmap(mapping,block,efs_get_block);
}
-static struct address_space_operations efs_aops = {
+static const struct address_space_operations efs_aops = {
.readpage = efs_readpage,
.sync_page = block_sync_page,
.bmap = _efs_bmap
diff --git a/fs/efs/symlink.c b/fs/efs/symlink.c
index 3d9a350e3e7f..e249cf733a6b 100644
--- a/fs/efs/symlink.c
+++ b/fs/efs/symlink.c
@@ -53,6 +53,6 @@ fail:
return err;
}
-struct address_space_operations efs_symlink_aops = {
+const struct address_space_operations efs_symlink_aops = {
.readpage = efs_symlink_readpage
};
diff --git a/fs/ext2/ext2.h b/fs/ext2/ext2.h
index 9f74a62be555..e65a019fc7a5 100644
--- a/fs/ext2/ext2.h
+++ b/fs/ext2/ext2.h
@@ -162,9 +162,9 @@ extern const struct file_operations ext2_file_operations;
extern const struct file_operations ext2_xip_file_operations;
/* inode.c */
-extern struct address_space_operations ext2_aops;
-extern struct address_space_operations ext2_aops_xip;
-extern struct address_space_operations ext2_nobh_aops;
+extern const struct address_space_operations ext2_aops;
+extern const struct address_space_operations ext2_aops_xip;
+extern const struct address_space_operations ext2_nobh_aops;
/* namei.c */
extern struct inode_operations ext2_dir_inode_operations;
diff --git a/fs/ext2/inode.c b/fs/ext2/inode.c
index 04af9c45dce2..fb4d3220eb8d 100644
--- a/fs/ext2/inode.c
+++ b/fs/ext2/inode.c
@@ -684,7 +684,7 @@ ext2_writepages(struct address_space *mapping, struct writeback_control *wbc)
return mpage_writepages(mapping, wbc, ext2_get_block);
}
-struct address_space_operations ext2_aops = {
+const struct address_space_operations ext2_aops = {
.readpage = ext2_readpage,
.readpages = ext2_readpages,
.writepage = ext2_writepage,
@@ -697,12 +697,12 @@ struct address_space_operations ext2_aops = {
.migratepage = buffer_migrate_page,
};
-struct address_space_operations ext2_aops_xip = {
+const struct address_space_operations ext2_aops_xip = {
.bmap = ext2_bmap,
.get_xip_page = ext2_get_xip_page,
};
-struct address_space_operations ext2_nobh_aops = {
+const struct address_space_operations ext2_nobh_aops = {
.readpage = ext2_readpage,
.readpages = ext2_readpages,
.writepage = ext2_nobh_writepage,
diff --git a/fs/ext3/inode.c b/fs/ext3/inode.c
index 0321e1b9034a..f804d5e9d60c 100644
--- a/fs/ext3/inode.c
+++ b/fs/ext3/inode.c
@@ -1698,7 +1698,7 @@ static int ext3_journalled_set_page_dirty(struct page *page)
return __set_page_dirty_nobuffers(page);
}
-static struct address_space_operations ext3_ordered_aops = {
+static const struct address_space_operations ext3_ordered_aops = {
.readpage = ext3_readpage,
.readpages = ext3_readpages,
.writepage = ext3_ordered_writepage,
@@ -1712,7 +1712,7 @@ static struct address_space_operations ext3_ordered_aops = {
.migratepage = buffer_migrate_page,
};
-static struct address_space_operations ext3_writeback_aops = {
+static const struct address_space_operations ext3_writeback_aops = {
.readpage = ext3_readpage,
.readpages = ext3_readpages,
.writepage = ext3_writeback_writepage,
@@ -1726,7 +1726,7 @@ static struct address_space_operations ext3_writeback_aops = {
.migratepage = buffer_migrate_page,
};
-static struct address_space_operations ext3_journalled_aops = {
+static const struct address_space_operations ext3_journalled_aops = {
.readpage = ext3_readpage,
.readpages = ext3_readpages,
.writepage = ext3_journalled_writepage,
diff --git a/fs/fat/inode.c b/fs/fat/inode.c
index 7c35d582ec10..31b7174176ba 100644
--- a/fs/fat/inode.c
+++ b/fs/fat/inode.c
@@ -196,7 +196,7 @@ static sector_t _fat_bmap(struct address_space *mapping, sector_t block)
return generic_block_bmap(mapping, block, fat_get_block);
}
-static struct address_space_operations fat_aops = {
+static const struct address_space_operations fat_aops = {
.readpage = fat_readpage,
.readpages = fat_readpages,
.writepage = fat_writepage,
diff --git a/fs/freevxfs/vxfs_immed.c b/fs/freevxfs/vxfs_immed.c
index 6f5df1700e95..4e25f3fbed86 100644
--- a/fs/freevxfs/vxfs_immed.c
+++ b/fs/freevxfs/vxfs_immed.c
@@ -56,7 +56,7 @@ struct inode_operations vxfs_immed_symlink_iops = {
/*
* Adress space operations for immed files and directories.
*/
-struct address_space_operations vxfs_immed_aops = {
+const struct address_space_operations vxfs_immed_aops = {
.readpage = vxfs_immed_readpage,
};
diff --git a/fs/freevxfs/vxfs_inode.c b/fs/freevxfs/vxfs_inode.c
index f544aae9169f..ca6a39714771 100644
--- a/fs/freevxfs/vxfs_inode.c
+++ b/fs/freevxfs/vxfs_inode.c
@@ -41,8 +41,8 @@
#include "vxfs_extern.h"
-extern struct address_space_operations vxfs_aops;
-extern struct address_space_operations vxfs_immed_aops;
+extern const struct address_space_operations vxfs_aops;
+extern const struct address_space_operations vxfs_immed_aops;
extern struct inode_operations vxfs_immed_symlink_iops;
@@ -295,7 +295,7 @@ vxfs_read_inode(struct inode *ip)
{
struct super_block *sbp = ip->i_sb;
struct vxfs_inode_info *vip;
- struct address_space_operations *aops;
+ const struct address_space_operations *aops;
ino_t ino = ip->i_ino;
if (!(vip = __vxfs_iget(ino, VXFS_SBI(sbp)->vsi_ilist)))
diff --git a/fs/freevxfs/vxfs_subr.c b/fs/freevxfs/vxfs_subr.c
index c1be118fc067..decac62efe57 100644
--- a/fs/freevxfs/vxfs_subr.c
+++ b/fs/freevxfs/vxfs_subr.c
@@ -42,7 +42,7 @@
static int vxfs_readpage(struct file *, struct page *);
static sector_t vxfs_bmap(struct address_space *, sector_t);
-struct address_space_operations vxfs_aops = {
+const struct address_space_operations vxfs_aops = {
.readpage = vxfs_readpage,
.bmap = vxfs_bmap,
.sync_page = block_sync_page,
diff --git a/fs/fuse/file.c b/fs/fuse/file.c
index 28aa81eae2cc..63614ed16336 100644
--- a/fs/fuse/file.c
+++ b/fs/fuse/file.c
@@ -770,7 +770,7 @@ static const struct file_operations fuse_direct_io_file_operations = {
/* no mmap and sendfile */
};
-static struct address_space_operations fuse_file_aops = {
+static const struct address_space_operations fuse_file_aops = {
.readpage = fuse_readpage,
.prepare_write = fuse_prepare_write,
.commit_write = fuse_commit_write,
diff --git a/fs/hfs/hfs_fs.h b/fs/hfs/hfs_fs.h
index 3ed8663a8db1..735332dfd1b8 100644
--- a/fs/hfs/hfs_fs.h
+++ b/fs/hfs/hfs_fs.h
@@ -182,8 +182,8 @@ extern void hfs_file_truncate(struct inode *);
extern int hfs_get_block(struct inode *, sector_t, struct buffer_head *, int);
/* inode.c */
-extern struct address_space_operations hfs_aops;
-extern struct address_space_operations hfs_btree_aops;
+extern const struct address_space_operations hfs_aops;
+extern const struct address_space_operations hfs_btree_aops;
extern struct inode *hfs_new_inode(struct inode *, struct qstr *, int);
extern void hfs_inode_write_fork(struct inode *, struct hfs_extent *, __be32 *, __be32 *);
diff --git a/fs/hfs/inode.c b/fs/hfs/inode.c
index 2d4ced22201b..315cf44a90b2 100644
--- a/fs/hfs/inode.c
+++ b/fs/hfs/inode.c
@@ -114,7 +114,7 @@ static int hfs_writepages(struct address_space *mapping,
return mpage_writepages(mapping, wbc, hfs_get_block);
}
-struct address_space_operations hfs_btree_aops = {
+const struct address_space_operations hfs_btree_aops = {
.readpage = hfs_readpage,
.writepage = hfs_writepage,
.sync_page = block_sync_page,
@@ -124,7 +124,7 @@ struct address_space_operations hfs_btree_aops = {
.releasepage = hfs_releasepage,
};
-struct address_space_operations hfs_aops = {
+const struct address_space_operations hfs_aops = {
.readpage = hfs_readpage,
.writepage = hfs_writepage,
.sync_page = block_sync_page,
diff --git a/fs/hfsplus/hfsplus_fs.h b/fs/hfsplus/hfsplus_fs.h
index 7ae393637a0c..8a1ca5ef7ada 100644
--- a/fs/hfsplus/hfsplus_fs.h
+++ b/fs/hfsplus/hfsplus_fs.h
@@ -323,8 +323,8 @@ int hfsplus_file_extend(struct inode *);
void hfsplus_file_truncate(struct inode *);
/* inode.c */
-extern struct address_space_operations hfsplus_aops;
-extern struct address_space_operations hfsplus_btree_aops;
+extern const struct address_space_operations hfsplus_aops;
+extern const struct address_space_operations hfsplus_btree_aops;
void hfsplus_inode_read_fork(struct inode *, struct hfsplus_fork_raw *);
void hfsplus_inode_write_fork(struct inode *, struct hfsplus_fork_raw *);
diff --git a/fs/hfsplus/inode.c b/fs/hfsplus/inode.c
index acf66dba3e01..924ecdef8091 100644
--- a/fs/hfsplus/inode.c
+++ b/fs/hfsplus/inode.c
@@ -109,7 +109,7 @@ static int hfsplus_writepages(struct address_space *mapping,
return mpage_writepages(mapping, wbc, hfsplus_get_block);
}
-struct address_space_operations hfsplus_btree_aops = {
+const struct address_space_operations hfsplus_btree_aops = {
.readpage = hfsplus_readpage,
.writepage = hfsplus_writepage,
.sync_page = block_sync_page,
@@ -119,7 +119,7 @@ struct address_space_operations hfsplus_btree_aops = {
.releasepage = hfsplus_releasepage,
};
-struct address_space_operations hfsplus_aops = {
+const struct address_space_operations hfsplus_aops = {
.readpage = hfsplus_readpage,
.writepage = hfsplus_writepage,
.sync_page = block_sync_page,
diff --git a/fs/hostfs/hostfs_kern.c b/fs/hostfs/hostfs_kern.c
index 8e0d37743e7c..b82e3d9c8790 100644
--- a/fs/hostfs/hostfs_kern.c
+++ b/fs/hostfs/hostfs_kern.c
@@ -54,7 +54,7 @@ static int append = 0;
static struct inode_operations hostfs_iops;
static struct inode_operations hostfs_dir_iops;
-static struct address_space_operations hostfs_link_aops;
+static const struct address_space_operations hostfs_link_aops;
#ifndef MODULE
static int __init hostfs_args(char *options, int *add)
@@ -518,7 +518,7 @@ int hostfs_commit_write(struct file *file, struct page *page, unsigned from,
return(err);
}
-static struct address_space_operations hostfs_aops = {
+static const struct address_space_operations hostfs_aops = {
.writepage = hostfs_writepage,
.readpage = hostfs_readpage,
.set_page_dirty = __set_page_dirty_nobuffers,
@@ -935,7 +935,7 @@ int hostfs_link_readpage(struct file *file, struct page *page)
return(err);
}
-static struct address_space_operations hostfs_link_aops = {
+static const struct address_space_operations hostfs_link_aops = {
.readpage = hostfs_link_readpage,
};
diff --git a/fs/hpfs/file.c b/fs/hpfs/file.c
index d3b9fffe45a1..d9eb19b7b8ae 100644
--- a/fs/hpfs/file.c
+++ b/fs/hpfs/file.c
@@ -99,7 +99,7 @@ static sector_t _hpfs_bmap(struct address_space *mapping, sector_t block)
{
return generic_block_bmap(mapping,block,hpfs_get_block);
}
-struct address_space_operations hpfs_aops = {
+const struct address_space_operations hpfs_aops = {
.readpage = hpfs_readpage,
.writepage = hpfs_writepage,
.sync_page = block_sync_page,
diff --git a/fs/hpfs/hpfs_fn.h b/fs/hpfs/hpfs_fn.h
index 29b7a3e55173..f687d54ed442 100644
--- a/fs/hpfs/hpfs_fn.h
+++ b/fs/hpfs/hpfs_fn.h
@@ -268,7 +268,7 @@ void hpfs_set_ea(struct inode *, struct fnode *, char *, char *, int);
int hpfs_file_fsync(struct file *, struct dentry *, int);
extern const struct file_operations hpfs_file_ops;
extern struct inode_operations hpfs_file_iops;
-extern struct address_space_operations hpfs_aops;
+extern const struct address_space_operations hpfs_aops;
/* inode.c */
@@ -304,7 +304,7 @@ void hpfs_decide_conv(struct inode *, unsigned char *, unsigned);
/* namei.c */
extern struct inode_operations hpfs_dir_iops;
-extern struct address_space_operations hpfs_symlink_aops;
+extern const struct address_space_operations hpfs_symlink_aops;
static inline struct hpfs_inode_info *hpfs_i(struct inode *inode)
{
diff --git a/fs/hpfs/namei.c b/fs/hpfs/namei.c
index a03abb12c610..59e7dc182a0c 100644
--- a/fs/hpfs/namei.c
+++ b/fs/hpfs/namei.c
@@ -538,7 +538,7 @@ fail:
return err;
}
-struct address_space_operations hpfs_symlink_aops = {
+const struct address_space_operations hpfs_symlink_aops = {
.readpage = hpfs_symlink_readpage
};
diff --git a/fs/hugetlbfs/inode.c b/fs/hugetlbfs/inode.c
index e6410d8edd0e..6449cb697967 100644
--- a/fs/hugetlbfs/inode.c
+++ b/fs/hugetlbfs/inode.c
@@ -34,7 +34,7 @@
#define HUGETLBFS_MAGIC 0x958458f6
static struct super_operations hugetlbfs_ops;
-static struct address_space_operations hugetlbfs_aops;
+static const struct address_space_operations hugetlbfs_aops;
const struct file_operations hugetlbfs_file_operations;
static struct inode_operations hugetlbfs_dir_inode_operations;
static struct inode_operations hugetlbfs_inode_operations;
@@ -547,7 +547,7 @@ static void hugetlbfs_destroy_inode(struct inode *inode)
kmem_cache_free(hugetlbfs_inode_cachep, HUGETLBFS_I(inode));
}
-static struct address_space_operations hugetlbfs_aops = {
+static const struct address_space_operations hugetlbfs_aops = {
.readpage = hugetlbfs_readpage,
.prepare_write = hugetlbfs_prepare_write,
.commit_write = hugetlbfs_commit_write,
diff --git a/fs/inode.c b/fs/inode.c
index 3a2446a27d2c..f42961eb983b 100644
--- a/fs/inode.c
+++ b/fs/inode.c
@@ -102,7 +102,7 @@ static kmem_cache_t * inode_cachep __read_mostly;
static struct inode *alloc_inode(struct super_block *sb)
{
- static struct address_space_operations empty_aops;
+ static const struct address_space_operations empty_aops;
static struct inode_operations empty_iops;
static const struct file_operations empty_fops;
struct inode *inode;
diff --git a/fs/isofs/compress.c b/fs/isofs/compress.c
index 4917315db732..3a39158cca96 100644
--- a/fs/isofs/compress.c
+++ b/fs/isofs/compress.c
@@ -312,7 +312,7 @@ eio:
return err;
}
-struct address_space_operations zisofs_aops = {
+const struct address_space_operations zisofs_aops = {
.readpage = zisofs_readpage,
/* No sync_page operation supported? */
/* No bmap operation supported */
diff --git a/fs/isofs/inode.c b/fs/isofs/inode.c
index 3f9c8ba1fa1f..bb11c7fb4019 100644
--- a/fs/isofs/inode.c
+++ b/fs/isofs/inode.c
@@ -1054,7 +1054,7 @@ static sector_t _isofs_bmap(struct address_space *mapping, sector_t block)
return generic_block_bmap(mapping,block,isofs_get_block);
}
-static struct address_space_operations isofs_aops = {
+static const struct address_space_operations isofs_aops = {
.readpage = isofs_readpage,
.sync_page = block_sync_page,
.bmap = _isofs_bmap
diff --git a/fs/isofs/isofs.h b/fs/isofs/isofs.h
index b87ba066f5e7..e6308c8b5735 100644
--- a/fs/isofs/isofs.h
+++ b/fs/isofs/isofs.h
@@ -176,5 +176,5 @@ isofs_normalize_block_and_offset(struct iso_directory_record* de,
extern struct inode_operations isofs_dir_inode_operations;
extern const struct file_operations isofs_dir_operations;
-extern struct address_space_operations isofs_symlink_aops;
+extern const struct address_space_operations isofs_symlink_aops;
extern struct export_operations isofs_export_ops;
diff --git a/fs/isofs/rock.c b/fs/isofs/rock.c
index 4326cb47f8fa..f3a1db3098de 100644
--- a/fs/isofs/rock.c
+++ b/fs/isofs/rock.c
@@ -754,6 +754,6 @@ error:
return -EIO;
}
-struct address_space_operations isofs_symlink_aops = {
+const struct address_space_operations isofs_symlink_aops = {
.readpage = rock_ridge_symlink_readpage
};
diff --git a/fs/isofs/zisofs.h b/fs/isofs/zisofs.h
index d78485d101c2..273795709155 100644
--- a/fs/isofs/zisofs.h
+++ b/fs/isofs/zisofs.h
@@ -15,7 +15,7 @@
*/
#ifdef CONFIG_ZISOFS
-extern struct address_space_operations zisofs_aops;
+extern const struct address_space_operations zisofs_aops;
extern int __init zisofs_init(void);
extern void zisofs_cleanup(void);
#endif
diff --git a/fs/jbd/journal.c b/fs/jbd/journal.c
index 7f96b5cb6781..8c9b28dff119 100644
--- a/fs/jbd/journal.c
+++ b/fs/jbd/journal.c
@@ -34,6 +34,7 @@
#include <linux/suspend.h>
#include <linux/pagemap.h>
#include <linux/kthread.h>
+#include <linux/poison.h>
#include <linux/proc_fs.h>
#include <asm/uaccess.h>
@@ -1675,7 +1676,7 @@ static void journal_free_journal_head(struct journal_head *jh)
{
#ifdef CONFIG_JBD_DEBUG
atomic_dec(&nr_journal_heads);
- memset(jh, 0x5b, sizeof(*jh));
+ memset(jh, JBD_POISON_FREE, sizeof(*jh));
#endif
kmem_cache_free(journal_head_cache, jh);
}
diff --git a/fs/jffs/inode-v23.c b/fs/jffs/inode-v23.c
index 9e46ea6da752..93068697a9bf 100644
--- a/fs/jffs/inode-v23.c
+++ b/fs/jffs/inode-v23.c
@@ -59,7 +59,7 @@ static const struct file_operations jffs_file_operations;
static struct inode_operations jffs_file_inode_operations;
static const struct file_operations jffs_dir_operations;
static struct inode_operations jffs_dir_inode_operations;
-static struct address_space_operations jffs_address_operations;
+static const struct address_space_operations jffs_address_operations;
kmem_cache_t *node_cache = NULL;
kmem_cache_t *fm_cache = NULL;
@@ -1614,7 +1614,7 @@ jffs_ioctl(struct inode *inode, struct file *filp, unsigned int cmd,
} /* jffs_ioctl() */
-static struct address_space_operations jffs_address_operations = {
+static const struct address_space_operations jffs_address_operations = {
.readpage = jffs_readpage,
.prepare_write = jffs_prepare_write,
.commit_write = jffs_commit_write,
diff --git a/fs/jffs2/acl.c b/fs/jffs2/acl.c
index 320dd48b834e..9c2077e7e081 100644
--- a/fs/jffs2/acl.c
+++ b/fs/jffs2/acl.c
@@ -267,6 +267,8 @@ static int jffs2_set_acl(struct inode *inode, int type, struct posix_acl *acl)
}
rc = do_jffs2_setxattr(inode, xprefix, "", value, size, 0);
+ if (!value && rc == -ENODATA)
+ rc = 0;
if (value)
kfree(value);
if (!rc) {
diff --git a/fs/jffs2/erase.c b/fs/jffs2/erase.c
index b8886f048eaa..ad0121088dde 100644
--- a/fs/jffs2/erase.c
+++ b/fs/jffs2/erase.c
@@ -225,7 +225,6 @@ static inline void jffs2_remove_node_refs_from_ino_list(struct jffs2_sb_info *c,
at the end of the linked list. Stash it and continue
from the beginning of the list */
ic = (struct jffs2_inode_cache *)(*prev);
- BUG_ON(ic->class != RAWNODE_CLASS_INODE_CACHE);
prev = &ic->nodes;
continue;
}
@@ -249,7 +248,8 @@ static inline void jffs2_remove_node_refs_from_ino_list(struct jffs2_sb_info *c,
/* PARANOIA */
if (!ic) {
- printk(KERN_WARNING "inode_cache not found in remove_node_refs()!!\n");
+ JFFS2_WARNING("inode_cache/xattr_datum/xattr_ref"
+ " not found in remove_node_refs()!!\n");
return;
}
@@ -274,8 +274,19 @@ static inline void jffs2_remove_node_refs_from_ino_list(struct jffs2_sb_info *c,
printk("\n");
});
- if (ic->nodes == (void *)ic && ic->nlink == 0)
- jffs2_del_ino_cache(c, ic);
+ switch (ic->class) {
+#ifdef CONFIG_JFFS2_FS_XATTR
+ case RAWNODE_CLASS_XATTR_DATUM:
+ jffs2_release_xattr_datum(c, (struct jffs2_xattr_datum *)ic);
+ break;
+ case RAWNODE_CLASS_XATTR_REF:
+ jffs2_release_xattr_ref(c, (struct jffs2_xattr_ref *)ic);
+ break;
+#endif
+ default:
+ if (ic->nodes == (void *)ic && ic->nlink == 0)
+ jffs2_del_ino_cache(c, ic);
+ }
}
void jffs2_free_jeb_node_refs(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb)
diff --git a/fs/jffs2/file.c b/fs/jffs2/file.c
index bb8844f40e48..3ed6e3e120b6 100644
--- a/fs/jffs2/file.c
+++ b/fs/jffs2/file.c
@@ -62,7 +62,7 @@ struct inode_operations jffs2_file_inode_operations =
.removexattr = jffs2_removexattr
};
-struct address_space_operations jffs2_file_address_operations =
+const struct address_space_operations jffs2_file_address_operations =
{
.readpage = jffs2_readpage,
.prepare_write =jffs2_prepare_write,
diff --git a/fs/jffs2/fs.c b/fs/jffs2/fs.c
index 2900ec3ec3af..97caa77d60cf 100644
--- a/fs/jffs2/fs.c
+++ b/fs/jffs2/fs.c
@@ -227,8 +227,6 @@ void jffs2_clear_inode (struct inode *inode)
struct jffs2_inode_info *f = JFFS2_INODE_INFO(inode);
D1(printk(KERN_DEBUG "jffs2_clear_inode(): ino #%lu mode %o\n", inode->i_ino, inode->i_mode));
-
- jffs2_xattr_delete_inode(c, f->inocache);
jffs2_do_clear_inode(c, f);
}
diff --git a/fs/jffs2/gc.c b/fs/jffs2/gc.c
index 477c526d638b..daff3341ff92 100644
--- a/fs/jffs2/gc.c
+++ b/fs/jffs2/gc.c
@@ -165,6 +165,7 @@ int jffs2_garbage_collect_pass(struct jffs2_sb_info *c)
D1(printk(KERN_DEBUG "Skipping check of ino #%d with nlink zero\n",
ic->ino));
spin_unlock(&c->inocache_lock);
+ jffs2_xattr_delete_inode(c, ic);
continue;
}
switch(ic->state) {
@@ -275,13 +276,12 @@ int jffs2_garbage_collect_pass(struct jffs2_sb_info *c)
* We can decide whether this node is inode or xattr by ic->class. */
if (ic->class == RAWNODE_CLASS_XATTR_DATUM
|| ic->class == RAWNODE_CLASS_XATTR_REF) {
- BUG_ON(raw->next_in_ino != (void *)ic);
spin_unlock(&c->erase_completion_lock);
if (ic->class == RAWNODE_CLASS_XATTR_DATUM) {
- ret = jffs2_garbage_collect_xattr_datum(c, (struct jffs2_xattr_datum *)ic);
+ ret = jffs2_garbage_collect_xattr_datum(c, (struct jffs2_xattr_datum *)ic, raw);
} else {
- ret = jffs2_garbage_collect_xattr_ref(c, (struct jffs2_xattr_ref *)ic);
+ ret = jffs2_garbage_collect_xattr_ref(c, (struct jffs2_xattr_ref *)ic, raw);
}
goto release_sem;
}
diff --git a/fs/jffs2/jffs2_fs_sb.h b/fs/jffs2/jffs2_fs_sb.h
index 935fec1b1201..b98594992eed 100644
--- a/fs/jffs2/jffs2_fs_sb.h
+++ b/fs/jffs2/jffs2_fs_sb.h
@@ -119,8 +119,11 @@ struct jffs2_sb_info {
#ifdef CONFIG_JFFS2_FS_XATTR
#define XATTRINDEX_HASHSIZE (57)
uint32_t highest_xid;
+ uint32_t highest_xseqno;
struct list_head xattrindex[XATTRINDEX_HASHSIZE];
struct list_head xattr_unchecked;
+ struct list_head xattr_dead_list;
+ struct jffs2_xattr_ref *xref_dead_list;
struct jffs2_xattr_ref *xref_temp;
struct rw_semaphore xattr_sem;
uint32_t xdatum_mem_usage;
diff --git a/fs/jffs2/malloc.c b/fs/jffs2/malloc.c
index 4889d0700c0e..8310c95478e9 100644
--- a/fs/jffs2/malloc.c
+++ b/fs/jffs2/malloc.c
@@ -291,6 +291,7 @@ struct jffs2_xattr_datum *jffs2_alloc_xattr_datum(void)
memset(xd, 0, sizeof(struct jffs2_xattr_datum));
xd->class = RAWNODE_CLASS_XATTR_DATUM;
+ xd->node = (void *)xd;
INIT_LIST_HEAD(&xd->xindex);
return xd;
}
@@ -309,6 +310,7 @@ struct jffs2_xattr_ref *jffs2_alloc_xattr_ref(void)
memset(ref, 0, sizeof(struct jffs2_xattr_ref));
ref->class = RAWNODE_CLASS_XATTR_REF;
+ ref->node = (void *)ref;
return ref;
}
diff --git a/fs/jffs2/nodelist.c b/fs/jffs2/nodelist.c
index 927dfe42ba76..7675b33396c7 100644
--- a/fs/jffs2/nodelist.c
+++ b/fs/jffs2/nodelist.c
@@ -906,6 +906,9 @@ void jffs2_del_ino_cache(struct jffs2_sb_info *c, struct jffs2_inode_cache *old)
{
struct jffs2_inode_cache **prev;
+#ifdef CONFIG_JFFS2_FS_XATTR
+ BUG_ON(old->xref);
+#endif
dbg_inocache("del %p (ino #%u)\n", old, old->ino);
spin_lock(&c->inocache_lock);
diff --git a/fs/jffs2/nodemgmt.c b/fs/jffs2/nodemgmt.c
index ac0c350ed7d7..d88376992ed9 100644
--- a/fs/jffs2/nodemgmt.c
+++ b/fs/jffs2/nodemgmt.c
@@ -683,19 +683,26 @@ void jffs2_mark_node_obsolete(struct jffs2_sb_info *c, struct jffs2_raw_node_ref
spin_lock(&c->erase_completion_lock);
ic = jffs2_raw_ref_to_ic(ref);
- /* It seems we should never call jffs2_mark_node_obsolete() for
- XATTR nodes.... yet. Make sure we notice if/when we change
- that :) */
- BUG_ON(ic->class != RAWNODE_CLASS_INODE_CACHE);
for (p = &ic->nodes; (*p) != ref; p = &((*p)->next_in_ino))
;
*p = ref->next_in_ino;
ref->next_in_ino = NULL;
- if (ic->nodes == (void *)ic && ic->nlink == 0)
- jffs2_del_ino_cache(c, ic);
-
+ switch (ic->class) {
+#ifdef CONFIG_JFFS2_FS_XATTR
+ case RAWNODE_CLASS_XATTR_DATUM:
+ jffs2_release_xattr_datum(c, (struct jffs2_xattr_datum *)ic);
+ break;
+ case RAWNODE_CLASS_XATTR_REF:
+ jffs2_release_xattr_ref(c, (struct jffs2_xattr_ref *)ic);
+ break;
+#endif
+ default:
+ if (ic->nodes == (void *)ic && ic->nlink == 0)
+ jffs2_del_ino_cache(c, ic);
+ break;
+ }
spin_unlock(&c->erase_completion_lock);
}
diff --git a/fs/jffs2/os-linux.h b/fs/jffs2/os-linux.h
index 6b5223565405..9f41fc01a371 100644
--- a/fs/jffs2/os-linux.h
+++ b/fs/jffs2/os-linux.h
@@ -158,7 +158,7 @@ extern struct inode_operations jffs2_dir_inode_operations;
/* file.c */
extern const struct file_operations jffs2_file_operations;
extern struct inode_operations jffs2_file_inode_operations;
-extern struct address_space_operations jffs2_file_address_operations;
+extern const struct address_space_operations jffs2_file_address_operations;
int jffs2_fsync(struct file *, struct dentry *, int);
int jffs2_do_readpage_unlock (struct inode *inode, struct page *pg);
diff --git a/fs/jffs2/readinode.c b/fs/jffs2/readinode.c
index 5fec012b02ed..cc1899268c43 100644
--- a/fs/jffs2/readinode.c
+++ b/fs/jffs2/readinode.c
@@ -968,6 +968,7 @@ void jffs2_do_clear_inode(struct jffs2_sb_info *c, struct jffs2_inode_info *f)
struct jffs2_full_dirent *fd, *fds;
int deleted;
+ jffs2_xattr_delete_inode(c, f->inocache);
down(&f->sem);
deleted = f->inocache && !f->inocache->nlink;
diff --git a/fs/jffs2/scan.c b/fs/jffs2/scan.c
index 61618080b86f..2bfdc33752d3 100644
--- a/fs/jffs2/scan.c
+++ b/fs/jffs2/scan.c
@@ -317,20 +317,23 @@ static int jffs2_scan_xattr_node(struct jffs2_sb_info *c, struct jffs2_erasebloc
struct jffs2_summary *s)
{
struct jffs2_xattr_datum *xd;
- uint32_t totlen, crc;
+ uint32_t xid, version, totlen, crc;
int err;
crc = crc32(0, rx, sizeof(struct jffs2_raw_xattr) - 4);
if (crc != je32_to_cpu(rx->node_crc)) {
- if (je32_to_cpu(rx->node_crc) != 0xffffffff)
- JFFS2_WARNING("node CRC failed at %#08x, read=%#08x, calc=%#08x\n",
- ofs, je32_to_cpu(rx->node_crc), crc);
+ JFFS2_WARNING("node CRC failed at %#08x, read=%#08x, calc=%#08x\n",
+ ofs, je32_to_cpu(rx->node_crc), crc);
if ((err = jffs2_scan_dirty_space(c, jeb, je32_to_cpu(rx->totlen))))
return err;
return 0;
}
- totlen = PAD(sizeof(*rx) + rx->name_len + 1 + je16_to_cpu(rx->value_len));
+ xid = je32_to_cpu(rx->xid);
+ version = je32_to_cpu(rx->version);
+
+ totlen = PAD(sizeof(struct jffs2_raw_xattr)
+ + rx->name_len + 1 + je16_to_cpu(rx->value_len));
if (totlen != je32_to_cpu(rx->totlen)) {
JFFS2_WARNING("node length mismatch at %#08x, read=%u, calc=%u\n",
ofs, je32_to_cpu(rx->totlen), totlen);
@@ -339,22 +342,24 @@ static int jffs2_scan_xattr_node(struct jffs2_sb_info *c, struct jffs2_erasebloc
return 0;
}
- xd = jffs2_setup_xattr_datum(c, je32_to_cpu(rx->xid), je32_to_cpu(rx->version));
- if (IS_ERR(xd)) {
- if (PTR_ERR(xd) == -EEXIST) {
- if ((err = jffs2_scan_dirty_space(c, jeb, PAD(je32_to_cpu(rx->totlen)))))
- return err;
- return 0;
- }
+ xd = jffs2_setup_xattr_datum(c, xid, version);
+ if (IS_ERR(xd))
return PTR_ERR(xd);
- }
- xd->xprefix = rx->xprefix;
- xd->name_len = rx->name_len;
- xd->value_len = je16_to_cpu(rx->value_len);
- xd->data_crc = je32_to_cpu(rx->data_crc);
- xd->node = jffs2_link_node_ref(c, jeb, ofs | REF_PRISTINE, totlen, NULL);
- /* FIXME */ xd->node->next_in_ino = (void *)xd;
+ if (xd->version > version) {
+ struct jffs2_raw_node_ref *raw
+ = jffs2_link_node_ref(c, jeb, ofs | REF_PRISTINE, totlen, NULL);
+ raw->next_in_ino = xd->node->next_in_ino;
+ xd->node->next_in_ino = raw;
+ } else {
+ xd->version = version;
+ xd->xprefix = rx->xprefix;
+ xd->name_len = rx->name_len;
+ xd->value_len = je16_to_cpu(rx->value_len);
+ xd->data_crc = je32_to_cpu(rx->data_crc);
+
+ jffs2_link_node_ref(c, jeb, ofs | REF_PRISTINE, totlen, (void *)xd);
+ }
if (jffs2_sum_active())
jffs2_sum_add_xattr_mem(s, rx, ofs - jeb->offset);
@@ -373,9 +378,8 @@ static int jffs2_scan_xref_node(struct jffs2_sb_info *c, struct jffs2_eraseblock
crc = crc32(0, rr, sizeof(*rr) - 4);
if (crc != je32_to_cpu(rr->node_crc)) {
- if (je32_to_cpu(rr->node_crc) != 0xffffffff)
- JFFS2_WARNING("node CRC failed at %#08x, read=%#08x, calc=%#08x\n",
- ofs, je32_to_cpu(rr->node_crc), crc);
+ JFFS2_WARNING("node CRC failed at %#08x, read=%#08x, calc=%#08x\n",
+ ofs, je32_to_cpu(rr->node_crc), crc);
if ((err = jffs2_scan_dirty_space(c, jeb, PAD(je32_to_cpu(rr->totlen)))))
return err;
return 0;
@@ -395,6 +399,7 @@ static int jffs2_scan_xref_node(struct jffs2_sb_info *c, struct jffs2_eraseblock
return -ENOMEM;
/* BEFORE jffs2_build_xattr_subsystem() called,
+ * and AFTER xattr_ref is marked as a dead xref,
* ref->xid is used to store 32bit xid, xd is not used
* ref->ino is used to store 32bit inode-number, ic is not used
* Thoes variables are declared as union, thus using those
@@ -404,11 +409,13 @@ static int jffs2_scan_xref_node(struct jffs2_sb_info *c, struct jffs2_eraseblock
*/
ref->ino = je32_to_cpu(rr->ino);
ref->xid = je32_to_cpu(rr->xid);
+ ref->xseqno = je32_to_cpu(rr->xseqno);
+ if (ref->xseqno > c->highest_xseqno)
+ c->highest_xseqno = (ref->xseqno & ~XREF_DELETE_MARKER);
ref->next = c->xref_temp;
c->xref_temp = ref;
- ref->node = jffs2_link_node_ref(c, jeb, ofs | REF_PRISTINE, PAD(je32_to_cpu(rr->totlen)), NULL);
- /* FIXME */ ref->node->next_in_ino = (void *)ref;
+ jffs2_link_node_ref(c, jeb, ofs | REF_PRISTINE, PAD(je32_to_cpu(rr->totlen)), (void *)ref);
if (jffs2_sum_active())
jffs2_sum_add_xref_mem(s, rr, ofs - jeb->offset);
diff --git a/fs/jffs2/summary.c b/fs/jffs2/summary.c
index be1acc3dad97..c19bd476e8ec 100644
--- a/fs/jffs2/summary.c
+++ b/fs/jffs2/summary.c
@@ -5,7 +5,7 @@
* Zoltan Sogor <weth@inf.u-szeged.hu>,
* Patrik Kluba <pajko@halom.u-szeged.hu>,
* University of Szeged, Hungary
- * 2005 KaiGai Kohei <kaigai@ak.jp.nec.com>
+ * 2006 KaiGai Kohei <kaigai@ak.jp.nec.com>
*
* For licensing information, see the file 'LICENCE' in this directory.
*
@@ -310,8 +310,6 @@ int jffs2_sum_add_kvec(struct jffs2_sb_info *c, const struct kvec *invecs,
#ifdef CONFIG_JFFS2_FS_XATTR
case JFFS2_NODETYPE_XATTR: {
struct jffs2_sum_xattr_mem *temp;
- if (je32_to_cpu(node->x.version) == 0xffffffff)
- return 0;
temp = kmalloc(sizeof(struct jffs2_sum_xattr_mem), GFP_KERNEL);
if (!temp)
goto no_mem;
@@ -327,10 +325,6 @@ int jffs2_sum_add_kvec(struct jffs2_sb_info *c, const struct kvec *invecs,
}
case JFFS2_NODETYPE_XREF: {
struct jffs2_sum_xref_mem *temp;
-
- if (je32_to_cpu(node->r.ino) == 0xffffffff
- && je32_to_cpu(node->r.xid) == 0xffffffff)
- return 0;
temp = kmalloc(sizeof(struct jffs2_sum_xref_mem), GFP_KERNEL);
if (!temp)
goto no_mem;
@@ -483,22 +477,20 @@ static int jffs2_sum_process_sum_data(struct jffs2_sb_info *c, struct jffs2_eras
xd = jffs2_setup_xattr_datum(c, je32_to_cpu(spx->xid),
je32_to_cpu(spx->version));
- if (IS_ERR(xd)) {
- if (PTR_ERR(xd) == -EEXIST) {
- /* a newer version of xd exists */
- if ((err = jffs2_scan_dirty_space(c, jeb, je32_to_cpu(spx->totlen))))
- return err;
- sp += JFFS2_SUMMARY_XATTR_SIZE;
- break;
- }
- JFFS2_NOTICE("allocation of xattr_datum failed\n");
+ if (IS_ERR(xd))
return PTR_ERR(xd);
+ if (xd->version > je32_to_cpu(spx->version)) {
+ /* node is not the newest one */
+ struct jffs2_raw_node_ref *raw
+ = sum_link_node_ref(c, jeb, je32_to_cpu(spx->offset) | REF_UNCHECKED,
+ PAD(je32_to_cpu(spx->totlen)), NULL);
+ raw->next_in_ino = xd->node->next_in_ino;
+ xd->node->next_in_ino = raw;
+ } else {
+ xd->version = je32_to_cpu(spx->version);
+ sum_link_node_ref(c, jeb, je32_to_cpu(spx->offset) | REF_UNCHECKED,
+ PAD(je32_to_cpu(spx->totlen)), (void *)xd);
}
-
- xd->node = sum_link_node_ref(c, jeb, je32_to_cpu(spx->offset) | REF_UNCHECKED,
- PAD(je32_to_cpu(spx->totlen)), NULL);
- /* FIXME */ xd->node->next_in_ino = (void *)xd;
-
*pseudo_random += je32_to_cpu(spx->xid);
sp += JFFS2_SUMMARY_XATTR_SIZE;
@@ -519,14 +511,11 @@ static int jffs2_sum_process_sum_data(struct jffs2_sb_info *c, struct jffs2_eras
JFFS2_NOTICE("allocation of xattr_datum failed\n");
return -ENOMEM;
}
- ref->ino = 0xfffffffe;
- ref->xid = 0xfffffffd;
ref->next = c->xref_temp;
c->xref_temp = ref;
- ref->node = sum_link_node_ref(c, jeb, je32_to_cpu(spr->offset) | REF_UNCHECKED,
- PAD(sizeof(struct jffs2_raw_xref)), NULL);
- /* FIXME */ ref->node->next_in_ino = (void *)ref;
+ sum_link_node_ref(c, jeb, je32_to_cpu(spr->offset) | REF_UNCHECKED,
+ PAD(sizeof(struct jffs2_raw_xref)), (void *)ref);
*pseudo_random += ref->node->flash_offset;
sp += JFFS2_SUMMARY_XREF_SIZE;
diff --git a/fs/jffs2/xattr.c b/fs/jffs2/xattr.c
index 2d82e250be34..18e66dbf23b4 100644
--- a/fs/jffs2/xattr.c
+++ b/fs/jffs2/xattr.c
@@ -23,18 +23,15 @@
* xattr_datum_hashkey(xprefix, xname, xvalue, xsize)
* is used to calcurate xdatum hashkey. The reminder of hashkey into XATTRINDEX_HASHSIZE is
* the index of the xattr name/value pair cache (c->xattrindex).
+ * is_xattr_datum_unchecked(c, xd)
+ * returns 1, if xdatum contains any unchecked raw nodes. if all raw nodes are not
+ * unchecked, it returns 0.
* unload_xattr_datum(c, xd)
* is used to release xattr name/value pair and detach from c->xattrindex.
* reclaim_xattr_datum(c)
* is used to reclaim xattr name/value pairs on the xattr name/value pair cache when
* memory usage by cache is over c->xdatum_mem_threshold. Currentry, this threshold
* is hard coded as 32KiB.
- * delete_xattr_datum_node(c, xd)
- * is used to delete a jffs2 node is dominated by xdatum. When EBS(Erase Block Summary) is
- * enabled, it overwrites the obsolete node by myself.
- * delete_xattr_datum(c, xd)
- * is used to delete jffs2_xattr_datum object. It must be called with 0-value of reference
- * counter. (It means how many jffs2_xattr_ref object refers this xdatum.)
* do_verify_xattr_datum(c, xd)
* is used to load the xdatum informations without name/value pair from the medium.
* It's necessary once, because those informations are not collected during mounting
@@ -53,8 +50,10 @@
* is used to write xdatum to medium. xd->version will be incremented.
* create_xattr_datum(c, xprefix, xname, xvalue, xsize)
* is used to create new xdatum and write to medium.
+ * delete_xattr_datum(c, xd)
+ * is used to delete a xdatum. It marks xd JFFS2_XFLAGS_DEAD, and allows
+ * GC to reclaim those physical nodes.
* -------------------------------------------------- */
-
static uint32_t xattr_datum_hashkey(int xprefix, const char *xname, const char *xvalue, int xsize)
{
int name_len = strlen(xname);
@@ -62,6 +61,22 @@ static uint32_t xattr_datum_hashkey(int xprefix, const char *xname, const char *
return crc32(xprefix, xname, name_len) ^ crc32(xprefix, xvalue, xsize);
}
+static int is_xattr_datum_unchecked(struct jffs2_sb_info *c, struct jffs2_xattr_datum *xd)
+{
+ struct jffs2_raw_node_ref *raw;
+ int rc = 0;
+
+ spin_lock(&c->erase_completion_lock);
+ for (raw=xd->node; raw != (void *)xd; raw=raw->next_in_ino) {
+ if (ref_flags(raw) == REF_UNCHECKED) {
+ rc = 1;
+ break;
+ }
+ }
+ spin_unlock(&c->erase_completion_lock);
+ return rc;
+}
+
static void unload_xattr_datum(struct jffs2_sb_info *c, struct jffs2_xattr_datum *xd)
{
/* must be called under down_write(xattr_sem) */
@@ -107,77 +122,33 @@ static void reclaim_xattr_datum(struct jffs2_sb_info *c)
before, c->xdatum_mem_usage, before - c->xdatum_mem_usage);
}
-static void delete_xattr_datum_node(struct jffs2_sb_info *c, struct jffs2_xattr_datum *xd)
-{
- /* must be called under down_write(xattr_sem) */
- struct jffs2_raw_xattr rx;
- size_t length;
- int rc;
-
- if (!xd->node) {
- JFFS2_WARNING("xdatum (xid=%u) is removed twice.\n", xd->xid);
- return;
- }
- if (jffs2_sum_active()) {
- memset(&rx, 0xff, sizeof(struct jffs2_raw_xattr));
- rc = jffs2_flash_read(c, ref_offset(xd->node),
- sizeof(struct jffs2_unknown_node),
- &length, (char *)&rx);
- if (rc || length != sizeof(struct jffs2_unknown_node)) {
- JFFS2_ERROR("jffs2_flash_read()=%d, req=%zu, read=%zu at %#08x\n",
- rc, sizeof(struct jffs2_unknown_node),
- length, ref_offset(xd->node));
- }
- rc = jffs2_flash_write(c, ref_offset(xd->node), sizeof(rx),
- &length, (char *)&rx);
- if (rc || length != sizeof(struct jffs2_raw_xattr)) {
- JFFS2_ERROR("jffs2_flash_write()=%d, req=%zu, wrote=%zu ar %#08x\n",
- rc, sizeof(rx), length, ref_offset(xd->node));
- }
- }
- spin_lock(&c->erase_completion_lock);
- xd->node->next_in_ino = NULL;
- spin_unlock(&c->erase_completion_lock);
- jffs2_mark_node_obsolete(c, xd->node);
- xd->node = NULL;
-}
-
-static void delete_xattr_datum(struct jffs2_sb_info *c, struct jffs2_xattr_datum *xd)
-{
- /* must be called under down_write(xattr_sem) */
- BUG_ON(xd->refcnt);
-
- unload_xattr_datum(c, xd);
- if (xd->node) {
- delete_xattr_datum_node(c, xd);
- xd->node = NULL;
- }
- jffs2_free_xattr_datum(xd);
-}
-
static int do_verify_xattr_datum(struct jffs2_sb_info *c, struct jffs2_xattr_datum *xd)
{
/* must be called under down_write(xattr_sem) */
struct jffs2_eraseblock *jeb;
+ struct jffs2_raw_node_ref *raw;
struct jffs2_raw_xattr rx;
size_t readlen;
- uint32_t crc, totlen;
+ uint32_t crc, offset, totlen;
int rc;
- BUG_ON(!xd->node);
- BUG_ON(ref_flags(xd->node) != REF_UNCHECKED);
+ spin_lock(&c->erase_completion_lock);
+ offset = ref_offset(xd->node);
+ if (ref_flags(xd->node) == REF_PRISTINE)
+ goto complete;
+ spin_unlock(&c->erase_completion_lock);
- rc = jffs2_flash_read(c, ref_offset(xd->node), sizeof(rx), &readlen, (char *)&rx);
+ rc = jffs2_flash_read(c, offset, sizeof(rx), &readlen, (char *)&rx);
if (rc || readlen != sizeof(rx)) {
JFFS2_WARNING("jffs2_flash_read()=%d, req=%zu, read=%zu at %#08x\n",
- rc, sizeof(rx), readlen, ref_offset(xd->node));
+ rc, sizeof(rx), readlen, offset);
return rc ? rc : -EIO;
}
crc = crc32(0, &rx, sizeof(rx) - 4);
if (crc != je32_to_cpu(rx.node_crc)) {
- if (je32_to_cpu(rx.node_crc) != 0xffffffff)
- JFFS2_ERROR("node CRC failed at %#08x, read=%#08x, calc=%#08x\n",
- ref_offset(xd->node), je32_to_cpu(rx.hdr_crc), crc);
+ JFFS2_ERROR("node CRC failed at %#08x, read=%#08x, calc=%#08x\n",
+ offset, je32_to_cpu(rx.hdr_crc), crc);
+ xd->flags |= JFFS2_XFLAGS_INVALID;
return EIO;
}
totlen = PAD(sizeof(rx) + rx.name_len + 1 + je16_to_cpu(rx.value_len));
@@ -188,11 +159,12 @@ static int do_verify_xattr_datum(struct jffs2_sb_info *c, struct jffs2_xattr_dat
|| je32_to_cpu(rx.version) != xd->version) {
JFFS2_ERROR("inconsistent xdatum at %#08x, magic=%#04x/%#04x, "
"nodetype=%#04x/%#04x, totlen=%u/%u, xid=%u/%u, version=%u/%u\n",
- ref_offset(xd->node), je16_to_cpu(rx.magic), JFFS2_MAGIC_BITMASK,
+ offset, je16_to_cpu(rx.magic), JFFS2_MAGIC_BITMASK,
je16_to_cpu(rx.nodetype), JFFS2_NODETYPE_XATTR,
je32_to_cpu(rx.totlen), totlen,
je32_to_cpu(rx.xid), xd->xid,
je32_to_cpu(rx.version), xd->version);
+ xd->flags |= JFFS2_XFLAGS_INVALID;
return EIO;
}
xd->xprefix = rx.xprefix;
@@ -200,14 +172,17 @@ static int do_verify_xattr_datum(struct jffs2_sb_info *c, struct jffs2_xattr_dat
xd->value_len = je16_to_cpu(rx.value_len);
xd->data_crc = je32_to_cpu(rx.data_crc);
- /* This JFFS2_NODETYPE_XATTR node is checked */
- jeb = &c->blocks[ref_offset(xd->node) / c->sector_size];
- totlen = PAD(je32_to_cpu(rx.totlen));
-
spin_lock(&c->erase_completion_lock);
- c->unchecked_size -= totlen; c->used_size += totlen;
- jeb->unchecked_size -= totlen; jeb->used_size += totlen;
- xd->node->flash_offset = ref_offset(xd->node) | REF_PRISTINE;
+ complete:
+ for (raw=xd->node; raw != (void *)xd; raw=raw->next_in_ino) {
+ jeb = &c->blocks[ref_offset(raw) / c->sector_size];
+ totlen = PAD(ref_totlen(c, jeb, raw));
+ if (ref_flags(raw) == REF_UNCHECKED) {
+ c->unchecked_size -= totlen; c->used_size += totlen;
+ jeb->unchecked_size -= totlen; jeb->used_size += totlen;
+ }
+ raw->flash_offset = ref_offset(raw) | ((xd->node==raw) ? REF_PRISTINE : REF_NORMAL);
+ }
spin_unlock(&c->erase_completion_lock);
/* unchecked xdatum is chained with c->xattr_unchecked */
@@ -227,7 +202,6 @@ static int do_load_xattr_datum(struct jffs2_sb_info *c, struct jffs2_xattr_datum
uint32_t crc, length;
int i, ret, retry = 0;
- BUG_ON(!xd->node);
BUG_ON(ref_flags(xd->node) != REF_PRISTINE);
BUG_ON(!list_empty(&xd->xindex));
retry:
@@ -253,6 +227,7 @@ static int do_load_xattr_datum(struct jffs2_sb_info *c, struct jffs2_xattr_datum
" at %#08x, read: 0x%08x calculated: 0x%08x\n",
ref_offset(xd->node), xd->data_crc, crc);
kfree(data);
+ xd->flags |= JFFS2_XFLAGS_INVALID;
return EIO;
}
@@ -286,16 +261,14 @@ static int load_xattr_datum(struct jffs2_sb_info *c, struct jffs2_xattr_datum *x
* rc > 0 : Unrecoverable error, this node should be deleted.
*/
int rc = 0;
- BUG_ON(xd->xname);
- if (!xd->node)
+
+ BUG_ON(xd->flags & JFFS2_XFLAGS_DEAD);
+ if (xd->xname)
+ return 0;
+ if (xd->flags & JFFS2_XFLAGS_INVALID)
return EIO;
- if (unlikely(ref_flags(xd->node) != REF_PRISTINE)) {
+ if (unlikely(is_xattr_datum_unchecked(c, xd)))
rc = do_verify_xattr_datum(c, xd);
- if (rc > 0) {
- list_del_init(&xd->xindex);
- delete_xattr_datum_node(c, xd);
- }
- }
if (!rc)
rc = do_load_xattr_datum(c, xd);
return rc;
@@ -304,7 +277,6 @@ static int load_xattr_datum(struct jffs2_sb_info *c, struct jffs2_xattr_datum *x
static int save_xattr_datum(struct jffs2_sb_info *c, struct jffs2_xattr_datum *xd)
{
/* must be called under down_write(xattr_sem) */
- struct jffs2_raw_node_ref *raw;
struct jffs2_raw_xattr rx;
struct kvec vecs[2];
size_t length;
@@ -312,14 +284,16 @@ static int save_xattr_datum(struct jffs2_sb_info *c, struct jffs2_xattr_datum *x
uint32_t phys_ofs = write_ofs(c);
BUG_ON(!xd->xname);
+ BUG_ON(xd->flags & (JFFS2_XFLAGS_DEAD|JFFS2_XFLAGS_INVALID));
vecs[0].iov_base = &rx;
- vecs[0].iov_len = PAD(sizeof(rx));
+ vecs[0].iov_len = sizeof(rx);
vecs[1].iov_base = xd->xname;
vecs[1].iov_len = xd->name_len + 1 + xd->value_len;
totlen = vecs[0].iov_len + vecs[1].iov_len;
/* Setup raw-xattr */
+ memset(&rx, 0, sizeof(rx));
rx.magic = cpu_to_je16(JFFS2_MAGIC_BITMASK);
rx.nodetype = cpu_to_je16(JFFS2_NODETYPE_XATTR);
rx.totlen = cpu_to_je32(PAD(totlen));
@@ -343,14 +317,8 @@ static int save_xattr_datum(struct jffs2_sb_info *c, struct jffs2_xattr_datum *x
return rc;
}
-
/* success */
- raw = jffs2_add_physical_node_ref(c, phys_ofs | REF_PRISTINE, PAD(totlen), NULL);
- /* FIXME */ raw->next_in_ino = (void *)xd;
-
- if (xd->node)
- delete_xattr_datum_node(c, xd);
- xd->node = raw;
+ jffs2_add_physical_node_ref(c, phys_ofs | REF_PRISTINE, PAD(totlen), (void *)xd);
dbg_xattr("success on saving xdatum (xid=%u, version=%u, xprefix=%u, xname='%s')\n",
xd->xid, xd->version, xd->xprefix, xd->xname);
@@ -377,7 +345,7 @@ static struct jffs2_xattr_datum *create_xattr_datum(struct jffs2_sb_info *c,
&& xd->value_len==xsize
&& !strcmp(xd->xname, xname)
&& !memcmp(xd->xvalue, xvalue, xsize)) {
- xd->refcnt++;
+ atomic_inc(&xd->refcnt);
return xd;
}
}
@@ -397,7 +365,7 @@ static struct jffs2_xattr_datum *create_xattr_datum(struct jffs2_sb_info *c,
strcpy(data, xname);
memcpy(data + name_len + 1, xvalue, xsize);
- xd->refcnt = 1;
+ atomic_set(&xd->refcnt, 1);
xd->xid = ++c->highest_xid;
xd->flags |= JFFS2_XFLAGS_HOT;
xd->xprefix = xprefix;
@@ -426,20 +394,36 @@ static struct jffs2_xattr_datum *create_xattr_datum(struct jffs2_sb_info *c,
return xd;
}
+static void delete_xattr_datum(struct jffs2_sb_info *c, struct jffs2_xattr_datum *xd)
+{
+ /* must be called under down_write(xattr_sem) */
+ BUG_ON(atomic_read(&xd->refcnt));
+
+ unload_xattr_datum(c, xd);
+ xd->flags |= JFFS2_XFLAGS_DEAD;
+ spin_lock(&c->erase_completion_lock);
+ if (xd->node == (void *)xd) {
+ BUG_ON(!(xd->flags & JFFS2_XFLAGS_INVALID));
+ jffs2_free_xattr_datum(xd);
+ } else {
+ list_add(&xd->xindex, &c->xattr_dead_list);
+ }
+ spin_unlock(&c->erase_completion_lock);
+ dbg_xattr("xdatum(xid=%u, version=%u) was removed.\n", xd->xid, xd->version);
+}
+
/* -------- xref related functions ------------------
* verify_xattr_ref(c, ref)
* is used to load xref information from medium. Because summary data does not
* contain xid/ino, it's necessary to verify once while mounting process.
- * delete_xattr_ref_node(c, ref)
- * is used to delete a jffs2 node is dominated by xref. When EBS is enabled,
- * it overwrites the obsolete node by myself.
- * delete_xattr_ref(c, ref)
- * is used to delete jffs2_xattr_ref object. If the reference counter of xdatum
- * is refered by this xref become 0, delete_xattr_datum() is called later.
* save_xattr_ref(c, ref)
- * is used to write xref to medium.
+ * is used to write xref to medium. If delete marker is marked, it write
+ * a delete marker of xref into medium.
* create_xattr_ref(c, ic, xd)
* is used to create a new xref and write to medium.
+ * delete_xattr_ref(c, ref)
+ * is used to delete jffs2_xattr_ref. It marks xref XREF_DELETE_MARKER,
+ * and allows GC to reclaim those physical nodes.
* jffs2_xattr_delete_inode(c, ic)
* is called to remove xrefs related to obsolete inode when inode is unlinked.
* jffs2_xattr_free_inode(c, ic)
@@ -450,25 +434,29 @@ static struct jffs2_xattr_datum *create_xattr_datum(struct jffs2_sb_info *c,
static int verify_xattr_ref(struct jffs2_sb_info *c, struct jffs2_xattr_ref *ref)
{
struct jffs2_eraseblock *jeb;
+ struct jffs2_raw_node_ref *raw;
struct jffs2_raw_xref rr;
size_t readlen;
- uint32_t crc, totlen;
+ uint32_t crc, offset, totlen;
int rc;
- BUG_ON(ref_flags(ref->node) != REF_UNCHECKED);
+ spin_lock(&c->erase_completion_lock);
+ if (ref_flags(ref->node) != REF_UNCHECKED)
+ goto complete;
+ offset = ref_offset(ref->node);
+ spin_unlock(&c->erase_completion_lock);
- rc = jffs2_flash_read(c, ref_offset(ref->node), sizeof(rr), &readlen, (char *)&rr);
+ rc = jffs2_flash_read(c, offset, sizeof(rr), &readlen, (char *)&rr);
if (rc || sizeof(rr) != readlen) {
JFFS2_WARNING("jffs2_flash_read()=%d, req=%zu, read=%zu, at %#08x\n",
- rc, sizeof(rr), readlen, ref_offset(ref->node));
+ rc, sizeof(rr), readlen, offset);
return rc ? rc : -EIO;
}
/* obsolete node */
crc = crc32(0, &rr, sizeof(rr) - 4);
if (crc != je32_to_cpu(rr.node_crc)) {
- if (je32_to_cpu(rr.node_crc) != 0xffffffff)
- JFFS2_ERROR("node CRC failed at %#08x, read=%#08x, calc=%#08x\n",
- ref_offset(ref->node), je32_to_cpu(rr.node_crc), crc);
+ JFFS2_ERROR("node CRC failed at %#08x, read=%#08x, calc=%#08x\n",
+ offset, je32_to_cpu(rr.node_crc), crc);
return EIO;
}
if (je16_to_cpu(rr.magic) != JFFS2_MAGIC_BITMASK
@@ -476,22 +464,28 @@ static int verify_xattr_ref(struct jffs2_sb_info *c, struct jffs2_xattr_ref *ref
|| je32_to_cpu(rr.totlen) != PAD(sizeof(rr))) {
JFFS2_ERROR("inconsistent xref at %#08x, magic=%#04x/%#04x, "
"nodetype=%#04x/%#04x, totlen=%u/%zu\n",
- ref_offset(ref->node), je16_to_cpu(rr.magic), JFFS2_MAGIC_BITMASK,
+ offset, je16_to_cpu(rr.magic), JFFS2_MAGIC_BITMASK,
je16_to_cpu(rr.nodetype), JFFS2_NODETYPE_XREF,
je32_to_cpu(rr.totlen), PAD(sizeof(rr)));
return EIO;
}
ref->ino = je32_to_cpu(rr.ino);
ref->xid = je32_to_cpu(rr.xid);
-
- /* fixup superblock/eraseblock info */
- jeb = &c->blocks[ref_offset(ref->node) / c->sector_size];
- totlen = PAD(sizeof(rr));
+ ref->xseqno = je32_to_cpu(rr.xseqno);
+ if (ref->xseqno > c->highest_xseqno)
+ c->highest_xseqno = (ref->xseqno & ~XREF_DELETE_MARKER);
spin_lock(&c->erase_completion_lock);
- c->unchecked_size -= totlen; c->used_size += totlen;
- jeb->unchecked_size -= totlen; jeb->used_size += totlen;
- ref->node->flash_offset = ref_offset(ref->node) | REF_PRISTINE;
+ complete:
+ for (raw=ref->node; raw != (void *)ref; raw=raw->next_in_ino) {
+ jeb = &c->blocks[ref_offset(raw) / c->sector_size];
+ totlen = PAD(ref_totlen(c, jeb, raw));
+ if (ref_flags(raw) == REF_UNCHECKED) {
+ c->unchecked_size -= totlen; c->used_size += totlen;
+ jeb->unchecked_size -= totlen; jeb->used_size += totlen;
+ }
+ raw->flash_offset = ref_offset(raw) | ((ref->node==raw) ? REF_PRISTINE : REF_NORMAL);
+ }
spin_unlock(&c->erase_completion_lock);
dbg_xattr("success on verifying xref (ino=%u, xid=%u) at %#08x\n",
@@ -499,58 +493,12 @@ static int verify_xattr_ref(struct jffs2_sb_info *c, struct jffs2_xattr_ref *ref
return 0;
}
-static void delete_xattr_ref_node(struct jffs2_sb_info *c, struct jffs2_xattr_ref *ref)
-{
- struct jffs2_raw_xref rr;
- size_t length;
- int rc;
-
- if (jffs2_sum_active()) {
- memset(&rr, 0xff, sizeof(rr));
- rc = jffs2_flash_read(c, ref_offset(ref->node),
- sizeof(struct jffs2_unknown_node),
- &length, (char *)&rr);
- if (rc || length != sizeof(struct jffs2_unknown_node)) {
- JFFS2_ERROR("jffs2_flash_read()=%d, req=%zu, read=%zu at %#08x\n",
- rc, sizeof(struct jffs2_unknown_node),
- length, ref_offset(ref->node));
- }
- rc = jffs2_flash_write(c, ref_offset(ref->node), sizeof(rr),
- &length, (char *)&rr);
- if (rc || length != sizeof(struct jffs2_raw_xref)) {
- JFFS2_ERROR("jffs2_flash_write()=%d, req=%zu, wrote=%zu at %#08x\n",
- rc, sizeof(rr), length, ref_offset(ref->node));
- }
- }
- spin_lock(&c->erase_completion_lock);
- ref->node->next_in_ino = NULL;
- spin_unlock(&c->erase_completion_lock);
- jffs2_mark_node_obsolete(c, ref->node);
- ref->node = NULL;
-}
-
-static void delete_xattr_ref(struct jffs2_sb_info *c, struct jffs2_xattr_ref *ref)
-{
- /* must be called under down_write(xattr_sem) */
- struct jffs2_xattr_datum *xd;
-
- BUG_ON(!ref->node);
- delete_xattr_ref_node(c, ref);
-
- xd = ref->xd;
- xd->refcnt--;
- if (!xd->refcnt)
- delete_xattr_datum(c, xd);
- jffs2_free_xattr_ref(ref);
-}
-
static int save_xattr_ref(struct jffs2_sb_info *c, struct jffs2_xattr_ref *ref)
{
/* must be called under down_write(xattr_sem) */
- struct jffs2_raw_node_ref *raw;
struct jffs2_raw_xref rr;
size_t length;
- uint32_t phys_ofs = write_ofs(c);
+ uint32_t xseqno, phys_ofs = write_ofs(c);
int ret;
rr.magic = cpu_to_je16(JFFS2_MAGIC_BITMASK);
@@ -558,8 +506,16 @@ static int save_xattr_ref(struct jffs2_sb_info *c, struct jffs2_xattr_ref *ref)
rr.totlen = cpu_to_je32(PAD(sizeof(rr)));
rr.hdr_crc = cpu_to_je32(crc32(0, &rr, sizeof(struct jffs2_unknown_node) - 4));
- rr.ino = cpu_to_je32(ref->ic->ino);
- rr.xid = cpu_to_je32(ref->xd->xid);
+ xseqno = (c->highest_xseqno += 2);
+ if (is_xattr_ref_dead(ref)) {
+ xseqno |= XREF_DELETE_MARKER;
+ rr.ino = cpu_to_je32(ref->ino);
+ rr.xid = cpu_to_je32(ref->xid);
+ } else {
+ rr.ino = cpu_to_je32(ref->ic->ino);
+ rr.xid = cpu_to_je32(ref->xd->xid);
+ }
+ rr.xseqno = cpu_to_je32(xseqno);
rr.node_crc = cpu_to_je32(crc32(0, &rr, sizeof(rr) - 4));
ret = jffs2_flash_write(c, phys_ofs, sizeof(rr), &length, (char *)&rr);
@@ -572,12 +528,9 @@ static int save_xattr_ref(struct jffs2_sb_info *c, struct jffs2_xattr_ref *ref)
return ret;
}
-
- raw = jffs2_add_physical_node_ref(c, phys_ofs | REF_PRISTINE, PAD(sizeof(rr)), NULL);
- /* FIXME */ raw->next_in_ino = (void *)ref;
- if (ref->node)
- delete_xattr_ref_node(c, ref);
- ref->node = raw;
+ /* success */
+ ref->xseqno = xseqno;
+ jffs2_add_physical_node_ref(c, phys_ofs | REF_PRISTINE, PAD(sizeof(rr)), (void *)ref);
dbg_xattr("success on saving xref (ino=%u, xid=%u)\n", ref->ic->ino, ref->xd->xid);
@@ -610,6 +563,27 @@ static struct jffs2_xattr_ref *create_xattr_ref(struct jffs2_sb_info *c, struct
return ref; /* success */
}
+static void delete_xattr_ref(struct jffs2_sb_info *c, struct jffs2_xattr_ref *ref)
+{
+ /* must be called under down_write(xattr_sem) */
+ struct jffs2_xattr_datum *xd;
+
+ xd = ref->xd;
+ ref->xseqno |= XREF_DELETE_MARKER;
+ ref->ino = ref->ic->ino;
+ ref->xid = ref->xd->xid;
+ spin_lock(&c->erase_completion_lock);
+ ref->next = c->xref_dead_list;
+ c->xref_dead_list = ref;
+ spin_unlock(&c->erase_completion_lock);
+
+ dbg_xattr("xref(ino=%u, xid=%u, xseqno=%u) was removed.\n",
+ ref->ino, ref->xid, ref->xseqno);
+
+ if (atomic_dec_and_test(&xd->refcnt))
+ delete_xattr_datum(c, xd);
+}
+
void jffs2_xattr_delete_inode(struct jffs2_sb_info *c, struct jffs2_inode_cache *ic)
{
/* It's called from jffs2_clear_inode() on inode removing.
@@ -638,8 +612,7 @@ void jffs2_xattr_free_inode(struct jffs2_sb_info *c, struct jffs2_inode_cache *i
for (ref = ic->xref; ref; ref = _ref) {
_ref = ref->next;
xd = ref->xd;
- xd->refcnt--;
- if (!xd->refcnt) {
+ if (atomic_dec_and_test(&xd->refcnt)) {
unload_xattr_datum(c, xd);
jffs2_free_xattr_datum(xd);
}
@@ -655,7 +628,7 @@ static int check_xattr_ref_inode(struct jffs2_sb_info *c, struct jffs2_inode_cac
* duplicate name/value pairs. If duplicate name/value pair would be found,
* one will be removed.
*/
- struct jffs2_xattr_ref *ref, *cmp, **pref;
+ struct jffs2_xattr_ref *ref, *cmp, **pref, **pcmp;
int rc = 0;
if (likely(ic->flags & INO_FLAGS_XATTR_CHECKED))
@@ -673,13 +646,13 @@ static int check_xattr_ref_inode(struct jffs2_sb_info *c, struct jffs2_inode_cac
} else if (unlikely(rc < 0))
goto out;
}
- for (cmp=ref->next, pref=&ref->next; cmp; pref=&cmp->next, cmp=cmp->next) {
+ for (cmp=ref->next, pcmp=&ref->next; cmp; pcmp=&cmp->next, cmp=cmp->next) {
if (!cmp->xd->xname) {
ref->xd->flags |= JFFS2_XFLAGS_BIND;
rc = load_xattr_datum(c, cmp->xd);
ref->xd->flags &= ~JFFS2_XFLAGS_BIND;
if (unlikely(rc > 0)) {
- *pref = cmp->next;
+ *pcmp = cmp->next;
delete_xattr_ref(c, cmp);
goto retry;
} else if (unlikely(rc < 0))
@@ -687,8 +660,13 @@ static int check_xattr_ref_inode(struct jffs2_sb_info *c, struct jffs2_inode_cac
}
if (ref->xd->xprefix == cmp->xd->xprefix
&& !strcmp(ref->xd->xname, cmp->xd->xname)) {
- *pref = cmp->next;
- delete_xattr_ref(c, cmp);
+ if (ref->xseqno > cmp->xseqno) {
+ *pcmp = cmp->next;
+ delete_xattr_ref(c, cmp);
+ } else {
+ *pref = ref->next;
+ delete_xattr_ref(c, ref);
+ }
goto retry;
}
}
@@ -719,9 +697,13 @@ void jffs2_init_xattr_subsystem(struct jffs2_sb_info *c)
for (i=0; i < XATTRINDEX_HASHSIZE; i++)
INIT_LIST_HEAD(&c->xattrindex[i]);
INIT_LIST_HEAD(&c->xattr_unchecked);
+ INIT_LIST_HEAD(&c->xattr_dead_list);
+ c->xref_dead_list = NULL;
c->xref_temp = NULL;
init_rwsem(&c->xattr_sem);
+ c->highest_xid = 0;
+ c->highest_xseqno = 0;
c->xdatum_mem_usage = 0;
c->xdatum_mem_threshold = 32 * 1024; /* Default 32KB */
}
@@ -751,7 +733,11 @@ void jffs2_clear_xattr_subsystem(struct jffs2_sb_info *c)
_ref = ref->next;
jffs2_free_xattr_ref(ref);
}
- c->xref_temp = NULL;
+
+ for (ref=c->xref_dead_list; ref; ref = _ref) {
+ _ref = ref->next;
+ jffs2_free_xattr_ref(ref);
+ }
for (i=0; i < XATTRINDEX_HASHSIZE; i++) {
list_for_each_entry_safe(xd, _xd, &c->xattrindex[i], xindex) {
@@ -761,100 +747,143 @@ void jffs2_clear_xattr_subsystem(struct jffs2_sb_info *c)
jffs2_free_xattr_datum(xd);
}
}
+
+ list_for_each_entry_safe(xd, _xd, &c->xattr_dead_list, xindex) {
+ list_del(&xd->xindex);
+ jffs2_free_xattr_datum(xd);
+ }
}
+#define XREF_TMPHASH_SIZE (128)
void jffs2_build_xattr_subsystem(struct jffs2_sb_info *c)
{
struct jffs2_xattr_ref *ref, *_ref;
+ struct jffs2_xattr_ref *xref_tmphash[XREF_TMPHASH_SIZE];
struct jffs2_xattr_datum *xd, *_xd;
struct jffs2_inode_cache *ic;
- int i, xdatum_count =0, xdatum_unchecked_count = 0, xref_count = 0;
+ struct jffs2_raw_node_ref *raw;
+ int i, xdatum_count = 0, xdatum_unchecked_count = 0, xref_count = 0;
+ int xdatum_orphan_count = 0, xref_orphan_count = 0, xref_dead_count = 0;
BUG_ON(!(c->flags & JFFS2_SB_FLAG_BUILDING));
- /* Phase.1 */
+ /* Phase.1 : Merge same xref */
+ for (i=0; i < XREF_TMPHASH_SIZE; i++)
+ xref_tmphash[i] = NULL;
for (ref=c->xref_temp; ref; ref=_ref) {
+ struct jffs2_xattr_ref *tmp;
+
_ref = ref->next;
- /* checking REF_UNCHECKED nodes */
if (ref_flags(ref->node) != REF_PRISTINE) {
if (verify_xattr_ref(c, ref)) {
- delete_xattr_ref_node(c, ref);
+ BUG_ON(ref->node->next_in_ino != (void *)ref);
+ ref->node->next_in_ino = NULL;
+ jffs2_mark_node_obsolete(c, ref->node);
jffs2_free_xattr_ref(ref);
continue;
}
}
- /* At this point, ref->xid and ref->ino contain XID and inode number.
- ref->xd and ref->ic are not valid yet. */
- xd = jffs2_find_xattr_datum(c, ref->xid);
- ic = jffs2_get_ino_cache(c, ref->ino);
- if (!xd || !ic) {
- if (ref_flags(ref->node) != REF_UNCHECKED)
- JFFS2_WARNING("xref(ino=%u, xid=%u) is orphan. \n",
- ref->ino, ref->xid);
- delete_xattr_ref_node(c, ref);
+
+ i = (ref->ino ^ ref->xid) % XREF_TMPHASH_SIZE;
+ for (tmp=xref_tmphash[i]; tmp; tmp=tmp->next) {
+ if (tmp->ino == ref->ino && tmp->xid == ref->xid)
+ break;
+ }
+ if (tmp) {
+ raw = ref->node;
+ if (ref->xseqno > tmp->xseqno) {
+ tmp->xseqno = ref->xseqno;
+ raw->next_in_ino = tmp->node;
+ tmp->node = raw;
+ } else {
+ raw->next_in_ino = tmp->node->next_in_ino;
+ tmp->node->next_in_ino = raw;
+ }
jffs2_free_xattr_ref(ref);
continue;
+ } else {
+ ref->next = xref_tmphash[i];
+ xref_tmphash[i] = ref;
}
- ref->xd = xd;
- ref->ic = ic;
- xd->refcnt++;
- ref->next = ic->xref;
- ic->xref = ref;
- xref_count++;
}
c->xref_temp = NULL;
- /* After this, ref->xid/ino are NEVER used. */
- /* Phase.2 */
+ /* Phase.2 : Bind xref with inode_cache and xattr_datum */
+ for (i=0; i < XREF_TMPHASH_SIZE; i++) {
+ for (ref=xref_tmphash[i]; ref; ref=_ref) {
+ xref_count++;
+ _ref = ref->next;
+ if (is_xattr_ref_dead(ref)) {
+ ref->next = c->xref_dead_list;
+ c->xref_dead_list = ref;
+ xref_dead_count++;
+ continue;
+ }
+ /* At this point, ref->xid and ref->ino contain XID and inode number.
+ ref->xd and ref->ic are not valid yet. */
+ xd = jffs2_find_xattr_datum(c, ref->xid);
+ ic = jffs2_get_ino_cache(c, ref->ino);
+ if (!xd || !ic) {
+ dbg_xattr("xref(ino=%u, xid=%u, xseqno=%u) is orphan.\n",
+ ref->ino, ref->xid, ref->xseqno);
+ ref->xseqno |= XREF_DELETE_MARKER;
+ ref->next = c->xref_dead_list;
+ c->xref_dead_list = ref;
+ xref_orphan_count++;
+ continue;
+ }
+ ref->xd = xd;
+ ref->ic = ic;
+ atomic_inc(&xd->refcnt);
+ ref->next = ic->xref;
+ ic->xref = ref;
+ }
+ }
+
+ /* Phase.3 : Link unchecked xdatum to xattr_unchecked list */
for (i=0; i < XATTRINDEX_HASHSIZE; i++) {
list_for_each_entry_safe(xd, _xd, &c->xattrindex[i], xindex) {
+ xdatum_count++;
list_del_init(&xd->xindex);
- if (!xd->refcnt) {
- if (ref_flags(xd->node) != REF_UNCHECKED)
- JFFS2_WARNING("orphan xdatum(xid=%u, version=%u) at %#08x\n",
- xd->xid, xd->version, ref_offset(xd->node));
- delete_xattr_datum(c, xd);
+ if (!atomic_read(&xd->refcnt)) {
+ dbg_xattr("xdatum(xid=%u, version=%u) is orphan.\n",
+ xd->xid, xd->version);
+ xd->flags |= JFFS2_XFLAGS_DEAD;
+ list_add(&xd->xindex, &c->xattr_unchecked);
+ xdatum_orphan_count++;
continue;
}
- if (ref_flags(xd->node) != REF_PRISTINE) {
- dbg_xattr("unchecked xdatum(xid=%u) at %#08x\n",
- xd->xid, ref_offset(xd->node));
+ if (is_xattr_datum_unchecked(c, xd)) {
+ dbg_xattr("unchecked xdatum(xid=%u, version=%u)\n",
+ xd->xid, xd->version);
list_add(&xd->xindex, &c->xattr_unchecked);
xdatum_unchecked_count++;
}
- xdatum_count++;
}
}
/* build complete */
- JFFS2_NOTICE("complete building xattr subsystem, %u of xdatum (%u unchecked) and "
- "%u of xref found.\n", xdatum_count, xdatum_unchecked_count, xref_count);
+ JFFS2_NOTICE("complete building xattr subsystem, %u of xdatum"
+ " (%u unchecked, %u orphan) and "
+ "%u of xref (%u dead, %u orphan) found.\n",
+ xdatum_count, xdatum_unchecked_count, xdatum_orphan_count,
+ xref_count, xref_dead_count, xref_orphan_count);
}
struct jffs2_xattr_datum *jffs2_setup_xattr_datum(struct jffs2_sb_info *c,
uint32_t xid, uint32_t version)
{
- struct jffs2_xattr_datum *xd, *_xd;
+ struct jffs2_xattr_datum *xd;
- _xd = jffs2_find_xattr_datum(c, xid);
- if (_xd) {
- dbg_xattr("duplicate xdatum (xid=%u, version=%u/%u) at %#08x\n",
- xid, version, _xd->version, ref_offset(_xd->node));
- if (version < _xd->version)
- return ERR_PTR(-EEXIST);
- }
- xd = jffs2_alloc_xattr_datum();
- if (!xd)
- return ERR_PTR(-ENOMEM);
- xd->xid = xid;
- xd->version = version;
- if (xd->xid > c->highest_xid)
- c->highest_xid = xd->xid;
- list_add_tail(&xd->xindex, &c->xattrindex[xid % XATTRINDEX_HASHSIZE]);
-
- if (_xd) {
- list_del_init(&_xd->xindex);
- delete_xattr_datum_node(c, _xd);
- jffs2_free_xattr_datum(_xd);
+ xd = jffs2_find_xattr_datum(c, xid);
+ if (!xd) {
+ xd = jffs2_alloc_xattr_datum();
+ if (!xd)
+ return ERR_PTR(-ENOMEM);
+ xd->xid = xid;
+ xd->version = version;
+ if (xd->xid > c->highest_xid)
+ c->highest_xid = xd->xid;
+ list_add_tail(&xd->xindex, &c->xattrindex[xid % XATTRINDEX_HASHSIZE]);
}
return xd;
}
@@ -1080,9 +1109,23 @@ int do_jffs2_setxattr(struct inode *inode, int xprefix, const char *xname,
goto out;
}
if (!buffer) {
- *pref = ref->next;
- delete_xattr_ref(c, ref);
- rc = 0;
+ ref->ino = ic->ino;
+ ref->xid = xd->xid;
+ ref->xseqno |= XREF_DELETE_MARKER;
+ rc = save_xattr_ref(c, ref);
+ if (!rc) {
+ *pref = ref->next;
+ spin_lock(&c->erase_completion_lock);
+ ref->next = c->xref_dead_list;
+ c->xref_dead_list = ref;
+ spin_unlock(&c->erase_completion_lock);
+ if (atomic_dec_and_test(&xd->refcnt))
+ delete_xattr_datum(c, xd);
+ } else {
+ ref->ic = ic;
+ ref->xd = xd;
+ ref->xseqno &= ~XREF_DELETE_MARKER;
+ }
goto out;
}
goto found;
@@ -1094,7 +1137,7 @@ int do_jffs2_setxattr(struct inode *inode, int xprefix, const char *xname,
goto out;
}
if (!buffer) {
- rc = -EINVAL;
+ rc = -ENODATA;
goto out;
}
found:
@@ -1110,16 +1153,14 @@ int do_jffs2_setxattr(struct inode *inode, int xprefix, const char *xname,
request = PAD(sizeof(struct jffs2_raw_xref));
rc = jffs2_reserve_space(c, request, &length,
ALLOC_NORMAL, JFFS2_SUMMARY_XREF_SIZE);
+ down_write(&c->xattr_sem);
if (rc) {
JFFS2_WARNING("jffs2_reserve_space()=%d, request=%u\n", rc, request);
- down_write(&c->xattr_sem);
- xd->refcnt--;
- if (!xd->refcnt)
+ if (atomic_dec_and_test(&xd->refcnt))
delete_xattr_datum(c, xd);
up_write(&c->xattr_sem);
return rc;
}
- down_write(&c->xattr_sem);
if (ref)
*pref = ref->next;
newref = create_xattr_ref(c, ic, xd);
@@ -1129,8 +1170,7 @@ int do_jffs2_setxattr(struct inode *inode, int xprefix, const char *xname,
ic->xref = ref;
}
rc = PTR_ERR(newref);
- xd->refcnt--;
- if (!xd->refcnt)
+ if (atomic_dec_and_test(&xd->refcnt))
delete_xattr_datum(c, xd);
} else if (ref) {
delete_xattr_ref(c, ref);
@@ -1142,38 +1182,40 @@ int do_jffs2_setxattr(struct inode *inode, int xprefix, const char *xname,
}
/* -------- garbage collector functions -------------
- * jffs2_garbage_collect_xattr_datum(c, xd)
+ * jffs2_garbage_collect_xattr_datum(c, xd, raw)
* is used to move xdatum into new node.
- * jffs2_garbage_collect_xattr_ref(c, ref)
+ * jffs2_garbage_collect_xattr_ref(c, ref, raw)
* is used to move xref into new node.
* jffs2_verify_xattr(c)
* is used to call do_verify_xattr_datum() before garbage collecting.
+ * jffs2_release_xattr_datum(c, xd)
+ * is used to release an in-memory object of xdatum.
+ * jffs2_release_xattr_ref(c, ref)
+ * is used to release an in-memory object of xref.
* -------------------------------------------------- */
-int jffs2_garbage_collect_xattr_datum(struct jffs2_sb_info *c, struct jffs2_xattr_datum *xd)
+int jffs2_garbage_collect_xattr_datum(struct jffs2_sb_info *c, struct jffs2_xattr_datum *xd,
+ struct jffs2_raw_node_ref *raw)
{
uint32_t totlen, length, old_ofs;
- int rc = -EINVAL;
+ int rc = 0;
down_write(&c->xattr_sem);
- BUG_ON(!xd->node);
-
- old_ofs = ref_offset(xd->node);
- totlen = ref_totlen(c, c->gcblock, xd->node);
- if (totlen < sizeof(struct jffs2_raw_xattr))
+ if (xd->node != raw)
+ goto out;
+ if (xd->flags & (JFFS2_XFLAGS_DEAD|JFFS2_XFLAGS_INVALID))
goto out;
- if (!xd->xname) {
- rc = load_xattr_datum(c, xd);
- if (unlikely(rc > 0)) {
- delete_xattr_datum_node(c, xd);
- rc = 0;
- goto out;
- } else if (unlikely(rc < 0))
- goto out;
+ rc = load_xattr_datum(c, xd);
+ if (unlikely(rc)) {
+ rc = (rc > 0) ? 0 : rc;
+ goto out;
}
+ old_ofs = ref_offset(xd->node);
+ totlen = PAD(sizeof(struct jffs2_raw_xattr)
+ + xd->name_len + 1 + xd->value_len);
rc = jffs2_reserve_space_gc(c, totlen, &length, JFFS2_SUMMARY_XATTR_SIZE);
- if (rc || length < totlen) {
- JFFS2_WARNING("jffs2_reserve_space()=%d, request=%u\n", rc, totlen);
+ if (rc) {
+ JFFS2_WARNING("jffs2_reserve_space_gc()=%d, request=%u\n", rc, totlen);
rc = rc ? rc : -EBADFD;
goto out;
}
@@ -1182,27 +1224,32 @@ int jffs2_garbage_collect_xattr_datum(struct jffs2_sb_info *c, struct jffs2_xatt
dbg_xattr("xdatum (xid=%u, version=%u) GC'ed from %#08x to %08x\n",
xd->xid, xd->version, old_ofs, ref_offset(xd->node));
out:
+ if (!rc)
+ jffs2_mark_node_obsolete(c, raw);
up_write(&c->xattr_sem);
return rc;
}
-
-int jffs2_garbage_collect_xattr_ref(struct jffs2_sb_info *c, struct jffs2_xattr_ref *ref)
+int jffs2_garbage_collect_xattr_ref(struct jffs2_sb_info *c, struct jffs2_xattr_ref *ref,
+ struct jffs2_raw_node_ref *raw)
{
uint32_t totlen, length, old_ofs;
- int rc = -EINVAL;
+ int rc = 0;
down_write(&c->xattr_sem);
BUG_ON(!ref->node);
+ if (ref->node != raw)
+ goto out;
+ if (is_xattr_ref_dead(ref) && (raw->next_in_ino == (void *)ref))
+ goto out;
+
old_ofs = ref_offset(ref->node);
totlen = ref_totlen(c, c->gcblock, ref->node);
- if (totlen != sizeof(struct jffs2_raw_xref))
- goto out;
rc = jffs2_reserve_space_gc(c, totlen, &length, JFFS2_SUMMARY_XREF_SIZE);
- if (rc || length < totlen) {
- JFFS2_WARNING("%s: jffs2_reserve_space() = %d, request = %u\n",
+ if (rc) {
+ JFFS2_WARNING("%s: jffs2_reserve_space_gc() = %d, request = %u\n",
__FUNCTION__, rc, totlen);
rc = rc ? rc : -EBADFD;
goto out;
@@ -1212,6 +1259,8 @@ int jffs2_garbage_collect_xattr_ref(struct jffs2_sb_info *c, struct jffs2_xattr_
dbg_xattr("xref (ino=%u, xid=%u) GC'ed from %#08x to %08x\n",
ref->ic->ino, ref->xd->xid, old_ofs, ref_offset(ref->node));
out:
+ if (!rc)
+ jffs2_mark_node_obsolete(c, raw);
up_write(&c->xattr_sem);
return rc;
}
@@ -1219,20 +1268,59 @@ int jffs2_garbage_collect_xattr_ref(struct jffs2_sb_info *c, struct jffs2_xattr_
int jffs2_verify_xattr(struct jffs2_sb_info *c)
{
struct jffs2_xattr_datum *xd, *_xd;
+ struct jffs2_eraseblock *jeb;
+ struct jffs2_raw_node_ref *raw;
+ uint32_t totlen;
int rc;
down_write(&c->xattr_sem);
list_for_each_entry_safe(xd, _xd, &c->xattr_unchecked, xindex) {
rc = do_verify_xattr_datum(c, xd);
- if (rc == 0) {
- list_del_init(&xd->xindex);
- break;
- } else if (rc > 0) {
- list_del_init(&xd->xindex);
- delete_xattr_datum_node(c, xd);
+ if (rc < 0)
+ continue;
+ list_del_init(&xd->xindex);
+ spin_lock(&c->erase_completion_lock);
+ for (raw=xd->node; raw != (void *)xd; raw=raw->next_in_ino) {
+ if (ref_flags(raw) != REF_UNCHECKED)
+ continue;
+ jeb = &c->blocks[ref_offset(raw) / c->sector_size];
+ totlen = PAD(ref_totlen(c, jeb, raw));
+ c->unchecked_size -= totlen; c->used_size += totlen;
+ jeb->unchecked_size -= totlen; jeb->used_size += totlen;
+ raw->flash_offset = ref_offset(raw)
+ | ((xd->node == (void *)raw) ? REF_PRISTINE : REF_NORMAL);
}
+ if (xd->flags & JFFS2_XFLAGS_DEAD)
+ list_add(&xd->xindex, &c->xattr_dead_list);
+ spin_unlock(&c->erase_completion_lock);
}
up_write(&c->xattr_sem);
-
return list_empty(&c->xattr_unchecked) ? 1 : 0;
}
+
+void jffs2_release_xattr_datum(struct jffs2_sb_info *c, struct jffs2_xattr_datum *xd)
+{
+ /* must be called under spin_lock(&c->erase_completion_lock) */
+ if (atomic_read(&xd->refcnt) || xd->node != (void *)xd)
+ return;
+
+ list_del(&xd->xindex);
+ jffs2_free_xattr_datum(xd);
+}
+
+void jffs2_release_xattr_ref(struct jffs2_sb_info *c, struct jffs2_xattr_ref *ref)
+{
+ /* must be called under spin_lock(&c->erase_completion_lock) */
+ struct jffs2_xattr_ref *tmp, **ptmp;
+
+ if (ref->node != (void *)ref)
+ return;
+
+ for (tmp=c->xref_dead_list, ptmp=&c->xref_dead_list; tmp; ptmp=&tmp->next, tmp=tmp->next) {
+ if (ref == tmp) {
+ *ptmp = tmp->next;
+ break;
+ }
+ }
+ jffs2_free_xattr_ref(ref);
+}
diff --git a/fs/jffs2/xattr.h b/fs/jffs2/xattr.h
index 2c199856c582..06a5c69dcf8b 100644
--- a/fs/jffs2/xattr.h
+++ b/fs/jffs2/xattr.h
@@ -16,6 +16,8 @@
#define JFFS2_XFLAGS_HOT (0x01) /* This datum is HOT */
#define JFFS2_XFLAGS_BIND (0x02) /* This datum is not reclaimed */
+#define JFFS2_XFLAGS_DEAD (0x40) /* This datum is already dead */
+#define JFFS2_XFLAGS_INVALID (0x80) /* This datum contains crc error */
struct jffs2_xattr_datum
{
@@ -23,10 +25,10 @@ struct jffs2_xattr_datum
struct jffs2_raw_node_ref *node;
uint8_t class;
uint8_t flags;
- uint16_t xprefix; /* see JFFS2_XATTR_PREFIX_* */
+ uint16_t xprefix; /* see JFFS2_XATTR_PREFIX_* */
struct list_head xindex; /* chained from c->xattrindex[n] */
- uint32_t refcnt; /* # of xattr_ref refers this */
+ atomic_t refcnt; /* # of xattr_ref refers this */
uint32_t xid;
uint32_t version;
@@ -47,6 +49,7 @@ struct jffs2_xattr_ref
uint8_t flags; /* Currently unused */
u16 unused;
+ uint32_t xseqno;
union {
struct jffs2_inode_cache *ic; /* reference to jffs2_inode_cache */
uint32_t ino; /* only used in scanning/building */
@@ -58,6 +61,12 @@ struct jffs2_xattr_ref
struct jffs2_xattr_ref *next; /* chained from ic->xref_list */
};
+#define XREF_DELETE_MARKER (0x00000001)
+static inline int is_xattr_ref_dead(struct jffs2_xattr_ref *ref)
+{
+ return ((ref->xseqno & XREF_DELETE_MARKER) != 0);
+}
+
#ifdef CONFIG_JFFS2_FS_XATTR
extern void jffs2_init_xattr_subsystem(struct jffs2_sb_info *c);
@@ -70,9 +79,13 @@ extern struct jffs2_xattr_datum *jffs2_setup_xattr_datum(struct jffs2_sb_info *c
extern void jffs2_xattr_delete_inode(struct jffs2_sb_info *c, struct jffs2_inode_cache *ic);
extern void jffs2_xattr_free_inode(struct jffs2_sb_info *c, struct jffs2_inode_cache *ic);
-extern int jffs2_garbage_collect_xattr_datum(struct jffs2_sb_info *c, struct jffs2_xattr_datum *xd);
-extern int jffs2_garbage_collect_xattr_ref(struct jffs2_sb_info *c, struct jffs2_xattr_ref *ref);
+extern int jffs2_garbage_collect_xattr_datum(struct jffs2_sb_info *c, struct jffs2_xattr_datum *xd,
+ struct jffs2_raw_node_ref *raw);
+extern int jffs2_garbage_collect_xattr_ref(struct jffs2_sb_info *c, struct jffs2_xattr_ref *ref,
+ struct jffs2_raw_node_ref *raw);
extern int jffs2_verify_xattr(struct jffs2_sb_info *c);
+extern void jffs2_release_xattr_datum(struct jffs2_sb_info *c, struct jffs2_xattr_datum *xd);
+extern void jffs2_release_xattr_ref(struct jffs2_sb_info *c, struct jffs2_xattr_ref *ref);
extern int do_jffs2_getxattr(struct inode *inode, int xprefix, const char *xname,
char *buffer, size_t size);
diff --git a/fs/jfs/inode.c b/fs/jfs/inode.c
index 04eb78f1252e..43e3f566aad6 100644
--- a/fs/jfs/inode.c
+++ b/fs/jfs/inode.c
@@ -305,7 +305,7 @@ static ssize_t jfs_direct_IO(int rw, struct kiocb *iocb,
offset, nr_segs, jfs_get_block, NULL);
}
-struct address_space_operations jfs_aops = {
+const struct address_space_operations jfs_aops = {
.readpage = jfs_readpage,
.readpages = jfs_readpages,
.writepage = jfs_writepage,
diff --git a/fs/jfs/jfs_inode.h b/fs/jfs/jfs_inode.h
index c30072674464..b5c7da6190dc 100644
--- a/fs/jfs/jfs_inode.h
+++ b/fs/jfs/jfs_inode.h
@@ -33,7 +33,7 @@ extern void jfs_free_zero_link(struct inode *);
extern struct dentry *jfs_get_parent(struct dentry *dentry);
extern void jfs_set_inode_flags(struct inode *);
-extern struct address_space_operations jfs_aops;
+extern const struct address_space_operations jfs_aops;
extern struct inode_operations jfs_dir_inode_operations;
extern const struct file_operations jfs_dir_operations;
extern struct inode_operations jfs_file_inode_operations;
diff --git a/fs/jfs/jfs_metapage.c b/fs/jfs/jfs_metapage.c
index 7f6e88039700..e1e0a6e6ebdf 100644
--- a/fs/jfs/jfs_metapage.c
+++ b/fs/jfs/jfs_metapage.c
@@ -577,7 +577,7 @@ static void metapage_invalidatepage(struct page *page, unsigned long offset)
metapage_releasepage(page, 0);
}
-struct address_space_operations jfs_metapage_aops = {
+const struct address_space_operations jfs_metapage_aops = {
.readpage = metapage_readpage,
.writepage = metapage_writepage,
.sync_page = block_sync_page,
diff --git a/fs/jfs/jfs_metapage.h b/fs/jfs/jfs_metapage.h
index f0b7d3282b07..d17a3290f5aa 100644
--- a/fs/jfs/jfs_metapage.h
+++ b/fs/jfs/jfs_metapage.h
@@ -139,7 +139,7 @@ static inline void metapage_homeok(struct metapage *mp)
put_metapage(mp);
}
-extern struct address_space_operations jfs_metapage_aops;
+extern const struct address_space_operations jfs_metapage_aops;
/*
* This routines invalidate all pages for an extent.
diff --git a/fs/minix/inode.c b/fs/minix/inode.c
index a6fb509b7341..9ea91c5eeb7b 100644
--- a/fs/minix/inode.c
+++ b/fs/minix/inode.c
@@ -335,7 +335,7 @@ static sector_t minix_bmap(struct address_space *mapping, sector_t block)
{
return generic_block_bmap(mapping,block,minix_get_block);
}
-static struct address_space_operations minix_aops = {
+static const struct address_space_operations minix_aops = {
.readpage = minix_readpage,
.writepage = minix_writepage,
.sync_page = block_sync_page,
diff --git a/fs/ncpfs/inode.c b/fs/ncpfs/inode.c
index 90d2ea28f333..6c51c1198464 100644
--- a/fs/ncpfs/inode.c
+++ b/fs/ncpfs/inode.c
@@ -105,7 +105,7 @@ static struct super_operations ncp_sops =
extern struct dentry_operations ncp_root_dentry_operations;
#if defined(CONFIG_NCPFS_EXTRAS) || defined(CONFIG_NCPFS_NFS_NS)
-extern struct address_space_operations ncp_symlink_aops;
+extern const struct address_space_operations ncp_symlink_aops;
extern int ncp_symlink(struct inode*, struct dentry*, const char*);
#endif
diff --git a/fs/ncpfs/symlink.c b/fs/ncpfs/symlink.c
index e935f1b34bc2..f76b1392a012 100644
--- a/fs/ncpfs/symlink.c
+++ b/fs/ncpfs/symlink.c
@@ -99,7 +99,7 @@ fail:
/*
* symlinks can't do much...
*/
-struct address_space_operations ncp_symlink_aops = {
+const struct address_space_operations ncp_symlink_aops = {
.readpage = ncp_symlink_readpage,
};
diff --git a/fs/nfs/direct.c b/fs/nfs/direct.c
index 402005c35ab3..8ca9707be6c9 100644
--- a/fs/nfs/direct.c
+++ b/fs/nfs/direct.c
@@ -909,7 +909,7 @@ int __init nfs_init_directcache(void)
* nfs_destroy_directcache - destroy the slab cache for nfs_direct_req structures
*
*/
-void __exit nfs_destroy_directcache(void)
+void nfs_destroy_directcache(void)
{
if (kmem_cache_destroy(nfs_direct_cachep))
printk(KERN_INFO "nfs_direct_cache: not all structures were freed\n");
diff --git a/fs/nfs/file.c b/fs/nfs/file.c
index add289138836..cc2b874ad5a4 100644
--- a/fs/nfs/file.c
+++ b/fs/nfs/file.c
@@ -315,7 +315,7 @@ static int nfs_release_page(struct page *page, gfp_t gfp)
return !nfs_wb_page(page->mapping->host, page);
}
-struct address_space_operations nfs_file_aops = {
+const struct address_space_operations nfs_file_aops = {
.readpage = nfs_readpage,
.readpages = nfs_readpages,
.set_page_dirty = __set_page_dirty_nobuffers,
diff --git a/fs/nfs/inode.c b/fs/nfs/inode.c
index 51bc88b662fe..c5b916605fb0 100644
--- a/fs/nfs/inode.c
+++ b/fs/nfs/inode.c
@@ -1132,7 +1132,7 @@ static int __init nfs_init_inodecache(void)
return 0;
}
-static void __exit nfs_destroy_inodecache(void)
+static void nfs_destroy_inodecache(void)
{
if (kmem_cache_destroy(nfs_inode_cachep))
printk(KERN_INFO "nfs_inode_cache: not all structures were freed\n");
diff --git a/fs/nfs/internal.h b/fs/nfs/internal.h
index bd2815e2dec1..4fe51c1292bb 100644
--- a/fs/nfs/internal.h
+++ b/fs/nfs/internal.h
@@ -31,15 +31,15 @@ extern struct svc_version nfs4_callback_version1;
/* pagelist.c */
extern int __init nfs_init_nfspagecache(void);
-extern void __exit nfs_destroy_nfspagecache(void);
+extern void nfs_destroy_nfspagecache(void);
extern int __init nfs_init_readpagecache(void);
-extern void __exit nfs_destroy_readpagecache(void);
+extern void nfs_destroy_readpagecache(void);
extern int __init nfs_init_writepagecache(void);
-extern void __exit nfs_destroy_writepagecache(void);
+extern void nfs_destroy_writepagecache(void);
#ifdef CONFIG_NFS_DIRECTIO
extern int __init nfs_init_directcache(void);
-extern void __exit nfs_destroy_directcache(void);
+extern void nfs_destroy_directcache(void);
#else
#define nfs_init_directcache() (0)
#define nfs_destroy_directcache() do {} while(0)
diff --git a/fs/nfs/pagelist.c b/fs/nfs/pagelist.c
index ef9429643ebc..d89f6fb3b3a3 100644
--- a/fs/nfs/pagelist.c
+++ b/fs/nfs/pagelist.c
@@ -390,7 +390,7 @@ int __init nfs_init_nfspagecache(void)
return 0;
}
-void __exit nfs_destroy_nfspagecache(void)
+void nfs_destroy_nfspagecache(void)
{
if (kmem_cache_destroy(nfs_page_cachep))
printk(KERN_INFO "nfs_page: not all structures were freed\n");
diff --git a/fs/nfs/read.c b/fs/nfs/read.c
index 41c2ffee24f5..32cf3773af0c 100644
--- a/fs/nfs/read.c
+++ b/fs/nfs/read.c
@@ -711,7 +711,7 @@ int __init nfs_init_readpagecache(void)
return 0;
}
-void __exit nfs_destroy_readpagecache(void)
+void nfs_destroy_readpagecache(void)
{
mempool_destroy(nfs_rdata_mempool);
if (kmem_cache_destroy(nfs_rdata_cachep))
diff --git a/fs/nfs/write.c b/fs/nfs/write.c
index b383fdd3a15c..8fccb9cb173b 100644
--- a/fs/nfs/write.c
+++ b/fs/nfs/write.c
@@ -1551,7 +1551,7 @@ int __init nfs_init_writepagecache(void)
return 0;
}
-void __exit nfs_destroy_writepagecache(void)
+void nfs_destroy_writepagecache(void)
{
mempool_destroy(nfs_commit_mempool);
mempool_destroy(nfs_wdata_mempool);
diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c
index 1630b5670dc2..7c7d01672d35 100644
--- a/fs/nfsd/nfs4state.c
+++ b/fs/nfsd/nfs4state.c
@@ -123,7 +123,7 @@ static void release_stateid(struct nfs4_stateid *stp, int flags);
*/
/* recall_lock protects the del_recall_lru */
-static spinlock_t recall_lock = SPIN_LOCK_UNLOCKED;
+static DEFINE_SPINLOCK(recall_lock);
static struct list_head del_recall_lru;
static void
diff --git a/fs/ntfs/aops.c b/fs/ntfs/aops.c
index 580412d330cb..bc579bfdfbd8 100644
--- a/fs/ntfs/aops.c
+++ b/fs/ntfs/aops.c
@@ -1544,7 +1544,7 @@ err_out:
/**
* ntfs_aops - general address space operations for inodes and attributes
*/
-struct address_space_operations ntfs_aops = {
+const struct address_space_operations ntfs_aops = {
.readpage = ntfs_readpage, /* Fill page with data. */
.sync_page = block_sync_page, /* Currently, just unplugs the
disk request queue. */
@@ -1560,7 +1560,7 @@ struct address_space_operations ntfs_aops = {
* ntfs_mst_aops - general address space operations for mst protecteed inodes
* and attributes
*/
-struct address_space_operations ntfs_mst_aops = {
+const struct address_space_operations ntfs_mst_aops = {
.readpage = ntfs_readpage, /* Fill page with data. */
.sync_page = block_sync_page, /* Currently, just unplugs the
disk request queue. */
diff --git a/fs/ntfs/ntfs.h b/fs/ntfs/ntfs.h
index bf7b3d7c0930..ddd3d503097c 100644
--- a/fs/ntfs/ntfs.h
+++ b/fs/ntfs/ntfs.h
@@ -57,8 +57,8 @@ extern struct kmem_cache *ntfs_attr_ctx_cache;
extern struct kmem_cache *ntfs_index_ctx_cache;
/* The various operations structs defined throughout the driver files. */
-extern struct address_space_operations ntfs_aops;
-extern struct address_space_operations ntfs_mst_aops;
+extern const struct address_space_operations ntfs_aops;
+extern const struct address_space_operations ntfs_mst_aops;
extern const struct file_operations ntfs_file_ops;
extern struct inode_operations ntfs_file_inode_ops;
diff --git a/fs/ocfs2/aops.c b/fs/ocfs2/aops.c
index 47152bf9a7f2..cca71317b6d6 100644
--- a/fs/ocfs2/aops.c
+++ b/fs/ocfs2/aops.c
@@ -666,7 +666,7 @@ out:
return ret;
}
-struct address_space_operations ocfs2_aops = {
+const struct address_space_operations ocfs2_aops = {
.readpage = ocfs2_readpage,
.writepage = ocfs2_writepage,
.prepare_write = ocfs2_prepare_write,
diff --git a/fs/ocfs2/cluster/heartbeat.c b/fs/ocfs2/cluster/heartbeat.c
index 21f38accd039..1d26cfcd9f84 100644
--- a/fs/ocfs2/cluster/heartbeat.c
+++ b/fs/ocfs2/cluster/heartbeat.c
@@ -54,7 +54,7 @@ static DECLARE_RWSEM(o2hb_callback_sem);
* multiple hb threads are watching multiple regions. A node is live
* whenever any of the threads sees activity from the node in its region.
*/
-static spinlock_t o2hb_live_lock = SPIN_LOCK_UNLOCKED;
+static DEFINE_SPINLOCK(o2hb_live_lock);
static struct list_head o2hb_live_slots[O2NM_MAX_NODES];
static unsigned long o2hb_live_node_bitmap[BITS_TO_LONGS(O2NM_MAX_NODES)];
static LIST_HEAD(o2hb_node_events);
diff --git a/fs/ocfs2/cluster/tcp.c b/fs/ocfs2/cluster/tcp.c
index 0f60cc0d3985..1591eb37a723 100644
--- a/fs/ocfs2/cluster/tcp.c
+++ b/fs/ocfs2/cluster/tcp.c
@@ -108,7 +108,7 @@
##args); \
} while (0)
-static rwlock_t o2net_handler_lock = RW_LOCK_UNLOCKED;
+static DEFINE_RWLOCK(o2net_handler_lock);
static struct rb_root o2net_handler_tree = RB_ROOT;
static struct o2net_node o2net_nodes[O2NM_MAX_NODES];
diff --git a/fs/ocfs2/dlm/dlmdomain.c b/fs/ocfs2/dlm/dlmdomain.c
index ba27c5c5e959..b8c23f7ba67e 100644
--- a/fs/ocfs2/dlm/dlmdomain.c
+++ b/fs/ocfs2/dlm/dlmdomain.c
@@ -88,7 +88,7 @@ out_free:
*
*/
-spinlock_t dlm_domain_lock = SPIN_LOCK_UNLOCKED;
+DEFINE_SPINLOCK(dlm_domain_lock);
LIST_HEAD(dlm_domains);
static DECLARE_WAIT_QUEUE_HEAD(dlm_domain_events);
diff --git a/fs/ocfs2/dlm/dlmlock.c b/fs/ocfs2/dlm/dlmlock.c
index d6f89577e25f..5ca57ec650c7 100644
--- a/fs/ocfs2/dlm/dlmlock.c
+++ b/fs/ocfs2/dlm/dlmlock.c
@@ -53,7 +53,7 @@
#define MLOG_MASK_PREFIX ML_DLM
#include "cluster/masklog.h"
-static spinlock_t dlm_cookie_lock = SPIN_LOCK_UNLOCKED;
+static DEFINE_SPINLOCK(dlm_cookie_lock);
static u64 dlm_next_cookie = 1;
static enum dlm_status dlm_send_remote_lock_request(struct dlm_ctxt *dlm,
diff --git a/fs/ocfs2/dlm/dlmrecovery.c b/fs/ocfs2/dlm/dlmrecovery.c
index da399013516f..29b2845f370d 100644
--- a/fs/ocfs2/dlm/dlmrecovery.c
+++ b/fs/ocfs2/dlm/dlmrecovery.c
@@ -98,8 +98,8 @@ static void dlm_mig_lockres_worker(struct dlm_work_item *item, void *data);
static u64 dlm_get_next_mig_cookie(void);
-static spinlock_t dlm_reco_state_lock = SPIN_LOCK_UNLOCKED;
-static spinlock_t dlm_mig_cookie_lock = SPIN_LOCK_UNLOCKED;
+static DEFINE_SPINLOCK(dlm_reco_state_lock);
+static DEFINE_SPINLOCK(dlm_mig_cookie_lock);
static u64 dlm_mig_cookie = 1;
static u64 dlm_get_next_mig_cookie(void)
diff --git a/fs/ocfs2/dlmglue.c b/fs/ocfs2/dlmglue.c
index 64cd52860c87..4acd37286bdd 100644
--- a/fs/ocfs2/dlmglue.c
+++ b/fs/ocfs2/dlmglue.c
@@ -242,7 +242,7 @@ static void ocfs2_build_lock_name(enum ocfs2_lock_type type,
mlog_exit_void();
}
-static spinlock_t ocfs2_dlm_tracking_lock = SPIN_LOCK_UNLOCKED;
+static DEFINE_SPINLOCK(ocfs2_dlm_tracking_lock);
static void ocfs2_add_lockres_tracking(struct ocfs2_lock_res *res,
struct ocfs2_dlm_debug *dlm_debug)
diff --git a/fs/ocfs2/inode.h b/fs/ocfs2/inode.h
index 84c507961287..35140f6cf840 100644
--- a/fs/ocfs2/inode.h
+++ b/fs/ocfs2/inode.h
@@ -114,7 +114,7 @@ static inline struct ocfs2_inode_info *OCFS2_I(struct inode *inode)
extern kmem_cache_t *ocfs2_inode_cache;
-extern struct address_space_operations ocfs2_aops;
+extern const struct address_space_operations ocfs2_aops;
struct buffer_head *ocfs2_bread(struct inode *inode, int block,
int *err, int reada);
diff --git a/fs/ocfs2/journal.c b/fs/ocfs2/journal.c
index 3fe8781c22cb..910a601b2e98 100644
--- a/fs/ocfs2/journal.c
+++ b/fs/ocfs2/journal.c
@@ -49,7 +49,7 @@
#include "buffer_head_io.h"
-spinlock_t trans_inc_lock = SPIN_LOCK_UNLOCKED;
+DEFINE_SPINLOCK(trans_inc_lock);
static int ocfs2_force_read_journal(struct inode *inode);
static int ocfs2_recover_node(struct ocfs2_super *osb,
diff --git a/fs/ocfs2/vote.c b/fs/ocfs2/vote.c
index ee42765a8553..cf70fe2075b8 100644
--- a/fs/ocfs2/vote.c
+++ b/fs/ocfs2/vote.c
@@ -988,9 +988,7 @@ int ocfs2_request_mount_vote(struct ocfs2_super *osb)
}
bail:
- if (request)
- kfree(request);
-
+ kfree(request);
return status;
}
@@ -1021,9 +1019,7 @@ int ocfs2_request_umount_vote(struct ocfs2_super *osb)
}
bail:
- if (request)
- kfree(request);
-
+ kfree(request);
return status;
}
diff --git a/fs/partitions/Makefile b/fs/partitions/Makefile
index 42c7d3878ed0..d713ce6b3e12 100644
--- a/fs/partitions/Makefile
+++ b/fs/partitions/Makefile
@@ -4,7 +4,6 @@
obj-y := check.o
-obj-$(CONFIG_DEVFS_FS) += devfs.o
obj-$(CONFIG_ACORN_PARTITION) += acorn.o
obj-$(CONFIG_AMIGA_PARTITION) += amiga.o
obj-$(CONFIG_ATARI_PARTITION) += atari.o
diff --git a/fs/partitions/check.c b/fs/partitions/check.c
index 2ef313a96b66..839634026eb5 100644
--- a/fs/partitions/check.c
+++ b/fs/partitions/check.c
@@ -18,10 +18,8 @@
#include <linux/fs.h>
#include <linux/kmod.h>
#include <linux/ctype.h>
-#include <linux/devfs_fs_kernel.h>
#include "check.h"
-#include "devfs.h"
#include "acorn.h"
#include "amiga.h"
@@ -161,18 +159,11 @@ check_partition(struct gendisk *hd, struct block_device *bdev)
if (!state)
return NULL;
-#ifdef CONFIG_DEVFS_FS
- if (hd->devfs_name[0] != '\0') {
- printk(KERN_INFO " /dev/%s:", hd->devfs_name);
+ disk_name(hd, 0, state->name);
+ printk(KERN_INFO " %s:", state->name);
+ if (isdigit(state->name[strlen(state->name)-1]))
sprintf(state->name, "p");
- }
-#endif
- else {
- disk_name(hd, 0, state->name);
- printk(KERN_INFO " %s:", state->name);
- if (isdigit(state->name[strlen(state->name)-1]))
- sprintf(state->name, "p");
- }
+
state->limit = hd->minors;
i = res = 0;
while (!res && check_part[i]) {
@@ -328,7 +319,6 @@ void delete_partition(struct gendisk *disk, int part)
p->nr_sects = 0;
p->ios[0] = p->ios[1] = 0;
p->sectors[0] = p->sectors[1] = 0;
- devfs_remove("%s/part%d", disk->devfs_name, part);
sysfs_remove_link(&p->kobj, "subsystem");
if (p->holder_dir)
kobject_unregister(p->holder_dir);
@@ -350,10 +340,6 @@ void add_partition(struct gendisk *disk, int part, sector_t start, sector_t len)
p->nr_sects = len;
p->partno = part;
- devfs_mk_bdev(MKDEV(disk->major, disk->first_minor + part),
- S_IFBLK|S_IRUSR|S_IWUSR,
- "%s/part%d", disk->devfs_name, part);
-
if (isdigit(disk->kobj.name[strlen(disk->kobj.name)-1]))
snprintf(p->kobj.name,KOBJ_NAME_LEN,"%sp%d",disk->kobj.name,part);
else
@@ -423,14 +409,8 @@ void register_disk(struct gendisk *disk)
disk_sysfs_add_subdirs(disk);
/* No minors to use for partitions */
- if (disk->minors == 1) {
- if (disk->devfs_name[0] != '\0')
- devfs_add_disk(disk);
+ if (disk->minors == 1)
goto exit;
- }
-
- /* always add handle for the whole disk */
- devfs_add_partitioned(disk);
/* No such device (e.g., media were just removed) */
if (!get_capacity(disk))
@@ -538,8 +518,6 @@ void del_gendisk(struct gendisk *disk)
disk_stat_set_all(disk, 0);
disk->stamp = 0;
- devfs_remove_disk(disk);
-
kobject_uevent(&disk->kobj, KOBJ_REMOVE);
if (disk->holder_dir)
kobject_unregister(disk->holder_dir);
diff --git a/fs/partitions/devfs.c b/fs/partitions/devfs.c
deleted file mode 100644
index 3f0a780c9cec..000000000000
--- a/fs/partitions/devfs.c
+++ /dev/null
@@ -1,130 +0,0 @@
-/*
- * This tries to keep block devices away from devfs as much as possible.
- */
-#include <linux/fs.h>
-#include <linux/devfs_fs_kernel.h>
-#include <linux/vmalloc.h>
-#include <linux/genhd.h>
-#include <linux/bitops.h>
-#include <linux/mutex.h>
-
-
-struct unique_numspace {
- u32 num_free; /* Num free in bits */
- u32 length; /* Array length in bytes */
- unsigned long *bits;
- struct semaphore mutex;
-};
-
-static DEFINE_MUTEX(numspace_mutex);
-
-static int expand_numspace(struct unique_numspace *s)
-{
- u32 length;
- void *bits;
-
- if (s->length < 16)
- length = 16;
- else
- length = s->length << 1;
-
- bits = vmalloc(length);
- if (!bits)
- return -ENOMEM;
- if (s->bits) {
- memcpy(bits, s->bits, s->length);
- vfree(s->bits);
- }
-
- s->num_free = (length - s->length) << 3;
- s->bits = bits;
- memset(bits + s->length, 0, length - s->length);
- s->length = length;
-
- return 0;
-}
-
-static int alloc_unique_number(struct unique_numspace *s)
-{
- int rval = 0;
-
- mutex_lock(&numspace_mutex);
- if (s->num_free < 1)
- rval = expand_numspace(s);
- if (!rval) {
- rval = find_first_zero_bit(s->bits, s->length << 3);
- --s->num_free;
- __set_bit(rval, s->bits);
- }
- mutex_unlock(&numspace_mutex);
-
- return rval;
-}
-
-static void dealloc_unique_number(struct unique_numspace *s, int number)
-{
- int old_val;
-
- if (number >= 0) {
- mutex_lock(&numspace_mutex);
- old_val = __test_and_clear_bit(number, s->bits);
- if (old_val)
- ++s->num_free;
- mutex_unlock(&numspace_mutex);
- }
-}
-
-static struct unique_numspace disc_numspace;
-static struct unique_numspace cdrom_numspace;
-
-void devfs_add_partitioned(struct gendisk *disk)
-{
- char dirname[64], symlink[16];
-
- devfs_mk_dir(disk->devfs_name);
- devfs_mk_bdev(MKDEV(disk->major, disk->first_minor),
- S_IFBLK|S_IRUSR|S_IWUSR,
- "%s/disc", disk->devfs_name);
-
- disk->number = alloc_unique_number(&disc_numspace);
-
- sprintf(symlink, "discs/disc%d", disk->number);
- sprintf(dirname, "../%s", disk->devfs_name);
- devfs_mk_symlink(symlink, dirname);
-
-}
-
-void devfs_add_disk(struct gendisk *disk)
-{
- devfs_mk_bdev(MKDEV(disk->major, disk->first_minor),
- (disk->flags & GENHD_FL_CD) ?
- S_IFBLK|S_IRUGO|S_IWUGO :
- S_IFBLK|S_IRUSR|S_IWUSR,
- "%s", disk->devfs_name);
-
- if (disk->flags & GENHD_FL_CD) {
- char dirname[64], symlink[16];
-
- disk->number = alloc_unique_number(&cdrom_numspace);
-
- sprintf(symlink, "cdroms/cdrom%d", disk->number);
- sprintf(dirname, "../%s", disk->devfs_name);
- devfs_mk_symlink(symlink, dirname);
- }
-}
-
-void devfs_remove_disk(struct gendisk *disk)
-{
- if (disk->minors != 1) {
- devfs_remove("discs/disc%d", disk->number);
- dealloc_unique_number(&disc_numspace, disk->number);
- devfs_remove("%s/disc", disk->devfs_name);
- }
- if (disk->flags & GENHD_FL_CD) {
- devfs_remove("cdroms/cdrom%d", disk->number);
- dealloc_unique_number(&cdrom_numspace, disk->number);
- }
- devfs_remove(disk->devfs_name);
-}
-
-
diff --git a/fs/partitions/devfs.h b/fs/partitions/devfs.h
deleted file mode 100644
index 176118b4e492..000000000000
--- a/fs/partitions/devfs.h
+++ /dev/null
@@ -1,10 +0,0 @@
-
-#ifdef CONFIG_DEVFS_FS
-void devfs_add_disk(struct gendisk *dev);
-void devfs_add_partitioned(struct gendisk *dev);
-void devfs_remove_disk(struct gendisk *dev);
-#else
-# define devfs_add_disk(disk) do { } while (0)
-# define devfs_add_partitioned(disk) do { } while (0)
-# define devfs_remove_disk(disk) do { } while (0)
-#endif
diff --git a/fs/proc/task_mmu.c b/fs/proc/task_mmu.c
index 0137ec4c1368..0a163a4f7764 100644
--- a/fs/proc/task_mmu.c
+++ b/fs/proc/task_mmu.c
@@ -122,6 +122,11 @@ struct mem_size_stats
unsigned long private_dirty;
};
+__attribute__((weak)) const char *arch_vma_name(struct vm_area_struct *vma)
+{
+ return NULL;
+}
+
static int show_map_internal(struct seq_file *m, void *v, struct mem_size_stats *mss)
{
struct proc_maps_private *priv = m->private;
@@ -158,22 +163,23 @@ static int show_map_internal(struct seq_file *m, void *v, struct mem_size_stats
pad_len_spaces(m, len);
seq_path(m, file->f_vfsmnt, file->f_dentry, "\n");
} else {
- if (mm) {
- if (vma->vm_start <= mm->start_brk &&
+ const char *name = arch_vma_name(vma);
+ if (!name) {
+ if (mm) {
+ if (vma->vm_start <= mm->start_brk &&
vma->vm_end >= mm->brk) {
- pad_len_spaces(m, len);
- seq_puts(m, "[heap]");
- } else {
- if (vma->vm_start <= mm->start_stack &&
- vma->vm_end >= mm->start_stack) {
-
- pad_len_spaces(m, len);
- seq_puts(m, "[stack]");
+ name = "[heap]";
+ } else if (vma->vm_start <= mm->start_stack &&
+ vma->vm_end >= mm->start_stack) {
+ name = "[stack]";
}
+ } else {
+ name = "[vdso]";
}
- } else {
+ }
+ if (name) {
pad_len_spaces(m, len);
- seq_puts(m, "[vdso]");
+ seq_puts(m, name);
}
}
seq_putc(m, '\n');
diff --git a/fs/qnx4/inode.c b/fs/qnx4/inode.c
index 2f24c46f72a1..8bc182a88748 100644
--- a/fs/qnx4/inode.c
+++ b/fs/qnx4/inode.c
@@ -450,7 +450,7 @@ static sector_t qnx4_bmap(struct address_space *mapping, sector_t block)
{
return generic_block_bmap(mapping,block,qnx4_get_block);
}
-static struct address_space_operations qnx4_aops = {
+static const struct address_space_operations qnx4_aops = {
.readpage = qnx4_readpage,
.writepage = qnx4_writepage,
.sync_page = block_sync_page,
diff --git a/fs/ramfs/file-mmu.c b/fs/ramfs/file-mmu.c
index 00a933eb820c..86f14cacf641 100644
--- a/fs/ramfs/file-mmu.c
+++ b/fs/ramfs/file-mmu.c
@@ -26,7 +26,7 @@
#include <linux/fs.h>
-struct address_space_operations ramfs_aops = {
+const struct address_space_operations ramfs_aops = {
.readpage = simple_readpage,
.prepare_write = simple_prepare_write,
.commit_write = simple_commit_write
diff --git a/fs/ramfs/file-nommu.c b/fs/ramfs/file-nommu.c
index f443a84b98a5..99fffc9e1bfd 100644
--- a/fs/ramfs/file-nommu.c
+++ b/fs/ramfs/file-nommu.c
@@ -27,7 +27,7 @@
static int ramfs_nommu_setattr(struct dentry *, struct iattr *);
-struct address_space_operations ramfs_aops = {
+const struct address_space_operations ramfs_aops = {
.readpage = simple_readpage,
.prepare_write = simple_prepare_write,
.commit_write = simple_commit_write
diff --git a/fs/ramfs/internal.h b/fs/ramfs/internal.h
index 313237631b49..c2bb58e74653 100644
--- a/fs/ramfs/internal.h
+++ b/fs/ramfs/internal.h
@@ -10,6 +10,6 @@
*/
-extern struct address_space_operations ramfs_aops;
+extern const struct address_space_operations ramfs_aops;
extern const struct file_operations ramfs_file_operations;
extern struct inode_operations ramfs_file_inode_operations;
diff --git a/fs/reiserfs/inode.c b/fs/reiserfs/inode.c
index 9857e50f85e7..a24858a632fa 100644
--- a/fs/reiserfs/inode.c
+++ b/fs/reiserfs/inode.c
@@ -2996,7 +2996,7 @@ int reiserfs_setattr(struct dentry *dentry, struct iattr *attr)
return error;
}
-struct address_space_operations reiserfs_address_space_operations = {
+const struct address_space_operations reiserfs_address_space_operations = {
.writepage = reiserfs_writepage,
.readpage = reiserfs_readpage,
.readpages = reiserfs_readpages,
diff --git a/fs/romfs/inode.c b/fs/romfs/inode.c
index 283fbc6b8eea..22eed61ebf69 100644
--- a/fs/romfs/inode.c
+++ b/fs/romfs/inode.c
@@ -459,7 +459,7 @@ err_out:
/* Mapping from our types to the kernel */
-static struct address_space_operations romfs_aops = {
+static const struct address_space_operations romfs_aops = {
.readpage = romfs_readpage
};
diff --git a/fs/smbfs/file.c b/fs/smbfs/file.c
index ed9a24d19d7d..dae67048baba 100644
--- a/fs/smbfs/file.c
+++ b/fs/smbfs/file.c
@@ -306,7 +306,7 @@ static int smb_commit_write(struct file *file, struct page *page,
return status;
}
-struct address_space_operations smb_file_aops = {
+const struct address_space_operations smb_file_aops = {
.readpage = smb_readpage,
.writepage = smb_writepage,
.prepare_write = smb_prepare_write,
diff --git a/fs/smbfs/proto.h b/fs/smbfs/proto.h
index 972ed7dad388..34fb462b2379 100644
--- a/fs/smbfs/proto.h
+++ b/fs/smbfs/proto.h
@@ -63,7 +63,7 @@ extern int smb_revalidate_inode(struct dentry *dentry);
extern int smb_getattr(struct vfsmount *mnt, struct dentry *dentry, struct kstat *stat);
extern int smb_notify_change(struct dentry *dentry, struct iattr *attr);
/* file.c */
-extern struct address_space_operations smb_file_aops;
+extern const struct address_space_operations smb_file_aops;
extern const struct file_operations smb_file_operations;
extern struct inode_operations smb_file_inode_operations;
/* ioctl.c */
diff --git a/fs/sysfs/inode.c b/fs/sysfs/inode.c
index f0b347bd12ca..5e0e31cc46f5 100644
--- a/fs/sysfs/inode.c
+++ b/fs/sysfs/inode.c
@@ -16,7 +16,7 @@
extern struct super_block * sysfs_sb;
-static struct address_space_operations sysfs_aops = {
+static const struct address_space_operations sysfs_aops = {
.readpage = simple_readpage,
.prepare_write = simple_prepare_write,
.commit_write = simple_commit_write
diff --git a/fs/sysv/itree.c b/fs/sysv/itree.c
index 86f5f8d43d0f..f2bcccd1d6fc 100644
--- a/fs/sysv/itree.c
+++ b/fs/sysv/itree.c
@@ -465,7 +465,7 @@ static sector_t sysv_bmap(struct address_space *mapping, sector_t block)
{
return generic_block_bmap(mapping,block,get_block);
}
-struct address_space_operations sysv_aops = {
+const struct address_space_operations sysv_aops = {
.readpage = sysv_readpage,
.writepage = sysv_writepage,
.sync_page = block_sync_page,
diff --git a/fs/sysv/sysv.h b/fs/sysv/sysv.h
index 393a480e4deb..9dcc82120935 100644
--- a/fs/sysv/sysv.h
+++ b/fs/sysv/sysv.h
@@ -161,7 +161,7 @@ extern struct inode_operations sysv_dir_inode_operations;
extern struct inode_operations sysv_fast_symlink_inode_operations;
extern const struct file_operations sysv_file_operations;
extern const struct file_operations sysv_dir_operations;
-extern struct address_space_operations sysv_aops;
+extern const struct address_space_operations sysv_aops;
extern struct super_operations sysv_sops;
extern struct dentry_operations sysv_dentry_operations;
diff --git a/fs/udf/file.c b/fs/udf/file.c
index e34b00e303f1..a59e5f33daf6 100644
--- a/fs/udf/file.c
+++ b/fs/udf/file.c
@@ -95,7 +95,7 @@ static int udf_adinicb_commit_write(struct file *file, struct page *page, unsign
return 0;
}
-struct address_space_operations udf_adinicb_aops = {
+const struct address_space_operations udf_adinicb_aops = {
.readpage = udf_adinicb_readpage,
.writepage = udf_adinicb_writepage,
.sync_page = block_sync_page,
diff --git a/fs/udf/inode.c b/fs/udf/inode.c
index 2983afd5e7fd..605f5111b6d8 100644
--- a/fs/udf/inode.c
+++ b/fs/udf/inode.c
@@ -132,7 +132,7 @@ static sector_t udf_bmap(struct address_space *mapping, sector_t block)
return generic_block_bmap(mapping,block,udf_get_block);
}
-struct address_space_operations udf_aops = {
+const struct address_space_operations udf_aops = {
.readpage = udf_readpage,
.writepage = udf_writepage,
.sync_page = block_sync_page,
diff --git a/fs/udf/symlink.c b/fs/udf/symlink.c
index 674bb40edc83..ba068a786563 100644
--- a/fs/udf/symlink.c
+++ b/fs/udf/symlink.c
@@ -113,6 +113,6 @@ out:
/*
* symlinks can't do much...
*/
-struct address_space_operations udf_symlink_aops = {
+const struct address_space_operations udf_symlink_aops = {
.readpage = udf_symlink_filler,
};
diff --git a/fs/udf/udfdecl.h b/fs/udf/udfdecl.h
index 023e19ba5a2e..2f992387cc9e 100644
--- a/fs/udf/udfdecl.h
+++ b/fs/udf/udfdecl.h
@@ -47,9 +47,9 @@ extern struct inode_operations udf_dir_inode_operations;
extern const struct file_operations udf_dir_operations;
extern struct inode_operations udf_file_inode_operations;
extern const struct file_operations udf_file_operations;
-extern struct address_space_operations udf_aops;
-extern struct address_space_operations udf_adinicb_aops;
-extern struct address_space_operations udf_symlink_aops;
+extern const struct address_space_operations udf_aops;
+extern const struct address_space_operations udf_adinicb_aops;
+extern const struct address_space_operations udf_symlink_aops;
struct udf_fileident_bh
{
diff --git a/fs/ufs/inode.c b/fs/ufs/inode.c
index f2dbdf5a8769..488b5ff48afb 100644
--- a/fs/ufs/inode.c
+++ b/fs/ufs/inode.c
@@ -98,7 +98,9 @@ static u64 ufs_frag_map(struct inode *inode, sector_t frag)
u64 temp = 0L;
UFSD(": frag = %llu depth = %d\n", (unsigned long long)frag, depth);
- UFSD(": uspi->s_fpbshift = %d ,uspi->s_apbmask = %x, mask=%llx\n",uspi->s_fpbshift,uspi->s_apbmask,mask);
+ UFSD(": uspi->s_fpbshift = %d ,uspi->s_apbmask = %x, mask=%llx\n",
+ uspi->s_fpbshift, uspi->s_apbmask,
+ (unsigned long long)mask);
if (depth == 0)
return 0;
@@ -429,7 +431,7 @@ int ufs_getfrag_block(struct inode *inode, sector_t fragment, struct buffer_head
if (!create) {
phys64 = ufs_frag_map(inode, fragment);
- UFSD("phys64 = %llu \n",phys64);
+ UFSD("phys64 = %llu\n", (unsigned long long)phys64);
if (phys64)
map_bh(bh_result, sb, phys64);
return 0;
@@ -574,7 +576,7 @@ static sector_t ufs_bmap(struct address_space *mapping, sector_t block)
{
return generic_block_bmap(mapping,block,ufs_getfrag_block);
}
-struct address_space_operations ufs_aops = {
+const struct address_space_operations ufs_aops = {
.readpage = ufs_readpage,
.writepage = ufs_writepage,
.sync_page = block_sync_page,
@@ -605,39 +607,12 @@ static void ufs_set_inode_ops(struct inode *inode)
ufs_get_inode_dev(inode->i_sb, UFS_I(inode)));
}
-void ufs_read_inode (struct inode * inode)
+static void ufs1_read_inode(struct inode *inode, struct ufs_inode *ufs_inode)
{
struct ufs_inode_info *ufsi = UFS_I(inode);
- struct super_block * sb;
- struct ufs_sb_private_info * uspi;
- struct ufs_inode * ufs_inode;
- struct ufs2_inode *ufs2_inode;
- struct buffer_head * bh;
+ struct super_block *sb = inode->i_sb;
mode_t mode;
unsigned i;
- unsigned flags;
-
- UFSD("ENTER, ino %lu\n", inode->i_ino);
-
- sb = inode->i_sb;
- uspi = UFS_SB(sb)->s_uspi;
- flags = UFS_SB(sb)->s_flags;
-
- if (inode->i_ino < UFS_ROOTINO ||
- inode->i_ino > (uspi->s_ncg * uspi->s_ipg)) {
- ufs_warning (sb, "ufs_read_inode", "bad inode number (%lu)\n", inode->i_ino);
- goto bad_inode;
- }
-
- bh = sb_bread(sb, uspi->s_sbbase + ufs_inotofsba(inode->i_ino));
- if (!bh) {
- ufs_warning (sb, "ufs_read_inode", "unable to read inode %lu\n", inode->i_ino);
- goto bad_inode;
- }
- if ((flags & UFS_TYPE_MASK) == UFS_TYPE_UFS2)
- goto ufs2_inode;
-
- ufs_inode = (struct ufs_inode *) (bh->b_data + sizeof(struct ufs_inode) * ufs_inotofsbo(inode->i_ino));
/*
* Copy data to the in-core inode.
@@ -661,14 +636,11 @@ void ufs_read_inode (struct inode * inode)
inode->i_atime.tv_nsec = 0;
inode->i_ctime.tv_nsec = 0;
inode->i_blocks = fs32_to_cpu(sb, ufs_inode->ui_blocks);
- inode->i_blksize = PAGE_SIZE; /* This is the optimal IO size (for stat) */
- inode->i_version++;
ufsi->i_flags = fs32_to_cpu(sb, ufs_inode->ui_flags);
ufsi->i_gen = fs32_to_cpu(sb, ufs_inode->ui_gen);
ufsi->i_shadow = fs32_to_cpu(sb, ufs_inode->ui_u3.ui_sun.ui_shadow);
ufsi->i_oeftflag = fs32_to_cpu(sb, ufs_inode->ui_u3.ui_sun.ui_oeftflag);
- ufsi->i_lastfrag = (inode->i_size + uspi->s_fsize - 1) >> uspi->s_fshift;
- ufsi->i_dir_start_lookup = 0;
+
if (S_ISCHR(mode) || S_ISBLK(mode) || inode->i_blocks) {
for (i = 0; i < (UFS_NDADDR + UFS_NINDIR); i++)
@@ -677,24 +649,16 @@ void ufs_read_inode (struct inode * inode)
for (i = 0; i < (UFS_NDADDR + UFS_NINDIR) * 4; i++)
ufsi->i_u1.i_symlink[i] = ufs_inode->ui_u2.ui_symlink[i];
}
- ufsi->i_osync = 0;
-
- ufs_set_inode_ops(inode);
-
- brelse (bh);
-
- UFSD("EXIT\n");
- return;
+}
-bad_inode:
- make_bad_inode(inode);
- return;
+static void ufs2_read_inode(struct inode *inode, struct ufs2_inode *ufs2_inode)
+{
+ struct ufs_inode_info *ufsi = UFS_I(inode);
+ struct super_block *sb = inode->i_sb;
+ mode_t mode;
+ unsigned i;
-ufs2_inode :
UFSD("Reading ufs2 inode, ino %lu\n", inode->i_ino);
-
- ufs2_inode = (struct ufs2_inode *)(bh->b_data + sizeof(struct ufs2_inode) * ufs_inotofsbo(inode->i_ino));
-
/*
* Copy data to the in-core inode.
*/
@@ -717,26 +681,64 @@ ufs2_inode :
inode->i_atime.tv_nsec = 0;
inode->i_ctime.tv_nsec = 0;
inode->i_blocks = fs64_to_cpu(sb, ufs2_inode->ui_blocks);
- inode->i_blksize = PAGE_SIZE; /*This is the optimal IO size(for stat)*/
-
- inode->i_version++;
ufsi->i_flags = fs32_to_cpu(sb, ufs2_inode->ui_flags);
ufsi->i_gen = fs32_to_cpu(sb, ufs2_inode->ui_gen);
/*
ufsi->i_shadow = fs32_to_cpu(sb, ufs_inode->ui_u3.ui_sun.ui_shadow);
ufsi->i_oeftflag = fs32_to_cpu(sb, ufs_inode->ui_u3.ui_sun.ui_oeftflag);
*/
- ufsi->i_lastfrag= (inode->i_size + uspi->s_fsize- 1) >> uspi->s_fshift;
if (S_ISCHR(mode) || S_ISBLK(mode) || inode->i_blocks) {
for (i = 0; i < (UFS_NDADDR + UFS_NINDIR); i++)
ufsi->i_u1.u2_i_data[i] =
ufs2_inode->ui_u2.ui_addr.ui_db[i];
- }
- else {
+ } else {
for (i = 0; i < (UFS_NDADDR + UFS_NINDIR) * 4; i++)
ufsi->i_u1.i_symlink[i] = ufs2_inode->ui_u2.ui_symlink[i];
}
+}
+
+void ufs_read_inode(struct inode * inode)
+{
+ struct ufs_inode_info *ufsi = UFS_I(inode);
+ struct super_block * sb;
+ struct ufs_sb_private_info * uspi;
+ struct buffer_head * bh;
+
+ UFSD("ENTER, ino %lu\n", inode->i_ino);
+
+ sb = inode->i_sb;
+ uspi = UFS_SB(sb)->s_uspi;
+
+ if (inode->i_ino < UFS_ROOTINO ||
+ inode->i_ino > (uspi->s_ncg * uspi->s_ipg)) {
+ ufs_warning(sb, "ufs_read_inode", "bad inode number (%lu)\n",
+ inode->i_ino);
+ goto bad_inode;
+ }
+
+ bh = sb_bread(sb, uspi->s_sbbase + ufs_inotofsba(inode->i_ino));
+ if (!bh) {
+ ufs_warning(sb, "ufs_read_inode", "unable to read inode %lu\n",
+ inode->i_ino);
+ goto bad_inode;
+ }
+ if ((UFS_SB(sb)->s_flags & UFS_TYPE_MASK) == UFS_TYPE_UFS2) {
+ struct ufs2_inode *ufs2_inode = (struct ufs2_inode *)bh->b_data;
+
+ ufs2_read_inode(inode,
+ ufs2_inode + ufs_inotofsbo(inode->i_ino));
+ } else {
+ struct ufs_inode *ufs_inode = (struct ufs_inode *)bh->b_data;
+
+ ufs1_read_inode(inode, ufs_inode + ufs_inotofsbo(inode->i_ino));
+ }
+
+ inode->i_blksize = PAGE_SIZE;/*This is the optimal IO size (for stat)*/
+ inode->i_version++;
+ ufsi->i_lastfrag =
+ (inode->i_size + uspi->s_fsize - 1) >> uspi->s_fshift;
+ ufsi->i_dir_start_lookup = 0;
ufsi->i_osync = 0;
ufs_set_inode_ops(inode);
@@ -745,6 +747,9 @@ ufs2_inode :
UFSD("EXIT\n");
return;
+
+bad_inode:
+ make_bad_inode(inode);
}
static int ufs_update_inode(struct inode * inode, int do_sync)
diff --git a/fs/xfs/linux-2.6/xfs_aops.c b/fs/xfs/linux-2.6/xfs_aops.c
index 3e807b828e22..c40f81ba9b13 100644
--- a/fs/xfs/linux-2.6/xfs_aops.c
+++ b/fs/xfs/linux-2.6/xfs_aops.c
@@ -1454,7 +1454,7 @@ xfs_vm_invalidatepage(
block_invalidatepage(page, offset);
}
-struct address_space_operations xfs_address_space_operations = {
+const struct address_space_operations xfs_address_space_operations = {
.readpage = xfs_vm_readpage,
.readpages = xfs_vm_readpages,
.writepage = xfs_vm_writepage,
diff --git a/fs/xfs/linux-2.6/xfs_aops.h b/fs/xfs/linux-2.6/xfs_aops.h
index 706d8c781b8a..2244e516b66a 100644
--- a/fs/xfs/linux-2.6/xfs_aops.h
+++ b/fs/xfs/linux-2.6/xfs_aops.h
@@ -40,7 +40,7 @@ typedef struct xfs_ioend {
struct work_struct io_work; /* xfsdatad work queue */
} xfs_ioend_t;
-extern struct address_space_operations xfs_address_space_operations;
+extern const struct address_space_operations xfs_address_space_operations;
extern int xfs_get_blocks(struct inode *, sector_t, struct buffer_head *, int);
#endif /* __XFS_AOPS_H__ */
diff --git a/fs/xfs/linux-2.6/xfs_buf.c b/fs/xfs/linux-2.6/xfs_buf.c
index 26fed0756f01..2af528dcfb04 100644
--- a/fs/xfs/linux-2.6/xfs_buf.c
+++ b/fs/xfs/linux-2.6/xfs_buf.c
@@ -1520,7 +1520,7 @@ xfs_mapping_buftarg(
struct backing_dev_info *bdi;
struct inode *inode;
struct address_space *mapping;
- static struct address_space_operations mapping_aops = {
+ static const struct address_space_operations mapping_aops = {
.sync_page = block_sync_page,
.migratepage = fail_migrate_page,
};
diff --git a/fs/xfs/linux-2.6/xfs_iops.c b/fs/xfs/linux-2.6/xfs_iops.c
index 12810baeb5d4..d9180020de63 100644
--- a/fs/xfs/linux-2.6/xfs_iops.c
+++ b/fs/xfs/linux-2.6/xfs_iops.c
@@ -419,16 +419,15 @@ xfs_vn_link(
int error;
ip = old_dentry->d_inode; /* inode being linked to */
- if (S_ISDIR(ip->i_mode))
- return -EPERM;
-
tdvp = vn_from_inode(dir);
vp = vn_from_inode(ip);
+ VN_HOLD(vp);
error = bhv_vop_link(tdvp, vp, dentry, NULL);
- if (likely(!error)) {
+ if (unlikely(error)) {
+ VN_RELE(vp);
+ } else {
VMODIFY(tdvp);
- VN_HOLD(vp);
xfs_validate_fields(ip, &vattr);
d_instantiate(dentry, ip);
}
diff --git a/fs/xfs/linux-2.6/xfs_linux.h b/fs/xfs/linux-2.6/xfs_linux.h
index aa26ab906c88..028eb17ec2ed 100644
--- a/fs/xfs/linux-2.6/xfs_linux.h
+++ b/fs/xfs/linux-2.6/xfs_linux.h
@@ -140,9 +140,7 @@ BUFFER_FNS(PrivateStart, unwritten);
#define current_pid() (current->pid)
#define current_fsuid(cred) (current->fsuid)
#define current_fsgid(cred) (current->fsgid)
-#define current_set_flags(f) (current->flags |= (f))
#define current_test_flags(f) (current->flags & (f))
-#define current_clear_flags(f) (current->flags & ~(f))
#define current_set_flags_nested(sp, f) \
(*(sp) = current->flags, current->flags |= (f))
#define current_clear_flags_nested(sp, f) \
diff --git a/fs/xfs/linux-2.6/xfs_vnode.h b/fs/xfs/linux-2.6/xfs_vnode.h
index 35c6a01963a7..c42b3221b20c 100644
--- a/fs/xfs/linux-2.6/xfs_vnode.h
+++ b/fs/xfs/linux-2.6/xfs_vnode.h
@@ -93,7 +93,7 @@ typedef enum {
*/
static inline struct bhv_vnode *vn_from_inode(struct inode *inode)
{
- return (bhv_vnode_t *)list_entry(inode, bhv_vnode_t, v_inode);
+ return container_of(inode, bhv_vnode_t, v_inode);
}
static inline struct inode *vn_to_inode(struct bhv_vnode *vnode)
{
diff --git a/fs/xfs/xfs_behavior.h b/fs/xfs/xfs_behavior.h
index 1d8ff103201c..6e6e56fb352d 100644
--- a/fs/xfs/xfs_behavior.h
+++ b/fs/xfs/xfs_behavior.h
@@ -78,15 +78,12 @@
*
*/
-struct bhv_head_lock;
-
/*
* Behavior head. Head of the chain of behaviors.
* Contained within each virtualized object data structure.
*/
typedef struct bhv_head {
struct bhv_desc *bh_first; /* first behavior in chain */
- struct bhv_head_lock *bh_lockp; /* pointer to lock info struct */
} bhv_head_t;
/*
diff --git a/fs/xfs/xfs_inode.c b/fs/xfs/xfs_inode.c
index 5fa0adb7e173..86c1bf0bba9e 100644
--- a/fs/xfs/xfs_inode.c
+++ b/fs/xfs/xfs_inode.c
@@ -1961,9 +1961,9 @@ xfs_iunlink_remove(
xfs_agino_t agino;
xfs_agino_t next_agino;
xfs_buf_t *last_ibp;
- xfs_dinode_t *last_dip;
+ xfs_dinode_t *last_dip = NULL;
short bucket_index;
- int offset, last_offset;
+ int offset, last_offset = 0;
int error;
int agi_ok;
diff --git a/fs/xfs/xfs_log.c b/fs/xfs/xfs_log.c
index d8f5d4cbe8b7..e730328636c3 100644
--- a/fs/xfs/xfs_log.c
+++ b/fs/xfs/xfs_log.c
@@ -1740,10 +1740,10 @@ xlog_write(xfs_mount_t * mp,
xlog_in_core_t **commit_iclog,
uint flags)
{
- xlog_t *log = mp->m_log;
+ xlog_t *log = mp->m_log;
xlog_ticket_t *ticket = (xlog_ticket_t *)tic;
+ xlog_in_core_t *iclog = NULL; /* ptr to current in-core log */
xlog_op_header_t *logop_head; /* ptr to log operation header */
- xlog_in_core_t *iclog; /* ptr to current in-core log */
__psint_t ptr; /* copy address into data region */
int len; /* # xlog_write() bytes 2 still copy */
int index; /* region index currently copying */
diff --git a/fs/xfs/xfs_log_recover.c b/fs/xfs/xfs_log_recover.c
index 55b4237c2153..3cb678e3a132 100644
--- a/fs/xfs/xfs_log_recover.c
+++ b/fs/xfs/xfs_log_recover.c
@@ -990,6 +990,8 @@ xlog_find_zeroed(
xfs_daddr_t num_scan_bblks;
int error, log_bbnum = log->l_logBBsize;
+ *blk_no = 0;
+
/* check totally zeroed log */
bp = xlog_get_bp(log, 1);
if (!bp)
diff --git a/fs/xfs/xfs_mount.c b/fs/xfs/xfs_mount.c
index 10dbf203c62f..4be5c0b2d296 100644
--- a/fs/xfs/xfs_mount.c
+++ b/fs/xfs/xfs_mount.c
@@ -1721,15 +1721,14 @@ xfs_mount_log_sbunit(
* is present to prevent thrashing).
*/
+#ifdef CONFIG_HOTPLUG_CPU
/*
* hot-plug CPU notifier support.
*
- * We cannot use the hotcpu_register() function because it does
- * not allow notifier instances. We need a notifier per filesystem
- * as we need to be able to identify the filesystem to balance
- * the counters out. This is achieved by having a notifier block
- * embedded in the xfs_mount_t and doing pointer magic to get the
- * mount pointer from the notifier block address.
+ * We need a notifier per filesystem as we need to be able to identify
+ * the filesystem to balance the counters out. This is achieved by
+ * having a notifier block embedded in the xfs_mount_t and doing pointer
+ * magic to get the mount pointer from the notifier block address.
*/
STATIC int
xfs_icsb_cpu_notify(
@@ -1779,6 +1778,7 @@ xfs_icsb_cpu_notify(
return NOTIFY_OK;
}
+#endif /* CONFIG_HOTPLUG_CPU */
int
xfs_icsb_init_counters(
@@ -1791,9 +1791,11 @@ xfs_icsb_init_counters(
if (mp->m_sb_cnts == NULL)
return -ENOMEM;
+#ifdef CONFIG_HOTPLUG_CPU
mp->m_icsb_notifier.notifier_call = xfs_icsb_cpu_notify;
mp->m_icsb_notifier.priority = 0;
- register_cpu_notifier(&mp->m_icsb_notifier);
+ register_hotcpu_notifier(&mp->m_icsb_notifier);
+#endif /* CONFIG_HOTPLUG_CPU */
for_each_online_cpu(i) {
cntp = (xfs_icsb_cnts_t *)per_cpu_ptr(mp->m_sb_cnts, i);
@@ -1812,7 +1814,7 @@ xfs_icsb_destroy_counters(
xfs_mount_t *mp)
{
if (mp->m_sb_cnts) {
- unregister_cpu_notifier(&mp->m_icsb_notifier);
+ unregister_hotcpu_notifier(&mp->m_icsb_notifier);
free_percpu(mp->m_sb_cnts);
}
}
@@ -2026,7 +2028,7 @@ xfs_icsb_balance_counter(
xfs_sb_field_t field,
int flags)
{
- uint64_t count, resid = 0;
+ uint64_t count, resid;
int weight = num_online_cpus();
int s;
@@ -2058,6 +2060,7 @@ xfs_icsb_balance_counter(
break;
default:
BUG();
+ count = resid = 0; /* quiet, gcc */
break;
}
diff --git a/fs/xfs/xfs_rtalloc.c b/fs/xfs/xfs_rtalloc.c
index 0c1e42b037ef..5a0b678956e0 100644
--- a/fs/xfs/xfs_rtalloc.c
+++ b/fs/xfs/xfs_rtalloc.c
@@ -1929,7 +1929,7 @@ xfs_growfs_rt(
/*
* Initial error checking.
*/
- if (mp->m_rtdev_targp || mp->m_rbmip == NULL ||
+ if (mp->m_rtdev_targp == NULL || mp->m_rbmip == NULL ||
(nrblocks = in->newblocks) <= sbp->sb_rblocks ||
(sbp->sb_rblocks && (in->extsize != sbp->sb_rextsize)))
return XFS_ERROR(EINVAL);
diff --git a/fs/xfs/xfs_trans.h b/fs/xfs/xfs_trans.h
index cb65c3a603f5..9dc88b380608 100644
--- a/fs/xfs/xfs_trans.h
+++ b/fs/xfs/xfs_trans.h
@@ -338,8 +338,6 @@ typedef void (*xfs_trans_callback_t)(struct xfs_trans *, void *);
typedef struct xfs_trans {
unsigned int t_magic; /* magic number */
xfs_log_callback_t t_logcb; /* log callback struct */
- struct xfs_trans *t_forw; /* async list pointers */
- struct xfs_trans *t_back; /* async list pointers */
unsigned int t_type; /* transaction type */
unsigned int t_log_res; /* amt of log space resvd */
unsigned int t_log_count; /* count for perm log res */
@@ -364,9 +362,11 @@ typedef struct xfs_trans {
long t_res_fdblocks_delta; /* on-disk only chg */
long t_frextents_delta;/* superblock freextents chg*/
long t_res_frextents_delta; /* on-disk only chg */
+#ifdef DEBUG
long t_ag_freeblks_delta; /* debugging counter */
long t_ag_flist_delta; /* debugging counter */
long t_ag_btree_delta; /* debugging counter */
+#endif
long t_dblocks_delta;/* superblock dblocks change */
long t_agcount_delta;/* superblock agcount change */
long t_imaxpct_delta;/* superblock imaxpct change */
diff --git a/fs/xfs/xfs_vnodeops.c b/fs/xfs/xfs_vnodeops.c
index 00a6b7dc24a0..23cfa5837728 100644
--- a/fs/xfs/xfs_vnodeops.c
+++ b/fs/xfs/xfs_vnodeops.c
@@ -2603,8 +2603,7 @@ xfs_link(
vn_trace_entry(src_vp, __FUNCTION__, (inst_t *)__return_address);
target_namelen = VNAMELEN(dentry);
- if (VN_ISDIR(src_vp))
- return XFS_ERROR(EPERM);
+ ASSERT(!VN_ISDIR(src_vp));
sip = xfs_vtoi(src_vp);
tdp = XFS_BHVTOI(target_dir_bdp);
@@ -2699,9 +2698,8 @@ xfs_link(
xfs_trans_log_inode(tp, tdp, XFS_ILOG_CORE);
error = xfs_bumplink(tp, sip);
- if (error) {
+ if (error)
goto abort_return;
- }
/*
* If this is a synchronous mount, make sure that the
@@ -2719,9 +2717,8 @@ xfs_link(
}
error = xfs_trans_commit(tp, XFS_TRANS_RELEASE_LOG_RES, NULL);
- if (error) {
+ if (error)
goto std_return;
- }
/* Fall through to std_return with error = 0. */
std_return:
@@ -2742,6 +2739,8 @@ std_return:
xfs_trans_cancel(tp, cancel_flags);
goto std_return;
}
+
+
/*
* xfs_mkdir
*