diff options
| -rw-r--r-- | fs/smbfs/ChangeLog | 5 | ||||
| -rw-r--r-- | fs/smbfs/Makefile | 5 | ||||
| -rw-r--r-- | fs/smbfs/dir.c | 76 | ||||
| -rw-r--r-- | fs/smbfs/inode.c | 28 | ||||
| -rw-r--r-- | fs/smbfs/proc.c | 469 | ||||
| -rw-r--r-- | fs/smbfs/proto.h | 14 | ||||
| -rw-r--r-- | fs/smbfs/symlink.c | 68 | ||||
| -rw-r--r-- | include/linux/smb.h | 3 | ||||
| -rw-r--r-- | include/linux/smb_fs.h | 27 | ||||
| -rw-r--r-- | include/linux/smbno.h | 32 |
10 files changed, 697 insertions, 30 deletions
diff --git a/fs/smbfs/ChangeLog b/fs/smbfs/ChangeLog index 4b6dec14564b..f1ceadace589 100644 --- a/fs/smbfs/ChangeLog +++ b/fs/smbfs/ChangeLog @@ -1,5 +1,10 @@ ChangeLog for smbfs. +2002-04-19 John Newbigin <jn@it.swin.edu.au> + + * Implementation of CIFS Extensions for UNIX systems, including soft + and hard links. + 2001-08-03 Urban Widmark <urban@teststation.com> * *.c: Unicode support diff --git a/fs/smbfs/Makefile b/fs/smbfs/Makefile index d6700b405844..b10c764a3913 100644 --- a/fs/smbfs/Makefile +++ b/fs/smbfs/Makefile @@ -5,7 +5,7 @@ obj-$(CONFIG_SMB_FS) += smbfs.o smbfs-objs := proc.o dir.o cache.o sock.o inode.o file.o ioctl.o getopt.o \ - smbiod.o request.o + symlink.o smbiod.o request.o # If you want debugging output, you may add these flags to the EXTRA_CFLAGS # SMBFS_PARANOIA should normally be enabled. @@ -24,7 +24,8 @@ include $(TOPDIR)/Rules.make # # getopt.c not included. It is intentionally separate -SRC = proc.c dir.c cache.c sock.c inode.c file.c ioctl.c smbiod.c request.c +SRC = proc.c dir.c cache.c sock.c inode.c file.c ioctl.c smbiod.c request.c \ + symlink.c proto: -rm -f proto.h diff --git a/fs/smbfs/dir.c b/fs/smbfs/dir.c index 5ba11f6db92a..1a74b5585ca2 100644 --- a/fs/smbfs/dir.c +++ b/fs/smbfs/dir.c @@ -30,6 +30,8 @@ static int smb_rmdir(struct inode *, struct dentry *); 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 = { @@ -51,6 +53,21 @@ struct inode_operations smb_dir_inode_operations = .setattr = smb_notify_change, }; +struct inode_operations smb_dir_inode_operations_unix = +{ + .create = smb_create, + .lookup = smb_lookup, + .unlink = smb_unlink, + .mkdir = smb_mkdir, + .rmdir = smb_rmdir, + .rename = smb_rename, + .getattr = smb_getattr, + .setattr = smb_notify_change, + .symlink = smb_symlink, + .mknod = smb_make_node, + .link = smb_link, +}; + /* * Read a directory, using filldir to fill the dirent memory. * smb_proc_readdir does the actual reading from the smb server. @@ -485,8 +502,10 @@ out_close: static int smb_create(struct inode *dir, struct dentry *dentry, int mode) { + struct smb_sb_info *server = server_from_dentry(dentry); __u16 fileid; int error; + struct iattr attr; VERBOSE("creating %s/%s, mode=%d\n", DENTRY_PATH(dentry), mode); @@ -494,6 +513,12 @@ smb_create(struct inode *dir, struct dentry *dentry, int mode) smb_invalid_dir_cache(dir); error = smb_proc_create(dentry, 0, CURRENT_TIME, &fileid); if (!error) { + if (server->opt.capabilities & SMB_CAP_UNIX) { + /* Set attributes for new file */ + attr.ia_valid = ATTR_MODE; + attr.ia_mode = mode; + error = smb_proc_setattr_unix(dentry, &attr, 0, 0); + } error = smb_instantiate(dentry, fileid, 1); } else { PARANOIA("%s/%s failed, error=%d\n", @@ -507,12 +532,20 @@ smb_create(struct inode *dir, struct dentry *dentry, int mode) static int smb_mkdir(struct inode *dir, struct dentry *dentry, int mode) { + struct smb_sb_info *server = server_from_dentry(dentry); int error; + struct iattr attr; lock_kernel(); smb_invalid_dir_cache(dir); error = smb_proc_mkdir(dentry); if (!error) { + if (server->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, 0, 0); + } error = smb_instantiate(dentry, 0, 0); } unlock_kernel(); @@ -601,3 +634,46 @@ out: unlock_kernel(); return error; } + +/* + * FIXME: samba servers won't let you create device nodes unless uid/gid + * matches the connection credentials (and we don't know which those are ...) + */ +static int +smb_make_node(struct inode *dir, struct dentry *dentry, int mode, int dev) +{ + int error; + struct iattr attr; + + attr.ia_valid = ATTR_MODE | ATTR_UID | ATTR_GID; + attr.ia_mode = mode; + attr.ia_uid = current->euid; + attr.ia_gid = current->egid; + + smb_invalid_dir_cache(dir); + error = smb_proc_setattr_unix(dentry, &attr, MAJOR(dev), MINOR(dev)); + if (!error) { + error = smb_instantiate(dentry, 0, 0); + } + 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 old=%s/%s new=%s/%s\n", + DENTRY_PATH(dentry), 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; +} diff --git a/fs/smbfs/inode.c b/fs/smbfs/inode.c index abffff680118..44f1e1f66b27 100644 --- a/fs/smbfs/inode.c +++ b/fs/smbfs/inode.c @@ -106,6 +106,7 @@ static struct super_operations smb_sops = struct inode * smb_iget(struct super_block *sb, struct smb_fattr *fattr) { + struct smb_sb_info *server = SMB_SB(sb); struct inode *result; DEBUG1("smb_iget: %p\n", fattr); @@ -126,8 +127,15 @@ smb_iget(struct super_block *sb, struct smb_fattr *fattr) result->i_fop = &smb_file_operations; result->i_data.a_ops = &smb_file_aops; } else if (S_ISDIR(result->i_mode)) { - result->i_op = &smb_dir_inode_operations; + if (server->opt.capabilities & SMB_CAP_UNIX) + result->i_op = &smb_dir_inode_operations_unix; + else + result->i_op = &smb_dir_inode_operations; result->i_fop = &smb_dir_operations; + } else if (S_ISLNK(result->i_mode)) { + result->i_op = &smb_link_inode_operations; + } else { + init_special_inode(result, result->i_mode, fattr->f_rdev); } insert_inode_hash(result); return result; @@ -235,7 +243,14 @@ smb_refresh_inode(struct dentry *dentry) /* * Check whether the type part of the mode changed, * and don't update the attributes if it did. + * + * And don't dick with the root inode */ + if (inode->i_ino == 2) + return error; + if (S_ISLNK(inode->i_mode)) + return error; /* VFS will deal with it */ + if ((inode->i_mode & S_IFMT) == (fattr.f_mode & S_IFMT)) { smb_set_inode_attr(inode, &fattr); } else { @@ -665,6 +680,17 @@ smb_notify_change(struct dentry *dentry, struct iattr *attr) refresh = 1; } + if (server->opt.capabilities & SMB_CAP_UNIX) { + /* For now we don't want to set the size with setattr_unix */ + attr->ia_valid &= ~ATTR_SIZE; + /* FIXME: only call if we actually want to set something? */ + error = smb_proc_setattr_unix(dentry, attr, 0, 0); + if (!error) + refresh = 1; + + goto out; + } + /* * Initialize the fattr and check for changed fields. * Note: CTIME under SMB is creation time rather than diff --git a/fs/smbfs/proc.c b/fs/smbfs/proc.c index bdba1c5432f8..21e76a6b58a2 100644 --- a/fs/smbfs/proc.c +++ b/fs/smbfs/proc.c @@ -54,6 +54,7 @@ static struct smb_ops smb_ops_core; static struct smb_ops smb_ops_os2; static struct smb_ops smb_ops_win95; static struct smb_ops smb_ops_winNT; +static struct smb_ops smb_ops_unix; static void smb_init_dirent(struct smb_sb_info *server, struct smb_fattr *fattr); @@ -71,6 +72,8 @@ smb_proc_setattr_core(struct smb_sb_info *server, struct dentry *dentry, static int smb_proc_setattr_ext(struct smb_sb_info *server, struct inode *inode, struct smb_fattr *fattr); +int +smb_proc_query_cifsunix(struct smb_sb_info *server); static void install_ops(struct smb_ops *dst, struct smb_ops *src); @@ -521,15 +524,47 @@ smb_ntutc2unixutc(u64 ntutc) return (time_t)t; } -#if 0 /* Convert the Unix UTC into NT time */ static u64 -smb_unixutc2ntutc(struct smb_sb_info *server, time_t t) +smb_unixutc2ntutc(time_t t) { /* Note: timezone conversion is probably wrong. */ - return ((u64)utc2local(server, t)) * 10000000 + NTFS_TIME_OFFSET; + /* return ((u64)utc2local(server, t)) * 10000000 + NTFS_TIME_OFFSET; */ + return ((u64)t) * 10000000 + NTFS_TIME_OFFSET; +} + +#define MAX_FILE_MODE 6 +static mode_t file_mode[] = { + S_IFREG, S_IFDIR, S_IFLNK, S_IFCHR, S_IFBLK, S_IFIFO, S_IFSOCK +}; + +static int smb_filetype_to_mode(u32 filetype) +{ + if (filetype > MAX_FILE_MODE) { + PARANOIA("Filetype out of range: %d\n", filetype); + return S_IFREG; + } + return file_mode[filetype]; +} + +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; } -#endif /*****************************************************************************/ @@ -913,6 +948,14 @@ smb_newconn(struct smb_sb_info *server, struct smb_conn_opt *opt) VERBOSE("Large writes enabled\n"); } #endif + if (server->opt.capabilities & SMB_CAP_UNIX) { + struct inode *inode; + VERBOSE("Using UNIX CIFS extensions\n"); + install_ops(server->ops, &smb_ops_unix); + inode = SB_of(server)->s_root->d_inode; + if (inode) + inode->i_op = &smb_dir_inode_operations_unix; + } VERBOSE("protocol=%d, max_xmit=%d, pid=%d capabilities=0x%x\n", server->opt.protocol, server->opt.max_xmit, server->conn_pid, @@ -923,6 +966,12 @@ smb_newconn(struct smb_sb_info *server, struct smb_conn_opt *opt) server->opt.max_xmit = SMB_MAX_PACKET_SIZE; } + smb_unlock_server(server); + smbiod_wake_up(); + if (server->opt.capabilities & SMB_CAP_UNIX) + smb_proc_query_cifsunix(server); + return error; + out: smb_unlock_server(server); smbiod_wake_up(); @@ -1197,7 +1246,8 @@ smb_proc_close_inode(struct smb_sb_info *server, struct inode * ino) * If the file is open with write permissions, * update the time stamps to sync mtime and atime. */ - if ((server->opt.protocol >= SMB_PROTOCOL_LANMAN2) && + if ((server->opt.capabilities & SMB_CAP_UNIX) == 0 && + (server->opt.protocol >= SMB_PROTOCOL_LANMAN2) && !(ei->access == SMB_O_RDONLY)) { struct smb_fattr fattr; @@ -1593,6 +1643,8 @@ smb_set_rw(struct dentry *dentry,struct smb_sb_info *server) int result; struct smb_fattr fattr; + /* FIXME: cifsUE should allow removing a readonly file. */ + /* first get current attribute */ smb_init_dirent(server, &fattr); result = server->ops->getattr(server, dentry, &fattr); @@ -1762,14 +1814,17 @@ smb_init_dirent(struct smb_sb_info *server, struct smb_fattr *fattr) 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) { + if (fattr->f_unix) + return; + fattr->f_mode = server->mnt->file_mode; - if (fattr->attr & aDIR) - { + if (fattr->attr & aDIR) { fattr->f_mode = server->mnt->dir_mode; fattr->f_size = SMB_ST_BLKSIZE; } @@ -2006,6 +2061,43 @@ out: return result; } +void smb_decode_unix_basic(struct smb_fattr *fattr, char *p) +{ + /* FIXME: verify nls support. all is sent as utf8? */ + __u64 devmajor, devminor; + + fattr->f_unix = 1; + fattr->f_mode = 0; + + /* FIXME: use the uniqueID from the remote instead? */ + /* 0 L file size in bytes */ + /* 8 L file size on disk in bytes (block count) */ + /* 40 L uid */ + /* 48 L gid */ + /* 56 W file type */ + /* 60 L devmajor */ + /* 68 L devminor */ + /* 76 L unique ID (inode) */ + /* 84 L permissions */ + /* 92 L link count */ + + fattr->f_size = LVAL(p, 0); + fattr->f_blocks = LVAL(p, 8); + fattr->f_ctime = smb_ntutc2unixutc(LVAL(p, 16)); + fattr->f_atime = smb_ntutc2unixutc(LVAL(p, 24)); + fattr->f_mtime = smb_ntutc2unixutc(LVAL(p, 32)); + fattr->f_uid = LVAL(p, 40); + fattr->f_gid = LVAL(p, 48); + fattr->f_mode |= smb_filetype_to_mode(WVAL(p, 56)); + + if (S_ISBLK(fattr->f_mode) || S_ISCHR(fattr->f_mode)) { + devmajor = LVAL(p, 60); + devminor = LVAL(p, 68); + fattr->f_rdev = ((devmajor & 0xFF) << 8) | (devminor & 0xFF); + } + fattr->f_mode |= LVAL(p, 84); +} + /* * Interpret a long filename structure using the specified info level: * level 1 for anything below NT1 protocol @@ -2078,6 +2170,18 @@ smb_decode_long_dirent(struct smb_sb_info *server, char *p, int level, 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); + /* FIXME: should we check the length?? */ + + p += 8; + smb_decode_unix_basic(fattr, p); + VERBOSE("info SMB_FIND_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); @@ -2166,9 +2270,12 @@ smb_proc_readdir_long(struct file *filp, void *dirent, filldir_t filldir, lock_kernel(); /* - * use info level 1 for older servers that don't do 260 + * We always prefer unix style. Use info level 1 for older + * servers that don't do 260. */ - if (server->opt.protocol < SMB_PROTOCOL_NT1) + if (server->opt.capabilities & SMB_CAP_UNIX) + info_level = SMB_FIND_FILE_UNIX; + else if (server->opt.protocol < SMB_PROTOCOL_NT1) info_level = 1; result = -ENOMEM; @@ -2309,13 +2416,18 @@ smb_proc_readdir_long(struct file *filp, void *dirent, filldir_t filldir, * here that those who do not point to a filename do not need * this info to continue the listing. * - * OS/2 needs this and talks infolevel 1 - * NetApps want lastname with infolevel 260 + * OS/2 needs this and talks infolevel 1. + * NetApps want lastname with infolevel 260. + * win2k want lastname with infolevel 260, and points to + * the record not to the name. + * Samba+CifsUnixExt doesn't need lastname. * * Both are happy if we return the data they point to. So we do. + * (FIXME: above is not true with win2k) */ mask_len = 0; - if (ff_lastname > 0 && ff_lastname < req->rq_ldata) { + if (info_level != SMB_FIND_FILE_UNIX && + ff_lastname > 0 && ff_lastname < req->rq_ldata) { lastname = req->rq_data + ff_lastname; switch (info_level) { @@ -2607,6 +2719,30 @@ out: } static int +smb_proc_getattr_unix(struct smb_sb_info *server, struct dentry *dir, + struct smb_fattr *attr) +{ + struct smb_request *req; + int result; + + result = -ENOMEM; + if (! (req = smb_alloc_request(server, PAGE_SIZE))) + goto out; + + result = smb_proc_getattr_trans2(server, dir, req, + SMB_QUERY_FILE_UNIX_BASIC); + if (result < 0) + goto out_free; + + smb_decode_unix_basic(attr, req->rq_data); + +out_free: + smb_rput(req); +out: + return result; +} + +static int smb_proc_getattr_95(struct smb_sb_info *server, struct dentry *dir, struct smb_fattr *attr) { @@ -2825,6 +2961,120 @@ out: } /* + * 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 + * + * major/minor should only be set by mknod. + */ +int +smb_proc_setattr_unix(struct dentry *d, struct iattr *attr, + int major, int minor) +{ + struct smb_sb_info *server = server_from_dentry(d); + u64 nttime; + char *p, *param; + int result; + char data[100]; + struct smb_request *req; + + result = -ENOMEM; + if (! (req = smb_alloc_request(server, PAGE_SIZE))) + goto out; + param = req->rq_buffer; + + DEBUG1("valid flags = 0x%04x\n", attr->ia_valid); + + WSET(param, 0, SMB_SET_FILE_UNIX_BASIC); + DSET(param, 2, 0); + result = smb_encode_path(server, param+6, SMB_MAXPATHLEN+1, d, NULL); + if (result < 0) + goto out_free; + p = param + 6 + result; + + /* 0 L file size in bytes */ + /* 8 L file size on disk in bytes (block count) */ + /* 40 L uid */ + /* 48 L gid */ + /* 56 W file type enum */ + /* 60 L devmajor */ + /* 68 L devminor */ + /* 76 L unique ID (inode) */ + /* 84 L permissions */ + /* 92 L link count */ + LSET(data, 0, SMB_SIZE_NO_CHANGE); + LSET(data, 8, SMB_SIZE_NO_CHANGE); + LSET(data, 16, SMB_TIME_NO_CHANGE); + LSET(data, 24, SMB_TIME_NO_CHANGE); + LSET(data, 32, SMB_TIME_NO_CHANGE); + LSET(data, 40, SMB_UID_NO_CHANGE); + LSET(data, 48, SMB_GID_NO_CHANGE); + LSET(data, 56, smb_filetype_from_mode(attr->ia_mode)); + LSET(data, 60, major); + LSET(data, 68, minor); + LSET(data, 76, 0); + LSET(data, 84, SMB_MODE_NO_CHANGE); + LSET(data, 92, 0); + + if (attr->ia_valid & ATTR_SIZE) { + LSET(data, 0, attr->ia_size); + LSET(data, 8, 0); /* can't set anyway */ + } + + /* + * FIXME: 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) { + nttime = smb_unixutc2ntutc(attr->ia_ctime); + LSET(data, 16, nttime); + } + if (attr->ia_valid & ATTR_ATIME) { + nttime = smb_unixutc2ntutc(attr->ia_atime); + LSET(data, 24, nttime); + } + if (attr->ia_valid & ATTR_MTIME) { + nttime = smb_unixutc2ntutc(attr->ia_mtime); + LSET(data, 32, nttime); + } + + if (attr->ia_valid & ATTR_UID) { + LSET(data, 40, attr->ia_uid); + } + if (attr->ia_valid & ATTR_GID) { + LSET(data, 48, attr->ia_gid); + } + + if (attr->ia_valid & ATTR_MODE) { + LSET(data, 84, attr->ia_mode); + } + + req->rq_trans2_command = TRANSACT2_SETPATHINFO; + req->rq_ldata = 100; + req->rq_data = data; + req->rq_lparm = p - param; + req->rq_parm = param; + req->rq_flags = 0; + result = smb_add_request(req); + +out_free: + smb_rput(req); +out: + return result; +} + + +/* * Set the modify and access timestamps for a file. * * Incredibly enough, in all of SMB there is no message to allow @@ -2907,6 +3157,189 @@ out: return result; } +int +smb_proc_read_link(struct smb_sb_info *server, struct dentry *d, + char *buffer, int len) +{ + char *p, *param; + int result; + struct smb_request *req; + + DEBUG1("readlink of %s/%s\n", DENTRY_PATH(d)); + + result = -ENOMEM; + if (! (req = smb_alloc_request(server, PAGE_SIZE))) + goto out; + param = req->rq_buffer; + + WSET(param, 0, SMB_QUERY_FILE_UNIX_LINK); + DSET(param, 2, 0); + result = smb_encode_path(server, param+6, SMB_MAXPATHLEN+1, d, NULL); + if (result < 0) + goto out_free; + p = param + 6 + result; + + req->rq_trans2_command = TRANSACT2_QPATHINFO; + req->rq_ldata = 0; + req->rq_data = NULL; + req->rq_lparm = p - param; + req->rq_parm = param; + req->rq_flags = 0; + result = smb_add_request(req); + if (result < 0) + goto out_free; + DEBUG1("for %s: result=%d, rcls=%d, err=%d\n", + ¶m[6], result, server->rcls, server->err); + + /* copy data up to the \0 or buffer length */ + result = len; + if (req->rq_ldata < len) + result = req->rq_ldata; + strncpy(buffer, req->rq_data, result); + +out_free: + smb_rput(req); +out: + return result; +} + + +/* + * Create a symlink object called dentry which points to oldpath. + * Samba does not permit dangling links but returns a suitable error message. + */ +int +smb_proc_symlink(struct smb_sb_info *server, struct dentry *d, + const char *oldpath) +{ + char *p, *param; + int result; + struct smb_request *req; + + result = -ENOMEM; + if (! (req = smb_alloc_request(server, PAGE_SIZE))) + goto out; + param = req->rq_buffer; + + WSET(param, 0, SMB_SET_FILE_UNIX_LINK); + DSET(param, 2, 0); + result = smb_encode_path(server, param + 6, SMB_MAXPATHLEN+1, d, NULL); + if (result < 0) + goto out_free; + p = param + 6 + result; + + req->rq_trans2_command = TRANSACT2_SETPATHINFO; + req->rq_ldata = strlen(oldpath) + 1; + req->rq_data = (char *) oldpath; + req->rq_lparm = p - param; + req->rq_parm = param; + req->rq_flags = 0; + result = smb_add_request(req); + if (result < 0) + goto out_free; + + DEBUG1("for %s: result=%d, rcls=%d, err=%d\n", + ¶m[6], result, server->rcls, server->err); + result = 0; + +out_free: + smb_rput(req); +out: + return result; +} + +/* + * Create a hard link object called new_dentry which points to dentry. + */ +int +smb_proc_link(struct smb_sb_info *server, struct dentry *dentry, + struct dentry *new_dentry) +{ + char *p, *param; + int result; + struct smb_request *req; + + result = -ENOMEM; + if (! (req = smb_alloc_request(server, PAGE_SIZE))) + goto out; + param = req->rq_buffer; + + WSET(param, 0, SMB_SET_FILE_UNIX_HLINK); + DSET(param, 2, 0); + result = smb_encode_path(server, param + 6, SMB_MAXPATHLEN+1, + new_dentry, NULL); + if (result < 0) + goto out_free; + p = param + 6 + result; + + /* Grr, pointless separation of parameters and data ... */ + req->rq_data = p; + req->rq_ldata = smb_encode_path(server, p, SMB_MAXPATHLEN+1, + dentry, NULL); + + req->rq_trans2_command = TRANSACT2_SETPATHINFO; + req->rq_lparm = p - param; + req->rq_parm = param; + req->rq_flags = 0; + result = smb_add_request(req); + if (result < 0) + goto out_free; + + DEBUG1("for %s: result=%d, rcls=%d, err=%d\n", + ¶m[6], result, server->rcls, server->err); + result = 0; + +out_free: + smb_rput(req); +out: + return result; +} + +int +smb_proc_query_cifsunix(struct smb_sb_info *server) +{ + int result; + int major, minor; + u64 caps; + char param[2]; + struct smb_request *req; + + result = -ENOMEM; + if (! (req = smb_alloc_request(server, 100))) + goto out; + + WSET(param, 0, SMB_QUERY_CIFS_UNIX_INFO); + + req->rq_trans2_command = TRANSACT2_QFSINFO; + req->rq_ldata = 0; + req->rq_data = NULL; + req->rq_lparm = 2; + req->rq_parm = param; + req->rq_flags = 0; + result = smb_add_request(req); + if (result < 0) + goto out_free; + + if (req->rq_ldata < 12) { + PARANOIA("Not enough data\n"); + goto out_free; + } + major = WVAL(req->rq_data, 0); + minor = WVAL(req->rq_data, 2); + + DEBUG1("Server implements CIFS Extensions for UNIX systems v%d.%d\n", + major, minor); + /* FIXME: verify that we are ok with this major/minor? */ + + caps = LVAL(req->rq_data, 4); + DEBUG1("Server capabilities 0x%016llx\n", caps); + +out_free: + smb_rput(req); +out: + return result; +} + static void install_ops(struct smb_ops *dst, struct smb_ops *src) @@ -2953,3 +3386,15 @@ static struct smb_ops smb_ops_winNT = .getattr = smb_proc_getattr_trans2_all, .truncate = smb_proc_trunc64, }; + +/* Samba w/ unix extensions. Others? */ +static struct smb_ops smb_ops_unix = +{ + .read = smb_proc_readX, + .write = smb_proc_writeX, + .readdir = smb_proc_readdir_long, + .getattr = smb_proc_getattr_unix, + /* FIXME: core/ext/time setattr needs to be cleaned up! */ + /* .setattr = smb_proc_setattr_unix, */ + .truncate = smb_proc_trunc64, +}; diff --git a/fs/smbfs/proto.h b/fs/smbfs/proto.h index da0717681907..fe9345ca52ce 100644 --- a/fs/smbfs/proto.h +++ b/fs/smbfs/proto.h @@ -1,5 +1,5 @@ /* - * Autogenerated with cproto on: Fri Jul 12 22:15:26 CEST 2002 + * Autogenerated with cproto on: Sun Sep 29 21:48:59 CEST 2002 */ struct smb_request; @@ -22,13 +22,20 @@ extern int smb_proc_rmdir(struct dentry *dentry); extern int smb_proc_unlink(struct dentry *dentry); extern int smb_proc_flush(struct smb_sb_info *server, __u16 fileid); 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_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 *d, struct iattr *attr, int major, int minor); 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 *d, char *buffer, int len); +extern int smb_proc_symlink(struct smb_sb_info *server, struct dentry *d, 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 struct inode_operations smb_dir_inode_operations_unix; extern void smb_new_dentry(struct dentry *dentry); extern void smb_renew_times(struct dentry *dentry); /* cache.c */ @@ -76,3 +83,8 @@ extern int smb_add_request(struct smb_request *req); extern int smb_request_send_req(struct smb_request *req); extern int smb_request_send_server(struct smb_sb_info *server); extern int smb_request_recv(struct smb_sb_info *server); +/* 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 --git a/fs/smbfs/symlink.c b/fs/smbfs/symlink.c new file mode 100644 index 000000000000..fe4831233f03 --- /dev/null +++ b/fs/smbfs/symlink.c @@ -0,0 +1,68 @@ +/* + * symlink.c + * + * Copyright (C) 2002 by John Newbigin + * + * Please add a note about your changes to smbfs in the ChangeLog file. + */ + +#include <linux/sched.h> +#include <linux/kernel.h> +#include <linux/errno.h> +#include <linux/fcntl.h> +#include <linux/stat.h> +#include <linux/mm.h> +#include <linux/slab.h> +#include <linux/pagemap.h> +#include <linux/smp_lock.h> + +#include <asm/uaccess.h> +#include <asm/system.h> + +#include <linux/smbno.h> +#include <linux/smb_fs.h> + +#include "smb_debug.h" +#include "proto.h" + +int smb_read_link(struct dentry *dentry, char *buffer, int len) +{ + char link[256]; /* FIXME: pain ... */ + 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 -ENOENT; + return vfs_readlink(dentry, buffer, len, link); +} + +int smb_symlink(struct inode *inode, struct dentry *dentry, const char *oldname) +{ + DEBUG1("create symlink %s -> %s/%s\n", oldname, DENTRY_PATH(dentry)); + + return smb_proc_symlink(server_from_dentry(dentry), dentry, oldname); +} + +int smb_follow_link(struct dentry *dentry, struct nameidata *nd) +{ + char link[256]; /* FIXME: pain ... */ + 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) + return -ENOENT; + + link[len] = 0; + return vfs_follow_link(nd, link); +} + + +struct inode_operations smb_link_inode_operations = +{ + .readlink smb_read_link, + .follow_link smb_follow_link, +}; diff --git a/include/linux/smb.h b/include/linux/smb.h index 5b8dce292377..9e4944b6121f 100644 --- a/include/linux/smb.h +++ b/include/linux/smb.h @@ -76,7 +76,6 @@ struct smb_nls_codepage { * Contains all relevant data on a SMB networked file. */ struct smb_fattr { - __u16 attr; unsigned long f_ino; @@ -84,12 +83,14 @@ struct smb_fattr { nlink_t f_nlink; uid_t f_uid; gid_t f_gid; + dev_t f_rdev; loff_t f_size; time_t f_atime; time_t f_mtime; time_t f_ctime; unsigned long f_blksize; unsigned long f_blocks; + int f_unix; }; enum smb_conn_state { diff --git a/include/linux/smb_fs.h b/include/linux/smb_fs.h index be0f419928bc..41f9cd84c0b9 100644 --- a/include/linux/smb_fs.h +++ b/include/linux/smb_fs.h @@ -112,19 +112,20 @@ smb_kfree(void *obj) /* NT1 protocol capability bits */ -#define SMB_CAP_RAW_MODE 0x0001 -#define SMB_CAP_MPX_MODE 0x0002 -#define SMB_CAP_UNICODE 0x0004 -#define SMB_CAP_LARGE_FILES 0x0008 -#define SMB_CAP_NT_SMBS 0x0010 -#define SMB_CAP_RPC_REMOTE_APIS 0x0020 -#define SMB_CAP_STATUS32 0x0040 -#define SMB_CAP_LEVEL_II_OPLOCKS 0x0080 -#define SMB_CAP_LOCK_AND_READ 0x0100 -#define SMB_CAP_NT_FIND 0x0200 -#define SMB_CAP_DFS 0x1000 -#define SMB_CAP_LARGE_READX 0x4000 -#define SMB_CAP_LARGE_WRITEX 0x8000 +#define SMB_CAP_RAW_MODE 0x00000001 +#define SMB_CAP_MPX_MODE 0x00000002 +#define SMB_CAP_UNICODE 0x00000004 +#define SMB_CAP_LARGE_FILES 0x00000008 +#define SMB_CAP_NT_SMBS 0x00000010 +#define SMB_CAP_RPC_REMOTE_APIS 0x00000020 +#define SMB_CAP_STATUS32 0x00000040 +#define SMB_CAP_LEVEL_II_OPLOCKS 0x00000080 +#define SMB_CAP_LOCK_AND_READ 0x00000100 +#define SMB_CAP_NT_FIND 0x00000200 +#define SMB_CAP_DFS 0x00001000 +#define SMB_CAP_LARGE_READX 0x00004000 +#define SMB_CAP_LARGE_WRITEX 0x00008000 +#define SMB_CAP_UNIX 0x00800000 /* unofficial ... */ /* diff --git a/include/linux/smbno.h b/include/linux/smbno.h index c1495bba359f..c202e2d6cb80 100644 --- a/include/linux/smbno.h +++ b/include/linux/smbno.h @@ -328,4 +328,36 @@ #define SMB_FLAGS2_32_BIT_ERROR_CODES 0x4000 #define SMB_FLAGS2_UNICODE_STRINGS 0x8000 + +/* + * UNIX stuff (from samba trans2.h) + */ +#define MIN_UNIX_INFO_LEVEL 0x200 +#define MAX_UNIX_INFO_LEVEL 0x2FF +#define SMB_FIND_FILE_UNIX 0x202 +#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_SET_FILE_UNIX_LINK 0x201 +#define SMB_SET_FILE_UNIX_HLINK 0x203 +#define SMB_QUERY_CIFS_UNIX_INFO 0x200 + +/* values which means "don't change it" */ +#define SMB_MODE_NO_CHANGE 0xFFFFFFFF +#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 + #endif /* _SMBNO_H_ */ |
