patch-2.1.58 linux/fs/smbfs/inode.c

Next file: linux/fs/smbfs/proc.c
Previous file: linux/fs/smbfs/file.c
Back to the patch index
Back to the overall index

diff -u --recursive --new-file v2.1.57/linux/fs/smbfs/inode.c linux/fs/smbfs/inode.c
@@ -6,6 +6,8 @@
  *
  */
 
+#define SMBFS_DCACHE_EXT 1
+
 #include <linux/config.h>
 #include <linux/module.h>
 
@@ -18,17 +20,20 @@
 #include <linux/stat.h>
 #include <linux/errno.h>
 #include <linux/locks.h>
-#include <linux/file.h>
-#include <linux/fcntl.h>
 #include <linux/malloc.h>
 #include <linux/init.h>
+#include <linux/dcache.h>
 
 #include <asm/system.h>
 #include <asm/uaccess.h>
 
-#define SB_of(server) ((struct super_block *) ((char *)(server) - \
-	(unsigned long)(&((struct super_block *)0)->u.smbfs_sb)))
+#define SMBFS_PARANOIA 1
+/* #define SMBFS_DEBUG_VERBOSE 1 */
 
+#ifndef SMBFS_DCACHE_EXT
+#define shrink_dcache_sb(sb) shrink_dcache()
+#endif
+extern void smb_renew_times(struct dentry *);
 extern int close_fp(struct file *filp);
 
 static void smb_put_inode(struct inode *);
@@ -67,7 +72,7 @@
 	return ino;
 }
 
-static struct smb_fattr *read_fattr;
+static struct smb_fattr *read_fattr = NULL;
 static struct semaphore read_semaphore = MUTEX;
 
 struct inode *
@@ -80,6 +85,7 @@
 	down(&read_semaphore);
 	read_fattr = fattr;
 	result = iget(sb, fattr->f_ino);
+	read_fattr = NULL;
 	up(&read_semaphore);
 	return result;
 }
@@ -87,7 +93,6 @@
 static void
 smb_set_inode_attr(struct inode *inode, struct smb_fattr *fattr)
 {
-	inode->i_dev	= inode->i_sb->s_dev;
 	inode->i_mode	= fattr->f_mode;
 	inode->i_nlink	= fattr->f_nlink;
 	inode->i_uid	= fattr->f_uid;
@@ -99,6 +104,10 @@
 	inode->i_atime	= fattr->f_atime;
 	inode->i_blksize= fattr->f_blksize;
 	inode->i_blocks = fattr->f_blocks;
+	/*
+	 * Update the "last time refreshed" field for revalidation.
+	 */
+	inode->u.smbfs_i.oldmtime = jiffies;
 }
 
 static void
@@ -106,15 +115,15 @@
 {
 	pr_debug("smb_iget: %p\n", read_fattr);
 
-	if ((atomic_read(&read_semaphore.count) == 1) ||
-	    (inode->i_ino != read_fattr->f_ino))
+	if (!read_fattr || inode->i_ino != read_fattr->f_ino)
 	{
 		printk("smb_read_inode called from invalid point\n");
 		return;
 	}
 
-	smb_set_inode_attr(inode, read_fattr);
+	inode->i_dev = inode->i_sb->s_dev;
 	memset(&(inode->u.smbfs_i), 0, sizeof(inode->u.smbfs_i));
+	smb_set_inode_attr(inode, read_fattr);
 
 	if (S_ISREG(inode->i_mode))
 		inode->i_op = &smb_file_inode_operations;
@@ -131,59 +140,129 @@
 void
 smb_invalidate_inodes(struct smb_sb_info *server)
 {
-	printk("smb_invalidate_inodes\n");
-	shrink_dcache(); /* should shrink only this sb */
+#ifdef SMBFS_DEBUG_VERBOSE
+printk("smb_invalidate_inodes\n");
+#endif
+	shrink_dcache_sb(SB_of(server));
 	invalidate_inodes(SB_of(server));
 }
 
 int
-smb_revalidate_inode(struct inode *ino)
+smb_revalidate_inode(struct inode *inode)
 {
-	struct dentry * dentry = ino->u.smbfs_i.dentry;
+	time_t last_time;
 	int error = 0;
 
 	pr_debug("smb_revalidate_inode\n");
-	if (!ino)
-		goto bad_no_inode;
-	dentry = ino->u.smbfs_i.dentry;
-#if 0
-	if (dentry)
+	/*
+	 * Check whether we've recently refreshed the inode.
+	 */
+	if (jiffies < inode->u.smbfs_i.oldmtime + HZ/10)
 	{
-		printk("smb_revalidate: checking %s/%s\n",
-			dentry->d_parent->d_name.name, dentry->d_name.name);
+#ifdef SMBFS_DEBUG_VERBOSE
+printk("smb_revalidate_inode: up-to-date, jiffies=%lu, oldtime=%lu\n",
+jiffies, inode->u.smbfs_i.oldmtime);
+#endif
+		goto out;
 	}
+
+	/*
+	 * Save the last modified time, then refresh the inode
+	 */
+	last_time = inode->i_mtime;
+	error = smb_refresh_inode(inode);
+	if (!error)
+	{
+		if (inode->i_mtime != last_time)
+		{
+#ifdef SMBFS_DEBUG_VERBOSE
+printk("smb_revalidate: %s/%s changed, old=%ld, new=%ld\n",
+((struct dentry *)inode->u.smbfs_i.dentry)->d_parent->d_name.name,
+((struct dentry *)inode->u.smbfs_i.dentry)->d_name.name,
+(long) last_time, (long) inode->i_mtime);
 #endif
+			if (!S_ISDIR(inode->i_mode))
+				invalidate_inode_pages(inode);
+			else
+				smb_invalid_dir_cache(inode);
+		}
+	}
 out:
 	return error;
-
-bad_no_inode:
-	printk("smb_revalidate: no inode!\n");
-	error = -EINVAL;
-	goto out;
 }
 
+/*
+ * This is called to update the inode attributes after
+ * we've made changes to a file or directory.
+ */
 int
-smb_refresh_inode(struct inode *ino)
+smb_refresh_inode(struct inode *inode)
 {
-	struct dentry * dentry = ino->u.smbfs_i.dentry;
+	struct dentry * dentry = inode->u.smbfs_i.dentry;
 	struct smb_fattr fattr;
 	int error;
 
 	pr_debug("smb_refresh_inode\n");
-	error = -EIO;
-	if (!dentry) {
+	if (!dentry)
+	{
 		printk("smb_refresh_inode: no dentry, can't refresh\n");
+		error = -EIO;
 		goto out;
 	}
 
-	/* N.B. Should check for changes of important fields! cf. NFS */
+	/*
+	 * Kludge alert ... for some reason we can't get attributes
+	 * for the root directory, so just return success.
+	 */
+	error = 0;
+	if (IS_ROOT(dentry))
+		goto out;
+
 	error = smb_proc_getattr(dentry->d_parent, &(dentry->d_name), &fattr);
 	if (!error)
 	{
-		smb_set_inode_attr(ino, &fattr);
+		smb_renew_times(dentry);
+		/*
+		 * Check whether the type part of the mode changed,
+		 * and don't update the attributes if it did.
+		 */
+		if ((inode->i_mode & S_IFMT) == (fattr.f_mode & S_IFMT))
+			smb_set_inode_attr(inode, &fattr);
+		else
+		{
+			/*
+			 * Big trouble! The inode has become a new object,
+			 * so any operations attempted on it are invalid.
+			 *
+			 * Take a couple of steps to limit damage:
+			 * (1) Mark the inode as bad so that subsequent
+			 *     lookup validations will fail.
+			 * (2) Clear i_nlink so the inode will be released
+			 *     at iput() time. (Unhash it as well?)
+			 * We also invalidate the caches for good measure.
+			 */
+#ifdef SMBFS_PARANOIA
+printk("smb_refresh_inode: %s/%s changed mode, %07o to %07o\n",
+dentry->d_parent->d_name.name, dentry->d_name.name,
+inode->i_mode, fattr.f_mode);
+#endif
+			fattr.f_mode = inode->i_mode; /* save mode */
+			make_bad_inode(inode);
+			inode->i_mode = fattr.f_mode; /* restore mode */
+			inode->i_nlink = 0;
+			/*
+			 * No need to worry about unhashing the dentry: the
+			 * lookup validation will see that the inode is bad.
+			 * But we may need to invalidate the caches ...
+			 */
+			invalidate_inode_pages(inode);
+			smb_invalid_dir_cache(inode);
+			error = -EIO;
+		}
 	}
 out:
 	return error;
+
 }
 
 /*
@@ -192,19 +271,20 @@
 static void
 smb_put_inode(struct inode *ino)
 {
-	struct dentry * dentry;
 	pr_debug("smb_put_inode: count = %d\n", ino->i_count);
 
 	if (ino->i_count > 1) {
+		struct dentry * dentry;
 		/*
 		 * Check whether the dentry still holds this inode. 
-		 * This looks scary, but should work ... d_inode is 
-		 * cleared before iput() in the dcache. 
+		 * This looks scary, but should work ... if this is
+		 * the last use, d_inode == NULL or d_count == 0. 
 		 */
 		dentry = (struct dentry *) ino->u.smbfs_i.dentry;
-		if (dentry && dentry->d_inode != ino) {
+		if (dentry && (dentry->d_inode != ino || dentry->d_count == 0))
+		{
 			ino->u.smbfs_i.dentry = NULL;
-#if 1
+#ifdef SMBFS_DEBUG_VERBOSE
 printk("smb_put_inode: cleared dentry for %s/%s (%ld), count=%d\n",
 dentry->d_parent->d_name.name, dentry->d_name.name, ino->i_ino, ino->i_count);
 #endif
@@ -263,7 +343,6 @@
 	struct smb_mount_data *data = (struct smb_mount_data *)raw_data;
 	struct smb_fattr root;
 	kdev_t dev = sb->s_dev;
-	unsigned char *packet;
 	struct inode *root_inode;
 	struct dentry *dentry;
 
@@ -275,62 +354,65 @@
 	if (data->version != SMB_MOUNT_VERSION)
 		goto out_wrong_data;
 
-	packet = smb_vmalloc(SMB_INITIAL_PACKET_SIZE);	
-	if (!packet)
-		goto out_no_mem;
-
 	lock_super(sb);
 
 	sb->s_blocksize = 1024;	/* Eh...  Is this correct? */
 	sb->s_blocksize_bits = 10;
 	sb->s_magic = SMB_SUPER_MAGIC;
-	sb->s_dev = dev;
+	sb->s_dev = dev; /* shouldn't need this ... */
 	sb->s_op = &smb_sops;
 
 	sb->u.smbfs_sb.sock_file = NULL;
 	sb->u.smbfs_sb.sem = MUTEX;
+	sb->u.smbfs_sb.wait = NULL;
 	sb->u.smbfs_sb.conn_pid = 0;
-	sb->u.smbfs_sb.packet = packet;
-	sb->u.smbfs_sb.packet_size = SMB_INITIAL_PACKET_SIZE;
+	sb->u.smbfs_sb.state = CONN_INVALID; /* no connection yet */
 	sb->u.smbfs_sb.generation = 0;
+	sb->u.smbfs_sb.packet_size = SMB_INITIAL_PACKET_SIZE;	
+	sb->u.smbfs_sb.packet = smb_vmalloc(SMB_INITIAL_PACKET_SIZE);	
+	if (!sb->u.smbfs_sb.packet)
+		goto out_no_mem;
 
 	sb->u.smbfs_sb.m = *data;
 	sb->u.smbfs_sb.m.file_mode = (sb->u.smbfs_sb.m.file_mode &
 				      (S_IRWXU | S_IRWXG | S_IRWXO)) | S_IFREG;
 	sb->u.smbfs_sb.m.dir_mode = (sb->u.smbfs_sb.m.dir_mode &
 				     (S_IRWXU | S_IRWXG | S_IRWXO)) | S_IFDIR;
-
+	/*
+	 * Keep the super block locked while we get the root inode.
+	 */
 	smb_init_root_dirent(&(sb->u.smbfs_sb), &root);
-
-	sb->s_root = NULL;
-	unlock_super(sb);
-
 	root_inode = smb_iget(sb, &root);
 	if (!root_inode)
 		goto out_no_root;
+
 	dentry = d_alloc_root(root_inode, NULL);
 	if (!dentry)
 		goto out_no_root;
 	root_inode->u.smbfs_i.dentry = dentry;
 	sb->s_root = dentry;
+
+	unlock_super(sb);
 	return sb;
 
-out_no_data:
-	printk("smb_read_super: missing data argument\n");
-	goto out;
-out_wrong_data:
-	printk(KERN_ERR "smb_read_super: wrong data argument."
-	       " Recompile smbmount.\n");
-	goto out;
-out_no_mem:
-	pr_debug("smb_read_super: could not alloc packet\n");
-	goto out;
 out_no_root:
-	sb->s_dev = 0; /* iput() might block */
 	printk(KERN_ERR "smb_read_super: get root inode failed\n");
 	iput(root_inode);
-	smb_vfree(packet);
-out:
+	smb_vfree(sb->u.smbfs_sb.packet);
+	goto out_unlock;
+out_no_mem:
+	printk("smb_read_super: could not alloc packet\n");
+	goto out_unlock;
+out_wrong_data:
+	printk(KERN_ERR "smb_read_super: wrong data argument."
+	       " Recompile smbmount.\n");
+	goto out_fail;
+out_no_data:
+	printk("smb_read_super: missing data argument\n");
+	goto out_fail;
+out_unlock:
+	unlock_super(sb);
+out_fail:
 	sb->s_dev = 0;
 	MOD_DEC_USE_COUNT;
 	return NULL;
@@ -355,8 +437,9 @@
 int
 smb_notify_change(struct inode *inode, struct iattr *attr)
 {
+	struct smb_sb_info *server = SMB_SERVER(inode);
 	struct dentry *dentry = inode->u.smbfs_i.dentry;
-	int error;
+	int error, refresh = 0;
 
 	error = -EIO;
 	if (!dentry)
@@ -365,40 +448,54 @@
 		goto out;
 	}
 
+	/*
+	 * Make sure our inode is up-to-date ...
+	 */
+	error = smb_revalidate_inode(inode);
+	if (error)
+		goto out;
+
 	if ((error = inode_change_ok(inode, attr)) < 0)
 		goto out;
 
 	error = -EPERM;
-	if (((attr->ia_valid & ATTR_UID) &&
-	     (attr->ia_uid != SMB_SERVER(inode)->m.uid)))
+	if (((attr->ia_valid & ATTR_UID) && (attr->ia_uid != server->m.uid)))
 		goto out;
 
-	if (((attr->ia_valid & ATTR_GID) &&
-	     (attr->ia_uid != SMB_SERVER(inode)->m.gid)))
+	if (((attr->ia_valid & ATTR_GID) && (attr->ia_uid != server->m.gid)))
 		goto out;
 
 	if (((attr->ia_valid & ATTR_MODE) &&
 	(attr->ia_mode & ~(S_IFREG | S_IFDIR | S_IRWXU | S_IRWXG | S_IRWXO))))
 		goto out;
 
-	/*
-	 * Assume success and invalidate the parent's dir cache
-	 */ 
-	smb_invalid_dir_cache(dentry->d_parent->d_inode);
-
 	if ((attr->ia_valid & ATTR_SIZE) != 0)
 	{
-		if ((error = smb_open(dentry, O_WRONLY)) < 0)
+		error = smb_open(dentry, O_WRONLY);
+		if (error)
 			goto out;
-
-		if ((error = smb_proc_trunc(SMB_SERVER(inode),
-					    inode->u.smbfs_i.fileid,
-					    attr->ia_size)) < 0)
+#ifdef SMBFS_DEBUG_VERBOSE
+printk("smb_notify_change: changing %s/%s, old size=%ld, new size=%ld\n",
+dentry->d_parent->d_name.name, dentry->d_name.name,
+(long) inode->i_size, (long) attr->ia_size);
+#endif
+		error = smb_proc_trunc(server, inode->u.smbfs_i.fileid,
+					 attr->ia_size);
+		if (error)
 			goto out;
+		/*
+		 * We don't implement an i_op->truncate operation,
+		 * so we have to update the page cache here.
+		 */
+		if (attr->ia_size < inode->i_size) {
+			truncate_inode_pages(inode, attr->ia_size);
+			inode->i_size = attr->ia_size;
+		}
+		refresh = 1;
 	}
+
 	if ((attr->ia_valid & (ATTR_CTIME | ATTR_MTIME | ATTR_ATIME)) != 0)
 	{
-
 		struct smb_fattr fattr;
 
 		fattr.attr = 0;
@@ -417,15 +514,25 @@
 		if ((attr->ia_valid & ATTR_ATIME) != 0)
 			fattr.f_atime = attr->ia_atime;
 
-		error = smb_proc_setattr(SMB_SERVER(inode), dentry, &fattr);
-		if (error >= 0)
-		{
-			inode->i_ctime = fattr.f_ctime;
-			inode->i_mtime = fattr.f_mtime;
-			inode->i_atime = fattr.f_atime;
-		}
+		error = smb_proc_setattr(server, dentry, &fattr);
+		if (error)
+			goto out;
+		refresh = 1;
 	}
+	error = 0;
+
 out:
+	if (refresh)
+	{
+		/*
+		 * N.B. Currently we're only using the dir cache for
+		 * file names, so we don't need to invalidate here.
+		 */
+#if 0
+		smb_invalid_dir_cache(dentry->d_parent->d_inode);
+#endif
+		smb_refresh_inode(inode);
+	}
 	return error;
 }
 
@@ -461,7 +568,6 @@
 	smb_current_vmalloced = 0;
 #endif
 
-	smb_init_dir_cache();
 	read_semaphore = MUTEX;
 
 	return init_smb_fs();
@@ -471,7 +577,6 @@
 cleanup_module(void)
 {
 	pr_debug("smbfs: cleanup_module called\n");
-	smb_free_dir_cache();
 	unregister_filesystem(&smb_fs_type);
 #ifdef DEBUG_SMB_MALLOC
 	printk(KERN_DEBUG "smb_malloced: %d\n", smb_malloced);

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen, slshen@lbl.gov