smbfs patch for inode numbers

Gordon Chaffee (chaffee@odie.cs.berkeley.edu)
Thu, 15 Feb 1996 14:07:03 -0800


I've been using smbfs on and off ever since it has come out, but I've
run into a number of problems because of its lack of real inode support.
I've been using an NT box as a fileserver and building source code on
both NT and Linux simultaneously. However, not everything works perfectly.
If you so something like

touch file1 file2
cp file1 file2

you will get an error message that says the two files are the same.
Obviously, this is wrong, but the inode numbers are the name. I've
created a patch that fixes this problem be creating inode number that
are based on the directory name and the filename. It uses 16 bits for
the directory name and 16 bits for the filename. This patch was against
1.3.59, but it will probably still work against newer kernels.

Gordon Chaffee
chaffee@bugs-bunny.cs.berkeley.edu

diff -u --recursive --new-file linux-1.3.59/fs/smbfs/dir.c linux/fs/smbfs/dir.c
--- linux-1.3.59/fs/smbfs/dir.c Tue Jan 2 04:18:43 1996
+++ linux/fs/smbfs/dir.c Fri Feb 2 15:50:29 1996
@@ -18,6 +18,12 @@
#define NAME_OFFSET(de) ((int) ((de)->d_name - (char *) (de)))
#define ROUND_UP(x) (((x)+3) & ~3)

+static unsigned long
+get_hash_value(const char *dirname, const char *entryname);
+
+static unsigned long
+get_unique_hash_value(struct inode *inode, const char *entryname);
+
static int
smb_dir_read(struct inode *inode, struct file *filp, char *buf, int count);

@@ -34,7 +40,8 @@
char *path, int *res_len);

static struct inode *
-smb_iget(struct inode *dir, char *path, struct smb_dirent *finfo);
+smb_iget(struct inode *dir, char *path, const char *ename,
+ struct smb_dirent *finfo);

static void
put_pname(char *path);
@@ -261,6 +268,7 @@
int len;
struct smb_inode_info *ino_info;
char complete_path[SMB_MAXPATHLEN];
+ unsigned long hashval;

len = strlen(entry->path);
if ((result = get_pname_static(inode, entry->path, len,
@@ -270,18 +278,17 @@

ino_info = smb_find_inode(server, complete_path);

- /* Some programs seem to be confused about a zero
- inode number, so we set it to one. Thanks to
- Gordon Chaffee for this one. */
if (ino_info == NULL) {
- ino_info = (struct smb_inode_info *) 1;
- }
+ hashval = get_unique_hash_value(inode, entry->path);
+ } else {
+ hashval = ino_info->i_ino;
+ }

DDPRINTK("smb_readdir: entry->path = %s\n", entry->path);
DDPRINTK("smb_readdir: entry->f_pos = %ld\n", entry->f_pos);

if (filldir(dirent, entry->path, len,
- entry->f_pos, (ino_t)ino_info) < 0) {
+ entry->f_pos, (ino_t)hashval) < 0) {
break;
}

@@ -456,18 +463,86 @@
smb_kfree_s(path, 0);
}

+/*
+ * This hash function is take from John Ousterhout's Tcl package
+ */
+static unsigned long
+get_hash_value(const char *dirname, const char *entryname)
+{
+ const char *p;
+ char c;
+ unsigned short hash_hi, hash_lo;
+ unsigned long hashval;
+
+ p = dirname;
+ hash_hi = 0;
+ while (1) {
+ c = *p;
+ p++;
+ if (c == 0)
+ break;
+ hash_hi += (hash_hi<<3) + c;
+ }
+
+ p = entryname;
+ hash_lo = 0;
+ while (1) {
+ c = *p;
+ p++;
+ if (c == 0)
+ break;
+ hash_lo += (hash_lo<<3) + c;
+ }
+ hashval = (hash_hi << 16) | hash_lo;
+ return hashval;
+}
+
+static unsigned long
+get_unique_hash_value(struct inode *dir, const char *entryname)
+{
+ char *parentname = SMB_INOP(dir)->finfo.path;
+ unsigned long hashval;
+
+ hashval = get_hash_value(parentname, entryname);
+ while (1) {
+ if (! smb_get_inode_info(dir, hashval)) {
+ return hashval;
+ }
+ hashval++;
+ }
+}
+
+struct smb_inode_info *
+smb_get_inode_info(struct inode *inode, unsigned long i_ino)
+{
+ struct smb_server *server = SMB_SERVER(inode);
+ struct smb_inode_info *result;
+
+ result = &(server->root);
+ do {
+ if (result->i_ino == i_ino) {
+ return result;
+ }
+ result = result->next;
+ } while (result != &(server->root));
+
+ return NULL;
+}
+
/* Insert a NEW smb_inode_info into the inode tree of our filesystem,
under dir. The caller must assure that it's not already there. We
assume that path is allocated for us. */

static struct inode *
-smb_iget(struct inode *dir, char *path, struct smb_dirent *finfo)
+smb_iget(struct inode *dir, char *path, const char *ename,
+ struct smb_dirent *finfo)
{
struct smb_dirent newent = { 0 };
struct inode *inode;
int error, len;
struct smb_inode_info *new_inode_info;
struct smb_inode_info *root;
+ unsigned long hashval;

if (!dir) {
printk("smb_iget: dir is NULL\n");
@@ -505,6 +580,9 @@
new_inode_info->nused = 0;
new_inode_info->dir = SMB_INOP(dir);

+ hashval = get_unique_hash_value(dir, ename);
+ new_inode_info->i_ino = hashval;
+
new_inode_info->finfo = *finfo;
new_inode_info->finfo.opened = 0;
new_inode_info->finfo.path = path;
@@ -523,7 +601,7 @@
root->next->prev = new_inode_info;
root->next = new_inode_info;

- if (!(inode = iget(dir->i_sb, (int)new_inode_info))) {
+ if (!(inode = iget(dir->i_sb, (int) hashval /* new_inode_info */))) {
printk("smb_iget: iget failed!");
return NULL;
}
@@ -569,6 +647,7 @@
root->nused = 1;
root->dir = NULL;
root->next = root->prev = root;
+ root->i_ino = 0;
return;
}

@@ -698,7 +777,7 @@
/* Here we convert the inode_info address into an
inode number */

- *result = iget(dir->i_sb, (int)result_info);
+ *result = iget(dir->i_sb, (int)result_info->i_ino);
iput(dir);

if (*result == NULL) {
@@ -747,7 +826,7 @@
}
}

- if (!(*result = smb_iget(dir, name, &finfo))) {
+ if (!(*result = smb_iget(dir, name, __name, &finfo))) {
put_pname(name);
iput(dir);
return -EACCES;
@@ -795,7 +874,7 @@

smb_invalid_dir_cache(dir->i_ino);

- if (!(*result = smb_iget(dir, path, &entry)) < 0) {
+ if (!(*result = smb_iget(dir, path, name, &entry)) < 0) {
put_pname(path);
iput(dir);
return error;
diff -u --recursive --new-file linux-1.3.59/fs/smbfs/inode.c linux/fs/smbfs/inode.c
--- linux-1.3.59/fs/smbfs/inode.c Fri Feb 2 18:50:05 1996
+++ linux/fs/smbfs/inode.c Fri Feb 2 15:50:20 1996
@@ -53,12 +53,13 @@
inode->i_ino. Just to make sure everything went well, we
check it's there. */

- struct smb_inode_info *inode_info
- = (struct smb_inode_info *)(inode->i_ino);
+ struct smb_inode_info *inode_info, *root, *check_info;
+
+ inode_info = smb_get_inode_info(inode, inode->i_ino);

#if 1
- struct smb_inode_info *root = &(SMB_SERVER(inode)->root);
- struct smb_inode_info *check_info = root;
+ root = &(SMB_SERVER(inode)->root);
+ check_info = root;

do {
if (inode_info == check_info) {
@@ -287,7 +288,7 @@

DPRINTK("smb_read_super: SMB_SBP(sb) = %x\n", (int)SMB_SBP(sb));

- if (!(sb->s_mounted = iget(sb, (int)&(server->root)))) {
+ if (!(sb->s_mounted = iget(sb, (int)(server->root.i_ino)))) {
sb->s_dev = 0;
printk("smb_read_super: get root inode failed\n");
smb_kfree_s(server->packet, server->max_xmit);
diff -u --recursive --new-file linux-1.3.59/include/linux/smb_fs.h linux/include/linux/smb_fs.h
--- linux-1.3.59/include/linux/smb_fs.h Fri Feb 2 18:50:11 1996
+++ linux/include/linux/smb_fs.h Fri Feb 2 12:44:37 1996
@@ -106,6 +106,7 @@
void smb_invalid_dir_cache(unsigned long ino);
void smb_invalidate_all_inodes(struct smb_server *server);
void smb_free_dir_cache(void);
+struct smb_inode_info *smb_get_inode_info(struct inode *inode, unsigned long i_ino);

/* linux/fs/smbfs/ioctl.c */
int smb_ioctl (struct inode * inode, struct file * filp,
diff -u --recursive --new-file linux-1.3.59/include/linux/smb_fs_i.h linux/include/linux/smb_fs_i.h
--- linux-1.3.59/include/linux/smb_fs_i.h Tue Jan 2 04:18:44 1996
+++ linux/include/linux/smb_fs_i.h Fri Feb 2 12:25:36 1996
@@ -28,6 +28,7 @@
struct smb_inode_info *dir;
struct smb_inode_info *next, *prev;
struct smb_dirent finfo;
+ unsigned long i_ino;
};

#endif