[Patch] Shorter patch for smbfs 2.2.16

klaus-georg.adams@rwg.de
Wed, 28 Jun 2000 08:55:20 +0200


Am 27.06.2000 19:58:44 schrieb urban:
> On Tue, 27 Jun 2000 klaus-georg.adams@rwg.de wrote:
>
> >
> > Hi Andrew,
> > your patch from 2.2.15 to 2.2.16, backing out the older protocol levels
breaks
> > reading from an OS/2 LAN Server.
> > The appended patch fixes things for me (against 2.2.16).
>
> This backs out a lot of desired changes. For example I think that 'rm -rf'
> on large directories will no longer work if you apply this because you
> change the cache to the old behaviour.

Yes, this was simply going back to 2.2.15 and adding a fix for the same problem
mentioned in
the mail about 2.4.0-test2.

>
> > - * Jan 2000, cpg@aladdin.de
> > - * - added posix semantics for unlink
> > - * March 2000, tridge
> > - * - removed support for old protocol levels. It didn't work
anyway
> > and
> > - * was cluttering things up a lot.
> > + * 20/03/00 (chrisp)
> > + * - fixed FINDFIRST flags for OS/2 Server
> > + * - added lastname/mask stuff back (OS/2 needs it)
>
> And you remove the nice "posix semantics for unlink".
>
> It looks like you have simply gone back to the 2.2.14/15 version, and then
> added findfirst/lastname things. Maybe you could make a smaller patch that
> only adds back the parts necessary for it to work with OS/2? (or not,
> Tridge can probably see that anyway).

That's what I hoped. I had only seen the ChangeLog entry from tridge regarding
older protocol levels. I hadn't realized that there were other fixes.
The parts that I need for reading from an OS/2 LANman server are:
- support for older protocols (I'm not sure which one exactly)
- the lastname/mask stuff
- the FINDFIRST flags in readdir_long

I have appended a shorter patch, which still works for me, but leaves in most
of the other stuff from 2.2.16

>
> Re: Your other email about listing long directories. I suggested something
> similar to your patch for fixing this in 2.2, but the preferred change was
> to use infolevel 260 instead of 259.

Note: my problem arises with an OS/2 server at a protocol level < NT1, thus
infolevel == 1

> 2.3/2.4-test hasn't received any of the changes that 2.2 has, including
> long dirs, posix unlink, rm -rf fixes. I have a patch vs 2.3.99-pre9 to
> upgrade smbfs to 2.2, that haven't made it past the maintainer yet.

Would you be so kind as to send it to me for testing?
Thanks!

PS: the same problem wrt reading long directories from an OS/2 server
existed in smbclient from samba 2.0.6.
Tridge has accepted a patch from Christoph Pfisterer to samba (which is the
equivalent to
what I posted above) into 2.0.7, so it can't be all wrong.

--
kga

Index: fs/smbfs/dir.c =================================================================== RCS file: /usr/src/cvsroot/linux/fs/smbfs/dir.c,v retrieving revision 1.1.1.5 diff -u -r1.1.1.5 dir.c --- fs/smbfs/dir.c 2000/06/13 09:23:24 1.1.1.5 +++ fs/smbfs/dir.c 2000/06/28 06:36:12 @@ -178,6 +178,16 @@ printk("smb_dir_open: (%s/%s)\n", dentry->d_parent->d_name.name, file->f_dentry->d_name.name); #endif + /* + * Directory timestamps in the core protocol aren't updated + * when a file is added, so we give them a very short TTL. + */ + if (server->opt.protocol < SMB_PROTOCOL_LANMAN2) + { + unsigned long age = jiffies - dir->u.smbfs_i.oldmtime; + if (age > 2*HZ) + smb_invalid_dir_cache(dir); + }

if (server->conn_pid) error = smb_revalidate_inode(dentry); @@ -420,6 +430,7 @@ dentry->d_parent->d_name.name, dentry->d_name.name, mode); #endif

+ smb_invalid_dir_cache(dir); error = smb_proc_create(dentry, 0, CURRENT_TIME, &fileid); if (!error) { @@ -440,6 +451,7 @@ { int error;

+ smb_invalid_dir_cache(dir); error = smb_proc_mkdir(dentry); if (!error) { @@ -466,6 +478,7 @@ if (!list_empty(&dentry->d_hash)) goto out;

+ smb_invalid_dir_cache(dir); error = smb_proc_rmdir(dentry);

out: @@ -482,6 +495,7 @@ */ smb_close(dentry->d_inode);

+ smb_invalid_dir_cache(dir); error = smb_proc_unlink(dentry); if (!error) { @@ -519,6 +533,8 @@ d_delete(new_dentry); }

+ smb_invalid_dir_cache(old_dir); + smb_invalid_dir_cache(new_dir); error = smb_proc_mv(old_dentry, new_dentry); if (!error) { Index: fs/smbfs/inode.c =================================================================== RCS file: /usr/src/cvsroot/linux/fs/smbfs/inode.c,v retrieving revision 1.1.1.3 diff -u -r1.1.1.3 inode.c --- fs/smbfs/inode.c 2000/06/13 09:23:24 1.1.1.3 +++ fs/smbfs/inode.c 2000/06/28 06:36:12 @@ -276,6 +276,8 @@ #endif if (!S_ISDIR(inode->i_mode)) invalidate_inode_pages(inode); + else + smb_invalid_dir_cache(inode); } out: return error; @@ -379,6 +381,13 @@ mnt->dir_mode &= (S_IRWXU | S_IRWXG | S_IRWXO); mnt->dir_mode |= S_IFDIR; sb->u.smbfs_sb.mnt = mnt; + /* + * Display the enabled options + */ + if (mnt->version & SMB_FIX_OLDATTR) + printk("SMBFS: Using core getattr (Win 95 speedup)\n"); + else if (mnt->version & SMB_FIX_DIRATTR) + printk("SMBFS: Using dir ff getattr\n");

/* * Keep the super block locked while we get the root inode. @@ -501,7 +510,9 @@ if ((attr->ia_valid & ATTR_ATIME) != 0) { fattr.f_atime = attr->ia_atime; - changed = 1; + /* Earlier protocols don't have an access time */ + if (server->opt.protocol >= SMB_PROTOCOL_LANMAN2) + changed = 1; } if (changed) { Index: fs/smbfs/proc.c =================================================================== RCS file: /usr/src/cvsroot/linux/fs/smbfs/proc.c,v retrieving revision 1.1.1.4 diff -u -r1.1.1.4 proc.c --- fs/smbfs/proc.c 2000/06/13 09:23:25 1.1.1.4 +++ fs/smbfs/proc.c 2000/06/28 06:36:12 @@ -14,9 +14,9 @@ * - got rid of resume_key * Jan 2000, cpg@aladdin.de * - added posix semantics for unlink - * March 2000, tridge - * - removed support for old protocol levels. It didn't work anyway and - * was cluttering things up a lot. + * 20/03/00 (chrisp) + * - fixed FINDFIRST flags for OS/2 Server + * - added lastname/mask stuff back (OS/2 needs it) */

#include <linux/types.h> @@ -62,6 +62,28 @@ static int smb_proc_do_getattr(struct dentry *dir, struct smb_fattr *fattr,struct smb_sb_info *server);

+static void +str_upper(char *name, int len) +{ + while (len--) + { + if (*name >= 'a' && *name <= 'z') + *name -= ('a' - 'A'); + name++; + } +} + +static void +str_lower(char *name, int len) +{ + while (len--) + { + if (*name >= 'A' && *name <= 'Z') + *name += ('a' - 'A'); + name++; + } +} + /* reverse a string inline. This is used by the dircache walking routines */ static void reverse_string(char *buf, int len) { char c; @@ -149,8 +171,13 @@ static char *smb_encode_path(struct smb_sb_info *server, char *buf, struct dentry *dir, struct qstr *name) { + char *start = buf; + buf += smb_build_path(dir, name, buf);

+ if (server->opt.protocol <= SMB_PROTOCOL_COREPLUS) + str_upper(start, buf - start); + return buf; }

@@ -625,11 +652,6 @@ !capable(CAP_SYS_ADMIN)) goto out;

- if (opt->protocol < SMB_PROTOCOL_NT1) { - printk(KERN_NOTICE " smbfs: protocols older than NT1 are not suppported\n"); - goto out; - } - error = -EBADF; filp = fget(opt->fd); if (!filp) @@ -652,7 +674,8 @@

/* now that we have an established connection we can detect the server type and enable bug workarounds */ - if ((server->opt.max_xmit < 0x1000) && + if (server->opt.protocol == SMB_PROTOCOL_NT1 && + (server->opt.max_xmit < 0x1000) && !(server->opt.capabilities & SMB_CAP_NT_SMBS)) { server->mnt->version |= SMB_FIX_WIN95; #ifdef SMBFS_DEBUG_VERBOSE @@ -706,8 +729,11 @@ WSET(buf, smb_uid, server->opt.server_uid); WSET(buf, smb_mid, 1);

- *(buf+smb_flg) = 0x8; - WSET(buf, smb_flg2, 0x3); + if (server->opt.protocol > SMB_PROTOCOL_CORE) + { + *(buf+smb_flg) = 0x8; + WSET(buf, smb_flg2, 0x3); + } *p++ = wct; /* wct */ p += 2 * wct; WSET(p, 0, bcc); @@ -893,7 +919,9 @@ * If the file is open with write permissions, * update the time stamps to sync mtime and atime. */ - if (ino->u.smbfs_i.access != SMB_O_RDONLY) { + if ((server->opt.protocol >= SMB_PROTOCOL_LANMAN2) && + !(ino->u.smbfs_i.access == SMB_O_RDONLY)) + { struct smb_fattr fattr; smb_get_inode_attr(ino, &fattr); smb_proc_setattr_ext(server, ino, &fattr); @@ -903,8 +931,11 @@ ino->i_mtime); ino->u.smbfs_i.cache_valid &= ~SMB_F_LOCALWRITE; /* - * Force a revalidation after closing + * Force a revalidation after closing ... some servers + * don't post the size until the file has been closed. */ + if (server->opt.protocol < SMB_PROTOCOL_NT1) + ino->u.smbfs_i.oldmtime = 0; ino->u.smbfs_i.closed = jiffies; } return result; @@ -1310,8 +1341,177 @@

/* - * Interpret a long filename structure using info level 260 + * Note that we are now returning the name as a reference to avoid + * an extra copy, and that the upper/lower casing is done in place. + * + * Bugs Noted: + * (1) Pathworks servers may pad the name with extra spaces. + */ +static __u8 * +smb_decode_dirent(struct smb_sb_info *server, __u8 *p, + struct cache_dirent *entry) +{ + int len; + + /* + * SMB doesn't have a concept of inode numbers ... + */ + entry->ino = 0; + + p += SMB_STATUS_SIZE; /* reserved (search_status) */ + entry->name = p + 9; + len = strlen(entry->name); + if (len > 12) + { + len = 12; + } + /* + * Trim trailing blanks for Pathworks servers + */ + while (len > 2 && entry->name[len-1] == ' ') + len--; + entry->len = len; + + switch (server->opt.case_handling) + { + case SMB_CASE_UPPER: + str_upper(entry->name, len); + break; + case SMB_CASE_LOWER: + str_lower(entry->name, len); + break; + default: + break; + } + pr_debug("smb_decode_dirent: len=%d, name=%s\n", len, entry->name); + return p + 22; +} + +/* This routine is used to read in directory entries from the network. + Note that it is for short directory name seeks, i.e.: protocol < + SMB_PROTOCOL_LANMAN2 */ + +static int +smb_proc_readdir_short(struct smb_sb_info *server, struct dentry *dir, int fpos, + void *cachep) +{ + char *p; + int result; + int i, first, entries_seen, entries; + int entries_asked = (server->opt.max_xmit - 100) / SMB_DIRINFO_SIZE; + __u16 bcc; + __u16 count; + char status[SMB_STATUS_SIZE]; + static struct qstr mask = { "*.*", 3, 0 }; + +#ifdef SMBFS_DEBUG_VERBOSE +printk("smb_proc_readdir_short: %s/%s, pos=%d\n", + DENTRY_PATH(dir), fpos); +#endif + + smb_lock_server(server); + + /* N.B. We need to reinitialize the cache to restart */ + retry: + smb_init_dircache(cachep); + first = 1; + entries = 0; + entries_seen = 2; /* implicit . and .. */ + + while (1) + { + p = smb_setup_header(server, SMBsearch, 2, 0); + WSET(server->packet, smb_vwv0, entries_asked); + WSET(server->packet, smb_vwv1, aDIR); + *p++ = 4; + if (first == 1) + { + p = smb_encode_path(server, p, dir, &mask); + *p++ = 5; + WSET(p, 0, 0); + p += 2; + first = 0; + } else + { + *p++ = 0; + *p++ = 5; + WSET(p, 0, SMB_STATUS_SIZE); + p += 2; + memcpy(p, status, SMB_STATUS_SIZE); + p += SMB_STATUS_SIZE; + } + + smb_setup_bcc(server, p); + + result = smb_request_ok(server, SMBsearch, 1, -1); + if (result < 0) + { + if ((server->rcls == ERRDOS) && + (server->err == ERRnofiles)) + break; + if (smb_retry(server)) + goto retry; + goto unlock_return; + } + p = SMB_VWV(server->packet); + count = WVAL(p, 0); + if (count <= 0) + break; + + result = -EIO; + bcc = WVAL(p, 2); + if (bcc != count * SMB_DIRINFO_SIZE + 3) + goto unlock_return; + p += 7; + + /* Read the last entry into the status field. */ + memcpy(status, + SMB_BUF(server->packet) + 3 + + (count - 1) * SMB_DIRINFO_SIZE, + SMB_STATUS_SIZE); + + /* Now we are ready to parse smb directory entries. */ + + for (i = 0; i < count; i++) + { + struct cache_dirent this_ent, *entry = &this_ent; + + p = smb_decode_dirent(server, p, entry); + if (entries_seen == 2 && entry->name[0] == '.') + { + if (entry->len == 1) + continue; + if (entry->name[1] == '.' && entry->len == 2) + continue; + } + if (entries_seen >= fpos) + { + pr_debug("smb_proc_readdir: fpos=%u\n", + entries_seen); + smb_add_to_cache(cachep, entry, entries_seen); + entries++; + } else + { +#ifdef SMBFS_DEBUG_VERBOSE +printk("smb_proc_readdir: skipped, seen=%d, i=%d, fpos=%d\n", +entries_seen, i, fpos); +#endif + } + entries_seen++; + } + } + result = entries; + + unlock_return: + smb_unlock_server(server); + return result; +}

+/* + * Interpret a long filename structure using the specified info level: + * level 1 for anything below NT1 protocol + * level 260 for NT1 protocol + * * We return a reference to the name string to avoid copying, and perform * any needed upper/lower casing in place.

@@ -1320,7 +1520,7 @@ */ static char * smb_decode_long_dirent(struct smb_sb_info *server, char *p, - struct cache_dirent *entry) + struct cache_dirent *entry, int level) { char *result; unsigned int len = 0; @@ -1330,19 +1530,47 @@ */ entry->ino = 0;

- result = p + WVAL(p, 0); - len = DVAL(p, 60); - if (len > 255) len = 255; - /* NT4 null terminates */ - entry->name = p + 94; - if (len && entry->name[len-1] == '\0') - len--; - entry->len = len; + switch (level) + { + case 1: + len = *((unsigned char *) p + 22); + entry->len = len; + entry->name = p + 23; + result = p + 24 + len; + break; + + case 260: /* SMB_FIND_FILE_BOTH_DIRECTORY_INFO = 0x104 */ + result = p + WVAL(p, 0); + len = DVAL(p, 60); + if (len > 255) len = 255; + /* NT4 null terminates */ + entry->name = p + 94; + if (len && entry->name[len-1] == '\0') + len--; + entry->len = len; #ifdef SMBFS_DEBUG_VERBOSE printk(KERN_DEBUG "smb_decode_long_dirent: info 260 at %p, len=%d, name=%s\n", p, entry->len, entry->name); #endif + break;

+ default: + printk("smb_decode_long_dirent: Unknown level %d\n", level); + result = p + WVAL(p, 0); + } + + switch (server->opt.case_handling) + { + case SMB_CASE_UPPER: + str_upper(entry->name, len); + break; + case SMB_CASE_LOWER: + str_lower(entry->name, len); + break; + default: + break; + } + return result; }

@@ -1366,6 +1594,8 @@ char *p, *mask, *param = server->temp_buf; __u16 command; int first, entries, entries_seen; + + /* Both NT and OS/2 accept info level 1 (but see note below). */ int info_level = 260; const int max_matches = 512;

@@ -1375,11 +1605,18 @@ int resp_param_len = 0; int ff_searchcount = 0; int ff_eos = 0; + int ff_lastname = 0; int ff_dir_handle = 0; int loop_count = 0; int mask_len, i, result; static struct qstr star = { "*", 1, 0 };

+ /* + * use info level 1 for older servers that don't do 260 + */ + if (server->opt.protocol < SMB_PROTOCOL_NT1) + info_level = 1; + smb_lock_server(server);

retry: @@ -1416,16 +1653,11 @@ command = TRANSACT2_FINDFIRST; WSET(param, 0, aSYSTEM | aHIDDEN | aDIR); WSET(param, 2, max_matches); /* max count */ - WSET(param, 4, - SMB_CONTINUE_BIT|SMB_CLOSE_IF_END); + WSET(param, 4, SMB_CLOSE_IF_END); WSET(param, 6, info_level); DSET(param, 8, 0); } else { - /* we don't need the mask after the first bit */ - mask_len = 0; - mask[0] = 0; - command = TRANSACT2_FINDNEXT; #ifdef SMBFS_DEBUG_VERBOSE printk(KERN_DEBUG "smb_proc_readdir_long: handle=0x%X, mask=%s\n", @@ -1485,10 +1717,12 @@ ff_dir_handle = WVAL(resp_param, 0); ff_searchcount = WVAL(resp_param, 2); ff_eos = WVAL(resp_param, 4); + ff_lastname = WVAL(resp_param, 8); } else { ff_searchcount = WVAL(resp_param, 0); ff_eos = WVAL(resp_param, 2); + ff_lastname = WVAL(resp_param, 6); }

if (ff_searchcount == 0) @@ -1496,6 +1730,18 @@ break; }

+ if (ff_lastname > 0 && info_level == 1) + { + mask_len = *(resp_data + ff_lastname); + memcpy(mask, resp_data + ff_lastname + 1, + mask_len); + mask[mask_len] = 0; + } else + { + mask_len = 0; + mask[0] = 0; + } + /* Now we are ready to parse smb directory entries. */

/* point to the data bytes */ @@ -1504,7 +1750,8 @@ { struct cache_dirent this_ent, *entry = &this_ent;

- p = smb_decode_long_dirent(server, p, entry); + p = smb_decode_long_dirent(server, p, entry, + info_level);

/* ignore . and .. from the server */ if (entries_seen == 2 && entry->name[0] == '.') @@ -1540,7 +1787,98 @@ struct smb_sb_info *server;

server = server_from_dentry(dir); - return smb_proc_readdir_long(server, dir, fpos, cachep); + if (server->opt.protocol >= SMB_PROTOCOL_LANMAN2) + return smb_proc_readdir_long(server, dir, fpos, cachep); + else + return smb_proc_readdir_short(server, dir, fpos, cachep); +} + +/* + * This version uses the trans2 TRANSACT2_FINDFIRST message + * to get the attribute data. + * Note: called with the server locked. + * + * Bugs Noted: + */ +static int +smb_proc_getattr_ff(struct smb_sb_info *server, struct dentry *dentry, + struct smb_fattr *fattr) +{ + char *param = server->temp_buf, *mask = param + 12; + __u16 date, time; + unsigned char *resp_data = NULL; + unsigned char *resp_param = NULL; + int resp_data_len = 0; + int resp_param_len = 0; + int mask_len, result; + +retry: + mask_len = smb_encode_path(server, mask, dentry, NULL) - mask; +#ifdef SMBFS_DEBUG_VERBOSE +printk("smb_proc_getattr_ff: name=%s, len=%d\n", mask, mask_len); +#endif + WSET(param, 0, aSYSTEM | aHIDDEN | aDIR); + WSET(param, 2, 1); /* max count */ + WSET(param, 4, 1); /* close after this call */ + WSET(param, 6, 1); /* info_level */ + DSET(param, 8, 0); + + result = smb_trans2_request(server, TRANSACT2_FINDFIRST, + 0, NULL, 12 + mask_len + 1, param, + &resp_data_len, &resp_data, + &resp_param_len, &resp_param); + if (result < 0) + { + if (smb_retry(server)) + goto retry; + goto out; + } + if (server->rcls != 0) + { + result = -smb_errno(server); +#ifdef SMBFS_PARANOIA +if (result != -ENOENT) +printk("smb_proc_getattr_ff: error for %s, rcls=%d, err=%d\n", +mask, server->rcls, server->err); +#endif + goto out; + } + /* Make sure we got enough data ... */ + result = -EINVAL; + if (resp_data_len < 22 || WVAL(resp_param, 2) != 1) + { +#ifdef SMBFS_PARANOIA +printk("smb_proc_getattr_ff: bad result for %s, len=%d, count=%d\n", +mask, resp_data_len, WVAL(resp_param, 2)); +#endif + goto out; + } + + /* + * Decode the response into the fattr ... + */ + date = WVAL(resp_data, 0); + time = WVAL(resp_data, 2); + fattr->f_ctime = date_dos2unix(server, date, time); + + date = WVAL(resp_data, 4); + time = WVAL(resp_data, 6); + fattr->f_atime = date_dos2unix(server, date, time); + + date = WVAL(resp_data, 8); + time = WVAL(resp_data, 10); + fattr->f_mtime = date_dos2unix(server, date, time); +#ifdef SMBFS_DEBUG_VERBOSE +printk("smb_proc_getattr_ff: name=%s, date=%x, time=%x, mtime=%ld\n", +mask, date, time, fattr->f_mtime); +#endif + fattr->f_size = DVAL(resp_data, 12); + /* ULONG allocation size */ + fattr->attr = WVAL(resp_data, 20); + result = 0; + +out: + return result; }

/* @@ -1687,12 +2025,20 @@

/* * Select whether to use core or trans2 getattr. - * Win 95 appears to break with the trans2 getattr. - */ - if (server->mnt->version & SMB_FIX_WIN95) { - result = smb_proc_getattr_core(server, dir, fattr); + */ + if (server->opt.protocol >= SMB_PROTOCOL_LANMAN2) { + /* + * Win 95 appears to break with the trans2 getattr. + */ + if (server->mnt->version & (SMB_FIX_OLDATTR|SMB_FIX_WIN95)) + goto core_attr; + if (server->mnt->version & SMB_FIX_DIRATTR) + result = smb_proc_getattr_ff(server, dir, fattr); + else + result = smb_proc_getattr_trans2(server, dir, fattr); } else { - result = smb_proc_getattr_trans2(server, dir, fattr); + core_attr: + result = smb_proc_getattr_core(server, dir, fattr); }

smb_finish_dirent(server, fattr); @@ -1906,7 +2252,8 @@ #endif smb_lock_server(server); /* setting the time on a Win95 server fails (tridge) */ - if (!(server->mnt->version & SMB_FIX_WIN95)) + if (server->opt.protocol >= SMB_PROTOCOL_LANMAN2 && + !(server->mnt->version & SMB_FIX_WIN95)) { if (smb_is_open(inode) && inode->u.smbfs_i.access != SMB_O_RDONLY) - To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majordomo@vger.rutgers.edu Please read the FAQ at http://www.tux.org/lkml/