diff -urN -X dontdiff linux-2.4.16/fs/smbfs/ChangeLog ../linux-2.4.16/fs/smbfs/ChangeLog --- linux-2.4.16/fs/smbfs/ChangeLog Wed Oct 3 10:03:34 2001 +++ ../linux-2.4.16/fs/smbfs/ChangeLog Sat Feb 2 19:00:45 2002 @@ -1,5 +1,12 @@ ChangeLog for smbfs. +2002-02-02 John Newbigin + * Clean up codeing style and CIFS Extensions for UNIX bugs + * Implement follow_link + +2002-01-28 John Newbigin + * Implementation of CIFS Extensions for UNIX systems + 2001-09-17 Urban Widmark * proc.c: Use 4096 (was 512) as the blocksize for better write diff -urN -X dontdiff linux-2.4.16/fs/smbfs/Makefile ../linux-2.4.16/fs/smbfs/Makefile --- linux-2.4.16/fs/smbfs/Makefile Wed Oct 3 10:03:34 2001 +++ ../linux-2.4.16/fs/smbfs/Makefile Mon Feb 4 22:11:58 2002 @@ -9,7 +9,7 @@ O_TARGET := smbfs.o -obj-y := proc.o dir.o cache.o sock.o inode.o file.o ioctl.o getopt.o +obj-y := proc.o dir.o cache.o sock.o inode.o file.o ioctl.o getopt.o symlink.o obj-m := $(O_TARGET) # If you want debugging output, you may add these flags to the EXTRA_CFLAGS @@ -29,7 +29,7 @@ # # getopt.c not included. It is intentionally separate -SRC = proc.c dir.c cache.c sock.c inode.c file.c ioctl.c +SRC = proc.c dir.c cache.c sock.c inode.c file.c ioctl.c symlink.c proto: -rm -f proto.h diff -urN -X dontdiff linux-2.4.16/fs/smbfs/dir.c ../linux-2.4.16/fs/smbfs/dir.c --- linux-2.4.16/fs/smbfs/dir.c Wed Oct 3 10:03:34 2001 +++ ../linux-2.4.16/fs/smbfs/dir.c Sun Feb 3 17:06:39 2002 @@ -30,6 +30,7 @@ static int smb_unlink(struct inode *, struct dentry *); static int smb_rename(struct inode *, struct dentry *, struct inode *, struct dentry *); +static int smb_make_node(struct inode *,struct dentry *,int,int); struct file_operations smb_dir_operations = { @@ -49,6 +50,8 @@ rename: smb_rename, revalidate: smb_revalidate_inode, setattr: smb_notify_change, + symlink: smb_symlink, + mknod: smb_make_node, }; /* @@ -479,12 +482,19 @@ { __u16 fileid; int error; + struct iattr attr; VERBOSE("creating %s/%s, mode=%d\n", DENTRY_PATH(dentry), mode); smb_invalid_dir_cache(dir); error = smb_proc_create(dentry, 0, CURRENT_TIME, &fileid); if (!error) { + if(server_from_dentry(dentry)->opt.capabilities & SMB_CAP_UNIX) { + /* Set attributes for new directory */ + attr.ia_valid = ATTR_MODE; + attr.ia_mode = mode; + error = smb_proc_setattr_unix(dentry, &attr); + } error = smb_instantiate(dentry, fileid, 1); } else { PARANOIA("%s/%s failed, error=%d\n", @@ -498,10 +508,17 @@ smb_mkdir(struct inode *dir, struct dentry *dentry, int mode) { int error; + struct iattr attr; smb_invalid_dir_cache(dir); error = smb_proc_mkdir(dentry); if (!error) { + if(server_from_dentry(dentry)->opt.capabilities & SMB_CAP_UNIX) { + /* Set attributes for new directory */ + attr.ia_valid = ATTR_MODE; + attr.ia_mode = mode; + error = smb_proc_setattr_unix(dentry, &attr); + } error = smb_instantiate(dentry, 0, 0); } return error; @@ -581,5 +598,28 @@ smb_renew_times(new_dentry); } out: + return error; +} + +int smb_make_node(struct inode *inode,struct dentry *dentry,int mode,int dev) +{ + int error = -EPERM; + + if(S_ISCHR(mode)) { + DEBUG1("Request to make a char device node\n"); + } + else if(S_ISBLK(mode)) { + DEBUG1("Request to make a block device node\n"); + } + else if(S_ISFIFO(mode)) { + DEBUG1("Request to make a fifo node\n"); + } + else if(S_ISSOCK(mode)) { + DEBUG1("Request to make a socket node\n"); + } + else { + DEBUG1("Request to make a unsupported node\n"); + } + return error; } diff -urN -X dontdiff linux-2.4.16/fs/smbfs/inode.c ../linux-2.4.16/fs/smbfs/inode.c --- linux-2.4.16/fs/smbfs/inode.c Wed Oct 3 10:03:34 2001 +++ ../linux-2.4.16/fs/smbfs/inode.c Sun Feb 3 19:28:55 2002 @@ -60,7 +60,7 @@ { struct inode *result; - DEBUG1("smb_iget: %p\n", fattr); + VERBOSE("smb_iget: %p\n", fattr); result = new_inode(sb); if (!result) @@ -75,6 +75,12 @@ } else if (S_ISDIR(result->i_mode)) { result->i_op = &smb_dir_inode_operations; result->i_fop = &smb_dir_operations; + } else if(S_ISLNK(result->i_mode)) { + DEBUG1("iget a symlink\n"); + result->i_op = &smb_link_inode_operations; + } else { + DEBUG1("iget special node type\n"); + init_special_inode(result, result->i_mode, fattr->f_rdev); } insert_inode_hash(result); return result; @@ -182,7 +188,22 @@ * 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)) { + + /* + * Don't dick with the root inode + */ + VERBOSE("inode no %ld\n", inode->i_ino); + if(inode->i_ino == 2) + { + return error; + } + if(S_ISLNK(inode->i_mode)) { + /* + * We don't need to do anything here because the vfs will + * call follow_link to find the target. + */ + + } else if ((inode->i_mode & S_IFMT) == (fattr.f_mode & S_IFMT)) { smb_set_inode_attr(inode, &fattr); } else { /* @@ -226,7 +247,7 @@ struct inode *inode = dentry->d_inode; int error = 0; - DEBUG1("smb_revalidate_inode\n"); + VERBOSE("smb_revalidate_inode\n"); lock_kernel(); /* @@ -514,90 +535,115 @@ int error, changed, refresh = 0; struct smb_fattr fattr; - error = smb_revalidate_inode(dentry); - 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 != server->mnt->uid)) - goto out; - - if ((attr->ia_valid & ATTR_GID) && (attr->ia_uid != server->mnt->gid)) - goto out; + DEBUG1("changing %s/%s, old mode=%07o, new mode=%07o\n", + DENTRY_PATH(dentry), + inode->i_mode, attr->ia_mode); - if ((attr->ia_valid & ATTR_MODE) && (attr->ia_mode & ~mask)) - goto out; + if((server_from_dentry(dentry))->opt.capabilities & SMB_CAP_UNIX) { - if ((attr->ia_valid & ATTR_SIZE) != 0) { - VERBOSE("changing %s/%s, old size=%ld, new size=%ld\n", + /* check permissions */ + + DEBUG1("changing %s/%s, old mode=%07o, new mode=%07o\n", DENTRY_PATH(dentry), - (long) inode->i_size, (long) attr->ia_size); - error = smb_open(dentry, O_WRONLY); + inode->i_mode, attr->ia_mode); + /* TODO should we sanity check changes (allow change of file type?) */ + /*fattr.mode &= ~(mask); + fattr.mode |= (mask & ??)*/ + /*DEBUG1("changing %s/%s, old mode=%07o, new mode=%07o\n", + DENTRY_PATH(dentry), + inode->i_mode, attr->ia_mode);*/ + error = smb_proc_setattr_unix(dentry, attr); if (error) goto out; - error = smb_proc_trunc(server, inode->u.smbfs_i.fileid, - attr->ia_size); + refresh = 1; + } else { + /* Original notify_change */ + error = smb_revalidate_inode(dentry); if (error) goto out; - error = vmtruncate(inode, attr->ia_size); - if (error) + + if ((error = inode_change_ok(inode, attr)) < 0) goto out; - refresh = 1; - } - /* - * Initialize the fattr and check for changed fields. - * Note: CTIME under SMB is creation time rather than - * change time, so we don't attempt to change it. - */ - smb_get_inode_attr(inode, &fattr); + error = -EPERM; + if ((attr->ia_valid & ATTR_UID) && (attr->ia_uid != server->mnt->uid)) + goto out; - changed = 0; - if ((attr->ia_valid & ATTR_MTIME) != 0) { - fattr.f_mtime = attr->ia_mtime; - changed = 1; - } - if ((attr->ia_valid & ATTR_ATIME) != 0) { - fattr.f_atime = attr->ia_atime; - /* Earlier protocols don't have an access time */ - if (server->opt.protocol >= SMB_PROTOCOL_LANMAN2) - changed = 1; - } - if (changed) { - error = smb_proc_settime(dentry, &fattr); - if (error) + if ((attr->ia_valid & ATTR_GID) && (attr->ia_uid != server->mnt->gid)) goto out; - refresh = 1; - } - /* - * Check for mode changes ... we're extremely limited in - * what can be set for SMB servers: just the read-only bit. - */ - if ((attr->ia_valid & ATTR_MODE) != 0) { - VERBOSE("%s/%s mode change, old=%x, new=%x\n", - DENTRY_PATH(dentry), fattr.f_mode, attr->ia_mode); + if ((attr->ia_valid & ATTR_MODE) && (attr->ia_mode & ~mask)) + goto out; + + if ((attr->ia_valid & ATTR_SIZE) != 0) { + VERBOSE("changing %s/%s, old size=%ld, new size=%ld\n", + DENTRY_PATH(dentry), + (long) inode->i_size, (long) attr->ia_size); + error = smb_open(dentry, O_WRONLY); + if (error) + goto out; + error = smb_proc_trunc(server, inode->u.smbfs_i.fileid, + attr->ia_size); + if (error) + goto out; + error = vmtruncate(inode, attr->ia_size); + if (error) + goto out; + refresh = 1; + } + + /* + * Initialize the fattr and check for changed fields. + * Note: CTIME under SMB is creation time rather than + * change time, so we don't attempt to change it. + */ + smb_get_inode_attr(inode, &fattr); + + changed = 0; - if (attr->ia_mode & S_IWUSR) { - if (fattr.attr & aRONLY) { - fattr.attr &= ~aRONLY; - changed = 1; - } - } else { - if (!(fattr.attr & aRONLY)) { - fattr.attr |= aRONLY; + if ((attr->ia_valid & ATTR_MTIME) != 0) { + fattr.f_mtime = attr->ia_mtime; + changed = 1; + } + if ((attr->ia_valid & ATTR_ATIME) != 0) { + fattr.f_atime = attr->ia_atime; + /* Earlier protocols don't have an access time */ + if (server->opt.protocol >= SMB_PROTOCOL_LANMAN2) changed = 1; - } } if (changed) { - error = smb_proc_setattr(dentry, &fattr); + error = smb_proc_settime(dentry, &fattr); if (error) goto out; refresh = 1; } + + /* + * Check for mode changes ... we're extremely limited in + * what can be set for SMB servers: just the read-only bit. + */ + if ((attr->ia_valid & ATTR_MODE) != 0) { + VERBOSE("%s/%s mode change, old=%x, new=%x\n", + DENTRY_PATH(dentry), fattr.f_mode, attr->ia_mode); + changed = 0; + if (attr->ia_mode & S_IWUSR) { + if (fattr.attr & aRONLY) { + fattr.attr &= ~aRONLY; + changed = 1; + } + } else { + if (!(fattr.attr & aRONLY)) { + fattr.attr |= aRONLY; + changed = 1; + } + } + if (changed) { + error = smb_proc_setattr(dentry, &fattr); + if (error) + goto out; + refresh = 1; + } + } } error = 0; @@ -606,6 +652,7 @@ smb_refresh_inode(dentry); return error; } + #ifdef DEBUG_SMB_MALLOC int smb_malloced; diff -urN -X dontdiff linux-2.4.16/fs/smbfs/proc.c ../linux-2.4.16/fs/smbfs/proc.c --- linux-2.4.16/fs/smbfs/proc.c Mon Oct 8 09:47:43 2001 +++ ../linux-2.4.16/fs/smbfs/proc.c Mon Feb 4 21:11:35 2002 @@ -58,7 +58,8 @@ smb_proc_do_getattr(struct smb_sb_info *server, struct dentry *dir, struct smb_fattr *fattr); - +int +smb_proc_query_cifsunix(struct smb_sb_info *server); static void str_upper(char *name, int len) @@ -379,6 +380,131 @@ return (time_t)t; } +/* Convert the Unix UTC into NT time */ +static u64 +smb_unixutc2ntutc(time_t t) +{ + /* Note: timezone conversion is probably wrong. */ + return ((u64)t) * 10000000 + NTFS_TIME_OFFSET; +} + +/* + * This function will create the flags for the mode of the file + * It should do security checks on what kind of files it will allow + * + * TODO What type of file has a 0 S_IFMT ??? perhaps we could use that... + */ +static int smb_filetype_to_mode(u32 filetype) +{ + switch(filetype) { + case UNIX_TYPE_FILE: + return S_IFREG; + + case UNIX_TYPE_DIR: + return S_IFDIR; + + case UNIX_TYPE_SYMLINK: + return S_IFLNK; + + case UNIX_TYPE_CHARDEV: + return S_IFCHR; + + case UNIX_TYPE_BLKDEV: + return S_IFBLK; + + case UNIX_TYPE_FIFO: + return S_IFIFO; + + case UNIX_TYPE_SOCKET: + return S_IFSOCK; + + case UNIX_TYPE_UNKNOWN: + default: + DEBUG1("unknown type %d\n", filetype); + return S_IFREG; + } +} + +static u32 smb_filetype_from_mode(int mode) +{ + if(mode & S_IFREG) + return UNIX_TYPE_FILE; + + if(mode & S_IFDIR) + return UNIX_TYPE_DIR; + + if(mode & S_IFLNK) + return UNIX_TYPE_SYMLINK; + + if(mode & S_IFCHR) + return UNIX_TYPE_CHARDEV; + + if(mode & S_IFBLK) + return UNIX_TYPE_BLKDEV; + + if(mode & S_IFIFO) + return UNIX_TYPE_FIFO; + + if(mode & S_IFSOCK) + return UNIX_TYPE_SOCKET; + + return UNIX_TYPE_UNKNOWN; +} + +/* + * This function will decode the 'standard' permissions into Linux + * permissions. It should do security checks to make sure that we + * don't do silly things like make devices owned by users. + * + * TODO To do this it needs more info. + */ +static int smb_permissions_to_mode(u64 permissions) +{ + int mode = 0; + + if(permissions & UNIX_X_OTH) mode |= S_IXOTH; + if(permissions & UNIX_R_OTH) mode |= S_IROTH; + if(permissions & UNIX_W_OTH) mode |= S_IWOTH; + + if(permissions & UNIX_X_GRP) mode |= S_IXGRP; + if(permissions & UNIX_R_GRP) mode |= S_IRGRP; + if(permissions & UNIX_W_GRP) mode |= S_IWGRP; + + if(permissions & UNIX_X_USR) mode |= S_IXUSR; + if(permissions & UNIX_R_USR) mode |= S_IRUSR; + if(permissions & UNIX_W_USR) mode |= S_IWUSR; + + /* these could pose security issues.... */ + if(permissions & UNIX_STICKY) mode |= S_ISVTX; + if(permissions & UNIX_SET_GID) mode |= S_ISGID; + if(permissions & UNIX_SET_UID) mode |= S_ISUID; + + return mode; +} + +static u64 smb_permissions_from_mode(int mode) +{ + u64 perm = 0; + + if(mode & S_IXOTH) perm |= UNIX_X_OTH; + if(mode & S_IROTH) perm |= UNIX_R_OTH; + if(mode & S_IWOTH) perm |= UNIX_W_OTH; + + if(mode & S_IXGRP) perm |= UNIX_X_GRP; + if(mode & S_IRGRP) perm |= UNIX_R_GRP; + if(mode & S_IWGRP) perm |= UNIX_W_GRP; + + if(mode & S_IXUSR) perm |= UNIX_X_USR; + if(mode & S_IRUSR) perm |= UNIX_R_USR; + if(mode & S_IWUSR) perm |= UNIX_W_USR; + + if(mode & S_ISVTX) perm |= UNIX_STICKY; + if(mode & S_ISUID) perm |= UNIX_SET_UID; + if(mode & S_ISGID) perm |= UNIX_SET_GID; + + return perm; +} + #if 0 /* Convert the Unix UTC into NT time */ static u64 @@ -832,6 +958,12 @@ } } + if(server->opt.capabilities & SMB_CAP_UNIX) + { + DEBUG1("using UNIX_CIFS extensions\n"); + smb_proc_query_cifsunix(server); + } + out: smb_unlock_server(server); smb_wakeup(server); @@ -1095,12 +1227,19 @@ * If the file is open with write permissions, * update the time stamps to sync mtime and atime. */ - 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); + if(server->opt.capabilities & SMB_CAP_UNIX) { + /* + * There is nothing to do here? + */ + } + else { + 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); + } } result = smb_proc_close(server, ino->u.smbfs_i.fileid, @@ -1358,6 +1497,16 @@ int result; struct smb_fattr fattr; + if(server->opt.capabilities & SMB_CAP_UNIX) + { + /* + * Perhaps we should call setattr_unix? + * The correct solution it to configure the server to have + * "delete readonly = Yes" + */ + PARANOIA("We should not be here because we are UNIX\n"); + } + /* first get current attribute */ result = smb_proc_do_getattr(server, dentry, &fattr); if (result < 0) @@ -1442,6 +1591,11 @@ char *p; int result; + if(server->opt.capabilities & SMB_CAP_UNIX) + { + PARANOIA("We should not be here because we are UNIX\n"); + } + smb_lock_server(server); retry: @@ -1481,25 +1635,37 @@ fattr->f_uid = server->mnt->uid; fattr->f_gid = server->mnt->gid; fattr->f_blksize = SMB_ST_BLKSIZE; + fattr->f_unix = 0; } static void smb_finish_dirent(struct smb_sb_info *server, struct smb_fattr *fattr) { - fattr->f_mode = server->mnt->file_mode; - if (fattr->attr & aDIR) - { - fattr->f_mode = server->mnt->dir_mode; - fattr->f_size = SMB_ST_BLKSIZE; + if(fattr->f_unix) { + /* mask out suid and sgid */ + fattr->f_mode &= ~(S_ISGID | S_ISUID); + + /* convert nodes into regular files...*/ + /*if(S_ISCHR(fattr->f_mode) | S_ISBLK(fattr->f_mode)) { + fattr->f_mode &= ~S_IFMT; + fattr->f_mode |= S_IFREG; + }*/ + } else { + fattr->f_mode = server->mnt->file_mode; + if (fattr->attr & aDIR) + { + fattr->f_mode = server->mnt->dir_mode; + fattr->f_size = SMB_ST_BLKSIZE; + } + /* Check the read-only flag */ + if (fattr->attr & aRONLY) + fattr->f_mode &= ~(S_IWUSR | S_IWGRP | S_IWOTH); + + /* How many 512 byte blocks do we need for this file? */ + fattr->f_blocks = 0; + if (fattr->f_size != 0) + fattr->f_blocks = 1 + ((fattr->f_size-1) >> 9); } - /* Check the read-only flag */ - if (fattr->attr & aRONLY) - fattr->f_mode &= ~(S_IWUSR | S_IWGRP | S_IWOTH); - - /* How many 512 byte blocks do we need for this file? */ - fattr->f_blocks = 0; - if (fattr->f_size != 0) - fattr->f_blocks = 1 + ((fattr->f_size-1) >> 9); return; } @@ -1511,6 +1677,7 @@ fattr->f_ino = 2; /* traditional root inode number */ fattr->f_mtime = CURRENT_TIME; smb_finish_dirent(server, fattr); + DEBUG1("root inode mode = %07o\n", fattr->f_mode); } /* @@ -1528,6 +1695,11 @@ { int len; + if(server->opt.capabilities & SMB_CAP_UNIX) + { + PARANOIA("We should not be here because we are UNIX\n"); + } + /* * SMB doesn't have a concept of inode numbers ... */ @@ -1572,7 +1744,6 @@ server->remote_nls, server->local_nls); qname->name = server->name_buf; - DEBUG1("len=%d, name=%.*s\n", qname->len, qname->len, qname->name); return p + 22; } @@ -1600,6 +1771,11 @@ static struct qstr mask = { "*.*", 3, 0 }; unsigned char *last_status; + if(server->opt.capabilities & SMB_CAP_UNIX) + { + PARANOIA("We should not be here because we are UNIX\n"); + } + VERBOSE("%s/%s\n", DENTRY_PATH(dir)); smb_lock_server(server); @@ -1700,6 +1876,48 @@ return result; } +void smb_decode_unix_basic(struct smb_fattr *fattr, char *p) +{ + __u64 devmajor, devminor; + + fattr->f_unix = 1; + fattr->f_mode = 0; + /* 0 L file size in bytes */ + fattr->f_size = LVAL(p, 0); + + /* 8 L file size on disk in bytes (block count) */ + fattr->f_blocks = LVAL(p, 8); + + /* times. */ + fattr->f_ctime = smb_ntutc2unixutc(LVAL(p, 16)); + fattr->f_atime = smb_ntutc2unixutc(LVAL(p, 24)); + fattr->f_mtime = smb_ntutc2unixutc(LVAL(p, 32)); + + /* 40 L uid */ + fattr->f_uid = LVAL(p, 40); + /* 48 L gid */ + fattr->f_gid = LVAL(p, 48); + + /* 56 W file type enum */ + fattr->f_mode |= smb_filetype_to_mode(WVAL(p, 56)); + + if(S_ISBLK(fattr->f_mode) || S_ISCHR(fattr->f_mode)) + { + /* 60 L devmajor */ + devmajor = LVAL(p, 60); + /* 68 L devminor */ + devminor = LVAL(p, 68); + + fattr->f_rdev = ((devmajor & 0xFF) << 8) | (devminor & 0xFF); + } + /* 76 L unique ID (inode) */ + /* 84 L permissions */ + fattr->f_mode |= smb_permissions_to_mode(LVAL(p, 84)); + + /* 92 L link count */ + +} + /* * Interpret a long filename structure using the specified info level: * level 1 for anything below NT1 protocol @@ -1769,9 +1987,25 @@ VERBOSE("info 260 at %p, len=%d, name=%.*s\n", p, len, len, qname->name); break; + + case SMB_FIND_FILE_UNIX: + result = p + WVAL(p, 0); + qname->name = p + 108; + + len = strlen(qname->name); + /* TODO should we check the length?? */ + + p += 8; + smb_decode_unix_basic(fattr, p); + VERBOSE("info decoded FILE_UNIX at %p, len=%d, name=%.*s\n", + p, len, len, qname->name); + break; + default: PARANOIA("Unknown info level %d\n", level); result = p + WVAL(p, 0); + + /* TODO debug this. I got an oops */ goto out; } @@ -1854,6 +2088,10 @@ if (server->opt.protocol < SMB_PROTOCOL_NT1) info_level = 1; + if(server->opt.capabilities & SMB_CAP_UNIX) { + info_level = SMB_FILE_FILE_UNIX; + } + smb_lock_server(server); /* @@ -1976,6 +2214,9 @@ if (ff_lastname + 1 + mask_len > resp_data_len) mask_len = resp_data_len - ff_lastname - 1; break; + case SMB_FIND_FILE_UNIX: + mask_len = resp_data_len - ff_lastname; + break; } /* @@ -2018,7 +2259,6 @@ if (qname.name[1] == '.' && qname.len == 2) continue; } - if (!smb_fill_cache(filp, dirent, filldir, ctl, &qname, &fattr)) ; /* stop reading? */ @@ -2067,6 +2307,11 @@ int resp_param_len = 0; int mask_len, result; + if(server->opt.capabilities & SMB_CAP_UNIX) + { + PARANOIA("We should not be here because we are UNIX\n"); + } + retry: mask_len = smb_encode_path(server, mask, dentry, NULL); if (mask_len < 0) { @@ -2144,6 +2389,11 @@ int result; char *p; + if(server->opt.capabilities & SMB_CAP_UNIX) + { + PARANOIA("We should not be here because we are UNIX\n"); + } + retry: p = smb_setup_header(server, SMBgetatr, 0, 0); *p++ = 4; @@ -2192,9 +2442,14 @@ int resp_data_len = 0; int resp_param_len = 0; int result; + int level = 1; /* Info level SMB_INFO_STANDARD */ + if(server->opt.capabilities & SMB_CAP_UNIX) { + level = SMB_QUERY_FILE_UNIX_BASIC; + } retry: - WSET(param, 0, 1); /* Info level SMB_INFO_STANDARD */ + + WSET(param, 0, level); DSET(param, 2, 0); result = smb_encode_path(server, param + 6, dir, NULL); if (result < 0) @@ -2219,40 +2474,49 @@ goto out; } result = -ENOENT; - if (resp_data_len < 22) - { - PARANOIA("not enough data for %s, len=%d\n", - ¶m[6], resp_data_len); - goto out; - } + if(level == 1) { + if (resp_data_len < 22) { + PARANOIA("not enough data for %s, len=%d\n", + ¶m[6], resp_data_len); + goto out; + } - /* - * Kludge alert: Win 95 swaps the date and time field, - * contrary to the CIFS docs and Win NT practice. - */ - if (server->mnt->flags & SMB_MOUNT_WIN95) { - off_date = 2; - off_time = 0; - } - date = WVAL(resp_data, off_date); - time = WVAL(resp_data, off_time); - attr->f_ctime = date_dos2unix(server, date, time); - - date = WVAL(resp_data, 4 + off_date); - time = WVAL(resp_data, 4 + off_time); - attr->f_atime = date_dos2unix(server, date, time); - - date = WVAL(resp_data, 8 + off_date); - time = WVAL(resp_data, 8 + off_time); - attr->f_mtime = date_dos2unix(server, date, time); + /* + * Kludge alert: Win 95 swaps the date and time field, + * contrary to the CIFS docs and Win NT practice. + */ + if (server->mnt->flags & SMB_MOUNT_WIN95) { + off_date = 2; + off_time = 0; + } + date = WVAL(resp_data, off_date); + time = WVAL(resp_data, off_time); + attr->f_ctime = date_dos2unix(server, date, time); + + date = WVAL(resp_data, 4 + off_date); + time = WVAL(resp_data, 4 + off_time); + attr->f_atime = date_dos2unix(server, date, time); + + date = WVAL(resp_data, 8 + off_date); + time = WVAL(resp_data, 8 + off_time); + attr->f_mtime = date_dos2unix(server, date, time); #ifdef SMBFS_DEBUG_TIMESTAMP - printk(KERN_DEBUG "getattr_trans2: %s/%s, date=%x, time=%x, mtime=%ld\n", - DENTRY_PATH(dir), date, time, attr->f_mtime); + printk(KERN_DEBUG "getattr_trans2: %s/%s, date=%x, time=%x, mtime=%ld\n", + DENTRY_PATH(dir), date, time, attr->f_mtime); #endif - attr->f_size = DVAL(resp_data, 12); - attr->attr = WVAL(resp_data, 20); - result = 0; + attr->f_size = DVAL(resp_data, 12); + attr->attr = WVAL(resp_data, 20); + result = 0; + } else if(level == SMB_QUERY_FILE_UNIX_BASIC) { + smb_decode_unix_basic(attr, resp_data); + result = 0; + + + } else { + PARANOIA("unknown info level\n"); + result = -ENOENT; + } out: return result; } @@ -2334,6 +2598,11 @@ char *p; int result; + if(server->opt.capabilities & SMB_CAP_UNIX) + { + PARANOIA("We should not be here because we are UNIX\n"); + } + retry: p = smb_setup_header(server, SMBsetatr, 8, 0); WSET(server->packet, smb_vwv0, attr); @@ -2367,6 +2636,7 @@ * Because of bugs in the trans2 setattr messages, we must set * attributes and timestamps separately. The core SMBsetatr * message seems to be the only reliable way to set attributes. + * */ int smb_proc_setattr(struct dentry *dir, struct smb_fattr *fattr) @@ -2374,6 +2644,11 @@ struct smb_sb_info *server = server_from_dentry(dir); int result; + if(server->opt.capabilities & SMB_CAP_UNIX) + { + PARANOIA("We should not be here because we are UNIX\n"); + } + VERBOSE("setting %s/%s, open=%d\n", DENTRY_PATH(dir), smb_is_open(dir->d_inode)); smb_lock_server(server); @@ -2393,6 +2668,11 @@ __u16 date, time; int result; + if(server->opt.capabilities & SMB_CAP_UNIX) + { + PARANOIA("We should not be here because we are UNIX\n"); + } + retry: smb_setup_header(server, SMBsetattrE, 7, 0); WSET(server->packet, smb_vwv0, inode->u.smbfs_i.fileid); @@ -2441,6 +2721,11 @@ int result; char data[26]; + if(server->opt.capabilities & SMB_CAP_UNIX) + { + PARANOIA("We should not be here because we are UNIX\n"); + } + retry: WSET(param, 0, 1); /* Info level SMB_INFO_STANDARD */ DSET(param, 2, 0); @@ -2485,6 +2770,142 @@ } /* + * TODO Note: we must lock the server ourself + * ATTR_MODE 0x001 + * ATTR_UID 0x002 + * ATTR_GID 0x004 + * ATTR_SIZE 0x008 + * ATTR_ATIME 0x010 + * ATTR_MTIME 0x020 + * ATTR_CTIME 0x040 + * ATTR_ATIME_SET 0x080 + * ATTR_MTIME_SET 0x100 + * ATTR_FORCE 0x200 + * ATTR_ATTR_FLAG 0x400 + */ + +extern int +smb_proc_setattr_unix(struct dentry *dentry, struct iattr *attr) +{ + u64 nttime; + char *p, *param; + unsigned char *resp_data = NULL; + unsigned char *resp_param = NULL; + int resp_data_len = 0; + int resp_param_len = 0; + int result; + char data[100]; + struct smb_sb_info *server; + + server = server_from_dentry(dentry); + param = server->temp_buf; + + DEBUG1("valid flags = 0x%04x\n", attr->ia_valid); + + retry: + WSET(param, 0, SMB_SET_FILE_UNIX_BASIC); + DSET(param, 2, 0); + result = smb_encode_path(server, param + 6, dentry, NULL); + if (result < 0) + goto out; + p = param + 6 + result; + + if(attr->ia_valid & ATTR_SIZE) { + /*DEBUG1("setting size = %d\n", attr->ia_size);*/ + /* 0 L file size in bytes */ + LSET(data, 0, attr->ia_size); + /* 8 L file size on disk in bytes (block count) */ + LSET(data, 8, 0); /* can't set anyway */ + } else { + LSET(data, 0, SMB_SIZE_NO_CHANGE); + LSET(data, 8, SMB_SIZE_NO_CHANGE); + } + + + /* times. TODO check the conversion function it the correct one + */ + /* we can't set ctime but we might as well pass this to the server + * and let it ignore it + */ + if(attr->ia_valid & ATTR_CTIME) { + VERBOSE("setting ctime = %d\n", attr->ia_ctime); + nttime = smb_unixutc2ntutc(attr->ia_ctime); + LSET(data, 16, nttime); + } else { + LSET(data, 16, SMB_TIME_NO_CHANGE); + } + + if(attr->ia_valid & ATTR_ATIME) { + VERBOSE("setting atime = %d\n", attr->ia_atime); + nttime = smb_unixutc2ntutc(attr->ia_atime); + LSET(data, 24, nttime); + } else { + LSET(data, 24, SMB_TIME_NO_CHANGE); + } + + if(attr->ia_valid & ATTR_MTIME) { + VERBOSE("setting mtime = %d\n", attr->ia_mtime); + nttime = smb_unixutc2ntutc(attr->ia_mtime); + LSET(data, 32, nttime); + } else { + LSET(data, 32, SMB_TIME_NO_CHANGE); + } + + if(attr->ia_valid & ATTR_UID) { + /* 40 L uid */ + LSET(data, 40, attr->ia_uid); + } else { + LSET(data, 40, SMB_UID_NO_CHANGE); + } + + if(attr->ia_valid & ATTR_GID) { + /* 48 L gid */ + LSET(data, 48, attr->ia_gid); + } else { + LSET(data, 48, SMB_GID_NO_CHANGE); + } + + /* 56 W file type enum */ + LSET(data, 56, smb_filetype_from_mode(attr->ia_mode)); + + /* 60 L devmajor */ + LSET(data, 60, 0); + /* 68 L devminor */ + LSET(data, 68, 0); + /* 76 L unique ID (inode) */ + LSET(data, 76, 0); + + if(attr->ia_valid & ATTR_MODE) { + DEBUG1("setting mode = %07o\n", attr->ia_mode); + /* 84 L permissions */ + LSET(data, 84, smb_permissions_from_mode(attr->ia_mode)); + } else { + LSET(data, 84, SMB_MODE_NO_CHANGE); + } + + /* 92 L link count */ + LSET(data, 92, 0); + + result = smb_trans2_request(server, TRANSACT2_SETPATHINFO, + sizeof(data), data, p - param, param, + &resp_data_len, &resp_data, + &resp_param_len, &resp_param); + if (result < 0) + { + if (smb_retry(server)) + goto retry; + goto out; + } + result = 0; + if (server->rcls != 0) + result = smb_errno(server); + +out: + return result; +} + + +/* * Set the modify and access timestamps for a file. * * Incredibly enough, in all of SMB there is no message to allow @@ -2504,6 +2925,11 @@ struct inode *inode = dentry->d_inode; int result; + if(server->opt.capabilities & SMB_CAP_UNIX) + { + PARANOIA("We should not be here because we are UNIX\n"); + } + VERBOSE("setting %s/%s, open=%d\n", DENTRY_PATH(dentry), smb_is_open(inode)); @@ -2568,5 +2994,183 @@ out: smb_unlock_server(server); + return result; +} + +int +smb_proc_read_link(struct smb_sb_info *server, struct dentry *dentry, char *buffer, int len) +{ + char *p, *param = server->temp_buf; + unsigned char *resp_data = NULL; + unsigned char *resp_param = NULL; + int resp_data_len = 0; + int resp_param_len = 0; + int result; + int level; + + if(!(server->opt.capabilities & SMB_CAP_UNIX)) + { + PARANOIA("We should not be here because we are not UNIX\n"); + } + + DEBUG1("readlink of %s/%s\n", DENTRY_PATH(dentry)); + + smb_lock_server(server); + level = SMB_QUERY_FILE_UNIX_LINK; + retry: + + WSET(param, 0, level); + DSET(param, 2, 0); + result = smb_encode_path(server, param + 6, dentry, NULL); + if (result < 0) + goto out; + p = param + 6 + result; + + result = smb_trans2_request(server, TRANSACT2_QPATHINFO, + 0, NULL, p - param, param, + &resp_data_len, &resp_data, + &resp_param_len, &resp_param); + DEBUG1("for %s: result=%d, rcls=%d, err=%d\n", + ¶m[6], result, server->rcls, server->err); + if (result < 0) { + if (smb_retry(server)) + goto retry; + goto out; + } + if (server->rcls != 0) { + VERBOSE("for %s: result=%d, rcls=%d, err=%d\n", + ¶m[6], result, server->rcls, server->err); + result = smb_errno(server); + goto out; + } + + /* copy data up to the \0 or buffer length */ + result = 0; + while(result < len && resp_data[result]) { + buffer[result] = resp_data[result]; + result++; + } + buffer[result] = 0; + +out: + smb_unlock_server(server); + return result; +} + + +/* + * Create a symlink object called dentry which points to oldpath. + * + * We need to lock server + * + * samba does not permit dangling links but returns a suitable error message + * + */ +extern int smb_proc_symlink(struct smb_sb_info *server, struct dentry *dentry,const char *oldpath) +{ + char *p, *param = server->temp_buf; + unsigned char *resp_data = NULL; + unsigned char *resp_param = NULL; + int resp_data_len = 0; + int resp_param_len = 0; + int result; + int level = SMB_SET_FILE_UNIX_LINK; + int oldpathlen = 0; + + if(!(server->opt.capabilities & SMB_CAP_UNIX)) + { + PARANOIA("We should not be here because we are not UNIX\n"); + } + + smb_lock_server(server); + oldpathlen = strlen(oldpath); + retry: + WSET(param, 0, level); + DSET(param, 2, 0); + result = smb_encode_path(server, param + 6, dentry, NULL); + if (result < 0) + goto out; + p = param + 6 + result; + + + result = smb_trans2_request(server, TRANSACT2_SETPATHINFO, + oldpathlen, (char *)oldpath, p - param, param, + &resp_data_len, &resp_data, + &resp_param_len, &resp_param); + DEBUG1("for %s: result=%d, rcls=%d, err=%d\n", + ¶m[6], result, server->rcls, server->err); + if (result < 0) { + if (smb_retry(server)) + goto retry; + goto out; + } + if (server->rcls != 0) { + VERBOSE("for %s: result=%d, rcls=%d, err=%d\n", + ¶m[6], result, server->rcls, server->err); + result = smb_errno(server); + goto out; + } + + result = 0; + +out: + smb_unlock_server(server); + return result; +} + +/* + * We are called with the server locked + */ +int +smb_proc_query_cifsunix(struct smb_sb_info *server) +{ + char *param = server->temp_buf; + unsigned char *resp_data = NULL; + unsigned char *resp_param = NULL; + int resp_data_len = 0; + int resp_param_len = 0; + int result; + int level; + int major, minor; + u64 caps; + + if(!(server->opt.capabilities & SMB_CAP_UNIX)) { + PARANOIA("We should not be here because we are not UNIX\n"); + } + + DEBUG1("SMB_QUERY_CIFS_UNIX_INFO\n"); + + level = SMB_QUERY_CIFS_UNIX_INFO; + retry: + + WSET(param, 0, level); + + result = smb_trans2_request(server, TRANSACT2_QFSINFO, + 0, NULL, 2, param, + &resp_data_len, &resp_data, + &resp_param_len, &resp_param); + if (result < 0) { + if (smb_retry(server)) + goto retry; + goto out; + } + VERBOSE("resp_data_len = %d\n", resp_data_len); + + if(resp_data_len < 12) + { + DEBUG1("Not enough data\n"); + goto out; + } + + major = WVAL(resp_data, 0); + minor = WVAL(resp_data, 2); + + + DEBUG1("Server implements \"CIFS Extensions for UNIX systems v%d.%d\"\n", major, minor); + + caps = LVAL(resp_data, 4); + DEBUG1("Server capabilities 0x%016llx\n", caps); + +out: return result; } diff -urN -X dontdiff linux-2.4.16/fs/smbfs/proto.h ../linux-2.4.16/fs/smbfs/proto.h --- linux-2.4.16/fs/smbfs/proto.h Wed Oct 3 10:03:34 2001 +++ ../linux-2.4.16/fs/smbfs/proto.h Sat Feb 2 22:48:05 2002 @@ -1,5 +1,5 @@ /* - * Autogenerated with cproto on: Tue Oct 2 20:40:54 CEST 2001 + * Autogenerated with cproto on: Sat Feb 2 22:48:02 EST 2002 */ /* proc.c */ @@ -24,11 +24,15 @@ extern int smb_proc_flush(struct smb_sb_info *server, __u16 fileid); extern int smb_proc_trunc(struct smb_sb_info *server, __u16 fid, __u32 length); extern void smb_init_root_dirent(struct smb_sb_info *server, struct smb_fattr *fattr); +extern void smb_decode_unix_basic(struct smb_fattr *fattr, char *p); extern int smb_proc_readdir(struct file *filp, void *dirent, filldir_t filldir, struct smb_cache_control *ctl); extern int smb_proc_getattr(struct dentry *dir, struct smb_fattr *fattr); extern int smb_proc_setattr(struct dentry *dir, struct smb_fattr *fattr); +extern int smb_proc_setattr_unix(struct dentry *dentry, struct iattr *attr); extern int smb_proc_settime(struct dentry *dentry, struct smb_fattr *fattr); extern int smb_proc_dskattr(struct super_block *sb, struct statfs *attr); +extern int smb_proc_read_link(struct smb_sb_info *server, struct dentry *dentry, char *buffer, int len); +extern int smb_proc_symlink(struct smb_sb_info *server, struct dentry *dentry, const char *oldpath); /* dir.c */ extern struct file_operations smb_dir_operations; extern struct inode_operations smb_dir_inode_operations; @@ -61,3 +65,8 @@ extern struct inode_operations smb_file_inode_operations; /* ioctl.c */ extern int smb_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg); +/* symlink.c */ +extern int smb_read_link(struct dentry *dentry, char *buffer, int len); +extern int smb_symlink(struct inode *inode, struct dentry *dentry, const char *oldname); +extern int smb_follow_link(struct dentry *dentry, struct nameidata *nd); +extern struct inode_operations smb_link_inode_operations; diff -urN -X dontdiff linux-2.4.16/fs/smbfs/sock.c ../linux-2.4.16/fs/smbfs/sock.c --- linux-2.4.16/fs/smbfs/sock.c Wed Oct 3 10:03:34 2001 +++ ../linux-2.4.16/fs/smbfs/sock.c Sun Feb 3 13:58:00 2002 @@ -219,7 +219,7 @@ server->data_ready = NULL; goto out; } - DEBUG1("sk->d_r = %x, server->d_r = %x\n", + VERBOSE("sk->d_r = %x, server->d_r = %x\n", (unsigned int) (sk->data_ready), (unsigned int) (server->data_ready)); @@ -266,7 +266,7 @@ "server->data_ready == NULL\n"); goto out; } - DEBUG1("smb_dont_catch_keepalive: sk->d_r = %x, server->d_r = %x\n", + VERBOSE("smb_dont_catch_keepalive: sk->d_r = %x, server->d_r = %x\n", (unsigned int) (sk->data_ready), (unsigned int) (server->data_ready)); diff -urN -X dontdiff linux-2.4.16/fs/smbfs/symlink.c ../linux-2.4.16/fs/smbfs/symlink.c --- linux-2.4.16/fs/smbfs/symlink.c Thu Jan 1 10:00:00 1970 +++ ../linux-2.4.16/fs/smbfs/symlink.c Mon Feb 4 20:22:46 2002 @@ -0,0 +1,73 @@ +/* + * symlink.c + * + * Copyright (C) 2002 by John Newbigin + * + * Please add a note about your changes to smbfs in the ChangeLog file. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include + +#include "smb_debug.h" +#include "proto.h" + +int +smb_read_link(struct dentry *dentry, char *buffer, int len) +{ + char link[256]; + int r; + DEBUG1("read link buffer len = %d\n", len); + + r = smb_proc_read_link(server_from_dentry(dentry), dentry, link, sizeof(link) - 1); + if(r > 0) { + return vfs_readlink(dentry, buffer, len, link); + } else { + return -ENOENT; + } +} + +int smb_symlink(struct inode *inode,struct dentry *dentry,const char *oldname) +{ + DEBUG1("create symlink %s -> %s/%s\n", oldname, DENTRY_PATH(dentry)); + + /* create a symlink object called dentry, pointing to oldname */ + return smb_proc_symlink(server_from_dentry(dentry), dentry, oldname); +} + +int +smb_follow_link(struct dentry *dentry, struct nameidata *nd) +{ + char link[256]; + int len; + DEBUG1("followlink of %s/%s\n", DENTRY_PATH(dentry)); + + len = smb_proc_read_link(server_from_dentry(dentry), dentry, link, sizeof(link) - 1); + if(len > 0) { + link[len] = 0; + return vfs_follow_link(nd, link); + } else { + return -ENOENT; + } +} + + +struct inode_operations smb_link_inode_operations = +{ + readlink: smb_read_link, + follow_link: smb_follow_link, +}; + diff -urN -X dontdiff linux-2.4.16/include/linux/smb.h ../linux-2.4.16/include/linux/smb.h --- linux-2.4.16/include/linux/smb.h Fri Nov 23 06:46:18 2001 +++ ../linux-2.4.16/include/linux/smb.h Sun Feb 3 17:33:35 2002 @@ -91,6 +91,7 @@ time_t f_ctime; unsigned long f_blksize; unsigned long f_blocks; + int f_unix; }; enum smb_conn_state { diff -urN -X dontdiff linux-2.4.16/include/linux/smb_fs.h ../linux-2.4.16/include/linux/smb_fs.h --- linux-2.4.16/include/linux/smb_fs.h Wed Oct 3 10:03:34 2001 +++ ../linux-2.4.16/include/linux/smb_fs.h Sun Feb 3 17:36:42 2002 @@ -112,7 +112,7 @@ #define SMB_CAP_NT_FIND 0x0200 #define SMB_CAP_DFS 0x1000 #define SMB_CAP_LARGE_READX 0x4000 - +#define SMB_CAP_UNIX 0x800000 /* * This is the time we allow an inode, dentry or dir cache to live. It is bad diff -urN -X dontdiff linux-2.4.16/include/linux/smbno.h ../linux-2.4.16/include/linux/smbno.h --- linux-2.4.16/include/linux/smbno.h Mon Oct 8 09:47:43 2001 +++ ../linux-2.4.16/include/linux/smbno.h Sun Feb 3 13:42:31 2002 @@ -281,4 +281,74 @@ #define TRANSACT2_FINDNOTIFYNEXT 12 #define TRANSACT2_MKDIR 13 + +/* UNIX stuff (from samba trans2.h) */ + +#define MIN_UNIX_INFO_LEVEL 0x200 +#define MAX_UNIX_INFO_LEVEL 0x2FF + +#define SMB_QUERY_FILE_UNIX_BASIC 0x200 +#define SMB_QUERY_FILE_UNIX_LINK 0x201 +#define SMB_QUERY_FILE_UNIX_HLINK 0x202 + +#define SMB_SET_FILE_UNIX_BASIC 0x200 + +#define SMB_FILE_FILE_UNIX 0x202 + +#define SMB_QUERY_CIFS_UNIX_INFO 0x200 + +#define SMB_MODE_NO_CHANGE 0xFFFFFFFF /* file mode value which */ + /* means "don't change it" */ +#define SMB_UID_NO_CHANGE 0xFFFFFFFF +#define SMB_GID_NO_CHANGE 0xFFFFFFFF + +#define SMB_TIME_NO_CHANGE 0xFFFFFFFFFFFFFFFF +#define SMB_SIZE_NO_CHANGE 0xFFFFFFFFFFFFFFFF + +/* UNIX filetype mappings. */ + +#define UNIX_TYPE_FILE 0 +#define UNIX_TYPE_DIR 1 +#define UNIX_TYPE_SYMLINK 2 +#define UNIX_TYPE_CHARDEV 3 +#define UNIX_TYPE_BLKDEV 4 +#define UNIX_TYPE_FIFO 5 +#define UNIX_TYPE_SOCKET 6 +#define UNIX_TYPE_UNKNOWN 0xFFFFFFFF + +/* + * Oh this is fun. "Standard UNIX permissions" has no + * meaning in POSIX. We need to define the mapping onto + * and off the wire as this was not done in the original HP + * spec. JRA. + */ + +#define UNIX_X_OTH 0000001 +#define UNIX_W_OTH 0000002 +#define UNIX_R_OTH 0000004 +#define UNIX_X_GRP 0000010 +#define UNIX_W_GRP 0000020 +#define UNIX_R_GRP 0000040 +#define UNIX_X_USR 0000100 +#define UNIX_W_USR 0000200 +#define UNIX_R_USR 0000400 +#define UNIX_STICKY 0001000 +#define UNIX_SET_GID 0002000 +#define UNIX_SET_UID 0004000 + +/* Masks for the above */ +#define UNIX_OTH_MASK 0000007 +#define UNIX_GRP_MASK 0000070 +#define UNIX_USR_MASK 0000700 +#define UNIX_PERM_MASK 0000777 +#define UNIX_EXTRA_MASK 0007000 +#define UNIX_ALL_MASK 0007777 + +#define SMB_QUERY_FILE_UNIX_LINK 0x201 +#define SMB_SET_FILE_UNIX_LINK 0x201 +#define SMB_SET_FILE_UNIX_HLINK 0x203 + +#define SMB_FIND_FILE_UNIX 0x202 + + #endif /* _SMBNO_H_ */