diff -ruN -X dontdiff /usr/src/linux-2.4.20-18.9/fs/smbfs/ChangeLog linux-2.4.20-18.9/fs/smbfs/ChangeLog --- /usr/src/linux-2.4.20-18.9/fs/smbfs/ChangeLog 2002-02-26 06:38:09.000000000 +1100 +++ linux-2.4.20-18.9/fs/smbfs/ChangeLog 2003-06-24 23:04:19.000000000 +1000 @@ -1,5 +1,12 @@ ChangeLog for smbfs. +2003-06-24 John Newbigin + * Fix symlink bug & clean up symlink code + +2002-04-19 John Newbigin + * Implement hard liks + * add missing lock to setattr_unix + 2001-12-31 René Scharfe * inode.c: added smb_show_options to show mount options in /proc/mounts @@ -10,6 +17,13 @@ * file.c, proc.c: Fix problems triggered by the "fsx test" +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 -ruN -X dontdiff /usr/src/linux-2.4.20-18.9/fs/smbfs/dir.c linux-2.4.20-18.9/fs/smbfs/dir.c --- /usr/src/linux-2.4.20-18.9/fs/smbfs/dir.c 2001-10-03 10:03:34.000000000 +1000 +++ linux-2.4.20-18.9/fs/smbfs/dir.c 2003-06-24 22:29:34.000000000 +1000 @@ -30,6 +30,8 @@ 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); +static int smb_link(struct dentry *, struct inode *, struct dentry *); struct file_operations smb_dir_operations = { @@ -49,6 +51,9 @@ rename: smb_rename, revalidate: smb_revalidate_inode, setattr: smb_notify_change, + symlink: smb_symlink, + mknod: smb_make_node, + link: smb_link, }; /* @@ -479,12 +484,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 +510,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; @@ -583,3 +602,63 @@ out: return error; } + +static 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; +} + +/* + * dentry = existing file + * new_dentry = new file + */ +static int smb_link(struct dentry *dentry, struct inode *dir, struct dentry *new_dentry) +{ + int error; + + DEBUG1("smb_link\n"); + DEBUG1("dentry1 %s/%s\n", DENTRY_PATH(dentry)); + DEBUG1("dentry2 %s/%s\n", DENTRY_PATH(new_dentry)); + smb_invalid_dir_cache(dir); + error = smb_proc_link(server_from_dentry(dentry), dentry, new_dentry); + if (!error) + { + smb_renew_times(dentry); + error = smb_instantiate(new_dentry, 0, 0); + } + return error; +} + +int smb_symlink(struct inode *inode,struct dentry *dentry,const char *oldname) +{ + int error; + + DEBUG1("create symlink %s -> %s/%s\n", oldname, DENTRY_PATH(dentry)); + + /* create a symlink object called dentry, pointing to oldname */ + error = smb_proc_symlink(server_from_dentry(dentry), dentry, oldname); + if(!error) + { + error = smb_instantiate(dentry, 0, 0); + } + return error; +} + diff -ruN -X dontdiff /usr/src/linux-2.4.20-18.9/fs/smbfs/inode.c linux-2.4.20-18.9/fs/smbfs/inode.c --- /usr/src/linux-2.4.20-18.9/fs/smbfs/inode.c 2002-11-29 10:53:15.000000000 +1100 +++ linux-2.4.20-18.9/fs/smbfs/inode.c 2003-06-21 21:24:38.000000000 +1000 @@ -81,6 +81,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; @@ -188,7 +194,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 { /* @@ -587,7 +608,9 @@ error = smb_open(dentry, O_WRONLY); if (error) goto out; - error = smb_proc_trunc(server, inode->u.smbfs_i.fileid, + + /*if(!(server_from_dentry(dentry))->opt.capabilities & SMB_CAP_UNIX) { */ + error = smb_proc_trunc(server, inode->u.smbfs_i.fileid, attr->ia_size); if (error) goto out; @@ -597,6 +620,26 @@ refresh = 1; } + + if((server_from_dentry(dentry))->opt.capabilities & SMB_CAP_UNIX) { + + /*if ((attr->ia_valid & ATTR_SIZE) != 0) { + error = vmtruncate(inode, attr->ia_size); + if (error) + goto out; + }*/ + + /* For now we don't want to set the size with setattr_unix */ + attr->ia_valid &= ~ATTR_SIZE; + + error = smb_proc_setattr_unix(dentry, attr); + if (error) + goto out; + refresh = 1; + + goto out; /* this way we don't upset the indent of the original code*/ + }; + /* * Initialize the fattr and check for changed fields. * Note: CTIME under SMB is creation time rather than diff -ruN -X dontdiff /usr/src/linux-2.4.20-18.9/fs/smbfs/Makefile linux-2.4.20-18.9/fs/smbfs/Makefile --- /usr/src/linux-2.4.20-18.9/fs/smbfs/Makefile 2001-10-03 10:03:34.000000000 +1000 +++ linux-2.4.20-18.9/fs/smbfs/Makefile 2003-06-21 21:24:38.000000000 +1000 @@ -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 -ruN -X dontdiff /usr/src/linux-2.4.20-18.9/fs/smbfs/proc.c linux-2.4.20-18.9/fs/smbfs/proc.c --- /usr/src/linux-2.4.20-18.9/fs/smbfs/proc.c 2002-11-29 10:53:15.000000000 +1100 +++ linux-2.4.20-18.9/fs/smbfs/proc.c 2003-06-24 22:56:28.000000000 +1000 @@ -55,7 +55,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) @@ -426,6 +427,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 @@ -862,6 +988,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); @@ -1118,12 +1250,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, @@ -1370,6 +1509,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) @@ -1491,25 +1640,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; } @@ -1538,6 +1699,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 ... */ @@ -1613,6 +1779,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); @@ -1723,6 +1894,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 @@ -1793,9 +2006,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; } @@ -1882,6 +2111,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); /* @@ -2004,6 +2237,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; } /* @@ -2097,6 +2333,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, SMB_MAXPATHLEN+1, dentry, NULL); if (mask_len < 0) { @@ -2174,6 +2415,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); result = smb_simple_encode_path(server, &p, dir, NULL); @@ -2220,9 +2466,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, SMB_MAXPATHLEN+1, dir, NULL); if (result < 0) @@ -2247,40 +2498,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; } @@ -2362,6 +2622,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); @@ -2397,6 +2662,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) @@ -2404,6 +2670,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); @@ -2423,6 +2694,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); @@ -2471,6 +2747,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); @@ -2515,6 +2796,144 @@ } /* + * 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); + smb_lock_server(server); + 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, SMB_MAXPATHLEN+1, 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 is 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: + smb_unlock_server(server); + return result; +} + + +/* * Set the modify and access timestamps for a file. * * Incredibly enough, in all of SMB there is no message to allow @@ -2534,6 +2953,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)); @@ -2600,3 +3024,244 @@ 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, SMB_MAXPATHLEN+1, 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, SMB_MAXPATHLEN+1, dentry, NULL); + if (result < 0) + goto out; + p = param + 6 + result; + + + result = smb_trans2_request(server, TRANSACT2_SETPATHINFO, + oldpathlen + 1, (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; +} + +/* + * Create a hard link object called new_dentry which points to dentry. + * + * We need to lock server + * + */ +extern int smb_proc_link(struct smb_sb_info *server, struct dentry *dentry, struct dentry *new_dentry) +{ + 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_HLINK; + int newpathlen = 0; + char newpath[SMB_MAXPATHLEN+1]; + + if(!(server->opt.capabilities & SMB_CAP_UNIX)) + { + PARANOIA("We should not be here because we are not UNIX\n"); + } + + smb_lock_server(server); + + newpathlen = smb_encode_path(server, newpath, SMB_MAXPATHLEN+1, dentry, NULL); + + DEBUG1("newpathlen = %d newpath=\"%s\"\n", newpathlen, newpath); +retry: + WSET(param, 0, level); + DSET(param, 2, 0); + result = smb_encode_path(server, param + 6, SMB_MAXPATHLEN+1, new_dentry, NULL); + if (result < 0) + goto out; + p = param + 6 + result; + DEBUG1("pathlen = %d oldpath=\"%s\"\n", result, param + 6); + + + result = smb_trans2_request(server, TRANSACT2_SETPATHINFO, + newpathlen + 1, (char *)newpath, 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 -ruN -X dontdiff /usr/src/linux-2.4.20-18.9/fs/smbfs/proto.h linux-2.4.20-18.9/fs/smbfs/proto.h --- /usr/src/linux-2.4.20-18.9/fs/smbfs/proto.h 2001-10-03 10:03:34.000000000 +1000 +++ linux-2.4.20-18.9/fs/smbfs/proto.h 2003-06-25 21:04:16.000000000 +1000 @@ -1,5 +1,5 @@ /* - * Autogenerated with cproto on: Tue Oct 2 20:40:54 CEST 2001 + * Autogenerated with cproto on: Wed Jun 25 21:04:11 EST 2003 */ /* proc.c */ @@ -24,16 +24,23 @@ 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); +extern int smb_proc_link(struct smb_sb_info *server, struct dentry *dentry, struct dentry *new_dentry); +extern int smb_proc_query_cifsunix(struct smb_sb_info *server); /* dir.c */ extern struct file_operations smb_dir_operations; extern struct inode_operations smb_dir_inode_operations; extern void smb_new_dentry(struct dentry *dentry); extern void smb_renew_times(struct dentry *dentry); +extern int smb_symlink(struct inode *inode, struct dentry *dentry, const char *oldname); /* cache.c */ extern void smb_invalid_dir_cache(struct inode *dir); extern void smb_invalidate_dircache_entries(struct dentry *parent); @@ -61,3 +68,7 @@ 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_follow_link(struct dentry *dentry, struct nameidata *nd); +extern struct inode_operations smb_link_inode_operations; diff -ruN -X dontdiff /usr/src/linux-2.4.20-18.9/fs/smbfs/symlink.c linux-2.4.20-18.9/fs/smbfs/symlink.c --- /usr/src/linux-2.4.20-18.9/fs/smbfs/symlink.c 1970-01-01 10:00:00.000000000 +1000 +++ linux-2.4.20-18.9/fs/smbfs/symlink.c 2003-06-24 22:47:37.000000000 +1000 @@ -0,0 +1,82 @@ +/* + * 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; + int error; + DEBUG1("read link buffer len = %d\n", len); + + error = -ENOMEM; + link = kmalloc(SMB_MAXNAMELEN + 1, GFP_KERNEL); + if(!link) + goto out; + + error = smb_proc_read_link(server_from_dentry(dentry), dentry, link, SMB_MAXNAMELEN); + if(error > 0) { + error = vfs_readlink(dentry, buffer, len, link); + } else { + error = -ENOENT; + } + kfree(link); +out: + return error; +} + +int +smb_follow_link(struct dentry *dentry, struct nameidata *nd) +{ + char *link; + int error; + int len; + DEBUG1("followlink of %s/%s\n", DENTRY_PATH(dentry)); + + error = -ENOMEM; + link = kmalloc(SMB_MAXNAMELEN + 1, GFP_KERNEL); + if(!link) + goto out; + + len = smb_proc_read_link(server_from_dentry(dentry), dentry, link, SMB_MAXNAMELEN); + if(len > 0 && len <= SMB_MAXNAMELEN) { + link[len] = 0; + error = vfs_follow_link(nd, link); + } else { + error = -ENOENT; + } + kfree(link); +out: + return error; +} + + +struct inode_operations smb_link_inode_operations = +{ + readlink: smb_read_link, + follow_link: smb_follow_link, +}; + diff -ruN -X dontdiff /usr/src/linux-2.4.20-18.9/include/linux/smb_fs.h linux-2.4.20-18.9/include/linux/smb_fs.h --- /usr/src/linux-2.4.20-18.9/include/linux/smb_fs.h 2003-05-29 22:37:28.000000000 +1000 +++ linux-2.4.20-18.9/include/linux/smb_fs.h 2003-06-21 21:24:53.000000000 +1000 @@ -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 -ruN -X dontdiff /usr/src/linux-2.4.20-18.9/include/linux/smb.h linux-2.4.20-18.9/include/linux/smb.h --- /usr/src/linux-2.4.20-18.9/include/linux/smb.h 2003-05-29 22:37:27.000000000 +1000 +++ linux-2.4.20-18.9/include/linux/smb.h 2003-06-21 21:24:39.000000000 +1000 @@ -91,6 +91,7 @@ time_t f_ctime; unsigned long f_blksize; unsigned long f_blocks; + int f_unix; }; enum smb_conn_state { diff -ruN -X dontdiff /usr/src/linux-2.4.20-18.9/include/linux/smbno.h linux-2.4.20-18.9/include/linux/smbno.h --- /usr/src/linux-2.4.20-18.9/include/linux/smbno.h 2001-10-08 09:47:43.000000000 +1000 +++ linux-2.4.20-18.9/include/linux/smbno.h 2003-06-21 21:24:39.000000000 +1000 @@ -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_ */