diff -r -c --new-file client-2.0.33.orig/Documentation/Configure.help client-2.0.33.s.v0.3/Documentation/Configure.help *** client-2.0.33.orig/Documentation/Configure.help Fri Apr 21 22:35:14 2000 --- client-2.0.33.s.v0.3/Documentation/Configure.help Fri Apr 21 22:37:36 2000 *************** *** 3586,3591 **** --- 3586,3602 ---- directories unreliable. This option slows down the listing of directories. This makes the Windows 95 server a bit more stable. + Experimental SMB Symlink work-around + CONFIG_SMB_SYMLINKS + If you have to use SMB shares e. g. for home directories, this option + provides symlinks for you. The workaround uses the system attribute, so + the server has to allow you to modify it (which on samba means "map + symlinks = yes" and "create mask = 750" (010, the group exec bit, is + usually mapped as the system bit)). + Note that if you use samba for the server, the symlinks WILL NOT be + visible as such on the server; you will just have a plain file with the + magic cookie and the content of the symlink. + NCP filesystem support (to mount NetWare volumes) CONFIG_NCP_FS NCP (NetWare Core Protocol) is a protocol that runs over IPX and is diff -r -c --new-file client-2.0.33.orig/fs/Config.in client-2.0.33.s.v0.3/fs/Config.in *** client-2.0.33.orig/fs/Config.in Fri Apr 21 22:35:14 2000 --- client-2.0.33.s.v0.3/fs/Config.in Wed Apr 19 16:30:35 2000 *************** *** 32,37 **** --- 32,38 ---- tristate 'SMB filesystem support (to mount WfW shares etc..)' CONFIG_SMB_FS if [ "$CONFIG_SMB_FS" != "n" ]; then bool 'SMB Win95 bug work-around' CONFIG_SMB_WIN95 + bool 'SMB symlinks in system attribute (EXPERIMENTAL)' CONFIG_SMB_SYMLINKS fi fi if [ "$CONFIG_IPX" != "n" ]; then diff -r -c --new-file client-2.0.33.orig/fs/smbfs/Makefile client-2.0.33.s.v0.3/fs/smbfs/Makefile *** client-2.0.33.orig/fs/smbfs/Makefile Fri Apr 21 22:35:14 2000 --- client-2.0.33.s.v0.3/fs/smbfs/Makefile Fri Apr 21 22:10:49 2000 *************** *** 9,14 **** --- 9,18 ---- O_TARGET := smbfs.o O_OBJS := proc.o sock.o inode.o file.o dir.o ioctl.o mmap.o + ifeq ($(CONFIG_SMB_SYMLINKS),y) + O_OBJS += symlink.o + endif + M_OBJS := $(O_TARGET) # If you want debugging output, please uncomment the following line diff -r -c --new-file client-2.0.33.orig/fs/smbfs/dir.c client-2.0.33.s.v0.3/fs/smbfs/dir.c *** client-2.0.33.orig/fs/smbfs/dir.c Fri Apr 21 22:35:14 2000 --- client-2.0.33.s.v0.3/fs/smbfs/dir.c Thu Apr 20 20:33:55 2000 *************** *** 49,54 **** --- 49,56 ---- struct inode *new_dir, const char *new_name, int new_len, int must_be_dir); + int smb_symlink(struct inode *, const char *, int, const char *); + static struct file_operations smb_dir_operations = { NULL, /* lseek - default */ *************** *** 70,76 **** --- 72,82 ---- smb_lookup, /* lookup */ NULL, /* link */ smb_unlink, /* unlink */ + #ifdef CONFIG_SMB_SYMLINKS + smb_symlink, /* symlink */ + #else NULL, /* symlink */ + #endif smb_mkdir, /* mkdir */ smb_rmdir, /* rmdir */ NULL, /* mknod */ diff -r -c --new-file client-2.0.33.orig/fs/smbfs/inode.c client-2.0.33.s.v0.3/fs/smbfs/inode.c *** client-2.0.33.orig/fs/smbfs/inode.c Fri Apr 21 22:35:14 2000 --- client-2.0.33.s.v0.3/fs/smbfs/inode.c Thu Apr 20 21:12:02 2000 *************** *** 29,34 **** --- 29,39 ---- static void smb_put_super(struct super_block *); static void smb_statfs(struct super_block *, struct statfs *, int bufsiz); + static int smb_follow_link(struct inode *, struct inode *, int, int, struct inode **); + static int smb_readlink (struct inode *, char *, int); + int smb_symlink(struct inode *, const char *, int, const char *); + + static struct super_operations smb_sops = { smb_read_inode, /* read inode */ *************** *** 41,46 **** --- 46,53 ---- NULL }; + struct inode_operations smb_symlink_inode_operations; + /* smb_read_inode: Called from iget, it only traverses the allocated smb_inode_info's and initializes the inode from the data found there. It does not allocate or deallocate anything. */ *************** *** 84,89 **** --- 91,101 ---- } else if (S_ISDIR(inode->i_mode)) { inode->i_op = &smb_dir_inode_operations; + #ifdef CONFIG_SMB_SYMLINKS + } else if (S_ISLNK(inode->i_mode)) + { + inode->i_op = &smb_symlink_inode_operations; + #endif } else { inode->i_op = NULL; *************** *** 397,402 **** --- 409,417 ---- struct smb_dirent finfo; if (attr->ia_mode & ~(S_IFREG | S_IFDIR | + #ifdef CONFIG_SMB_SYMLINKS + S_IFLNK | + #endif S_IRWXU | S_IRWXG | S_IRWXO)) return -EPERM; *************** *** 408,413 **** --- 423,434 ---- else finfo.attr &= ~aRONLY; + if((attr->ia_mode & S_IFLNK) == S_IFLNK) + finfo.attr |= aSYSTEM; + else + finfo.attr &= ~aSYSTEM; + + if ((error = smb_proc_setattr(SMB_SERVER(inode), inode, &finfo)) >= 0) { *************** *** 499,501 **** --- 520,523 ---- } #endif + diff -r -c --new-file client-2.0.33.orig/fs/smbfs/proc.c client-2.0.33.s.v0.3/fs/smbfs/proc.c *** client-2.0.33.orig/fs/smbfs/proc.c Fri Apr 21 22:35:14 2000 --- client-2.0.33.s.v0.3/fs/smbfs/proc.c Fri Apr 21 22:12:45 2000 *************** *** 685,691 **** --- 685,717 ---- res = WVAL(buf, smb_vwv0); } smb_unlock_server(server); + return res; + } + int + smb_proc_write_mem(struct smb_server *server, struct smb_dirent *finfo, + off_t offset, int count, const char *data) + { + int res = 0; + char *buf; + byte *p; + + p = smb_setup_header_exclusive(server, SMBwrite, 5, count + 3); + buf = server->packet; + WSET(buf, smb_vwv0, finfo->fileid); + WSET(buf, smb_vwv1, count); + DSET(buf, smb_vwv2, offset); + WSET(buf, smb_vwv4, 0); + + *p++ = 1; + WSET(p, 0, count); + + memcpy(p + 2, data, count); + if ((res = smb_request_ok(server, SMBwrite, 1, 0)) >= 0) + { + res = WVAL(buf, smb_vwv0); + } + smb_unlock_server(server); return res; } *************** *** 891,896 **** --- 917,928 ---- if (entry->attr & aRONLY) entry->f_mode &= ~0222; + #ifdef CONFIG_SMB_SYMLINKS + /* symlinks mustn't be anything but symlinks */ + if (entry->attr & aSYSTEM) + entry->f_mode = (entry->f_mode & ~S_IFMT) | S_IFLNK; + #endif + if ((entry->f_blksize != 0) && (entry->f_size != 0)) { entry->f_blocks = diff -r -c --new-file client-2.0.33.orig/fs/smbfs/symlink.c client-2.0.33.s.v0.3/fs/smbfs/symlink.c *** client-2.0.33.orig/fs/smbfs/symlink.c Thu Jan 1 01:00:00 1970 --- client-2.0.33.s.v0.3/fs/smbfs/symlink.c Wed Apr 26 14:53:14 2000 *************** *** 0 **** --- 1,421 ---- + /* + * symlink.c + * v 0.3, Wed Apr 26 14:53:02 CEST 2000 + * + * built using parts of smbfs/file.c + * Copyright (C) 1995, 1996, 1997 by Paal-Kr. Engstad and Volker Lendecke + * Copyright (C) 1997 by Volker Lendecke + * and linux/fs/ncpfs/symlink.c + * Copyright (C) 1998-99, Frank A. Vorstenbosch + * as well as linux/fs/ext2/symlink.c + * Copyright (C) 1992, 1993, 1994, 1995 + * Remy Card (card@masi.ibp.fr) + * Laboratoire MASI - Institut Blaise Pascal + * Universite Pierre et Marie Curie (Paris VI) + * which itself borrowed from linux/fs/minix/symlink.c + * Copyright (C) 1991, 1992 Linus Torvalds + * + * (C) 2000 Alexander Oelzant + * + */ + + #include + #include + + #include + #include + #include + #include + #include + #include + #include + #include + #include + + #include "symlink.h" + + #ifdef DEBUG_SMB + #define SMBFS_DEBUG_VERBOSE + #endif + + int smb_create(struct inode *dir, const char *name, int len, int mode, struct inode **result); + int smb_proc_open(struct smb_server *server, struct smb_inode_info *dir, const char *name, int len, struct smb_dirent *entry); + int smb_proc_create(struct inode *dir, const char *name, int len, word attr, time_t ctime); + static int smb_readlink (struct inode *, char *, int); + static int smb_follow_link (struct inode *, struct inode *, int, int, + struct inode **); + struct inode_operations smb_symlink_inode_operations; + ino_t smb_fresh_inodes(struct smb_server *server, int no); + struct inode * smb_iget(struct inode *dir, + struct smb_inode_info *new_inode_info); + + + + static inline int + min(int a, int b) + { + return a < b ? a : b; + } + + + /* ----- follow a symbolic link ------------------------------------------ */ + + static int smb_follow_link(struct inode * dir, struct inode * inode, int flag, int mode, struct inode ** res_inode) + { + + int error, length; + struct smb_dirent *de; + char *link; + + *res_inode = NULL; + de = SMB_FINFO(inode); + DPRINTK("smb_follow_link: (%s),",de->name); + if (!dir) { + dir = current->fs->root; + dir->i_count++; + } + if (!inode) { + iput (dir); + return -ENOENT; + } + if (!S_ISLNK(inode->i_mode)) { + iput (dir); + *res_inode = inode; + return 0; + } + + if (current->link_count > 5) { + iput (dir); + iput (inode); + return -ELOOP; + } + + if ((length = smb_proc_open(SMB_SERVER(inode),SMB_INOP(inode)->dir,de->name,de->len, de)) != 0) + return length; + + if (inode->i_blocks) { + #ifdef SMBFS_DEBUG_VERBOSE + printk(" %i blocks to read,",inode->i_blocks); + #endif + link = (char *)smb_kmalloc(SMB_MAX_SYMLINK_SIZE, GFP_KERNEL); + if (link == NULL) { + iput (inode); + return -ENOMEM; + } + length = smb_proc_read (SMB_SERVER(inode), SMB_FINFO(inode), 0, + min(SMB_MAX_SYMLINK_SIZE-1, de->f_size), link, 0); + if (length < 0) { + #ifdef SMBFS_DEBUG_VERBOSE + printk(" error %i on read\n",length); + #endif + smb_kfree_s(link,SMB_MAX_SYMLINK_SIZE); + iput (inode); + return length; + } + } else { + link = de->name; + length = de->len; + } + #ifdef SMBFS_DEBUG_VERBOSE + printk(" read ok,"); + #endif + if ((length < SMB_MIN_SYMLINK_SIZE) || + (length > SMB_MAX_SYMLINK_SIZE)) { + #ifdef SMBFS_DEBUG_VERBOSE + printk(" wrong length\n"); + #endif + + smb_kfree_s(link,SMB_MAX_SYMLINK_SIZE); + iput (inode); + return -EIO; + } + if ((((__u32 *)link)[0] != SMB_SYMLINK_MAGIC0) || + (((__u32 *)link)[1] != SMB_SYMLINK_MAGIC1)) { + #ifdef SMBFS_DEBUG_VERBOSE + printk(" no cookie\n"); + #endif + smb_kfree_s(link,SMB_MAX_SYMLINK_SIZE); + iput (inode); + return -EIO; + } + + + + UPDATE_ATIME(inode); + current->link_count++; + error = open_namei (link+8, flag, mode, res_inode, dir); + current->link_count--; + iput (inode); + return 0 /* open_namei doesn't like me, I guess ... otherwise error*/; + } + + /* + * symlinks can't do much... + */ + + /* ----- create a new symbolic link -------------------------------------- */ + + int smb_symlink(struct inode *dir, const char * name, int len, const char * symname) { + struct inode *inode; + struct smb_inode_info *new_inode_info; + struct smb_dirent entry, *dirent; + char *link; + int length, err, bufsize; + + #ifdef SMBFS_DEBUG_VERBOSE + printk("smb_symlink(dir=%p,name=%s,symname=%s):",dir,name,symname); + #endif + + if (!dir || !S_ISDIR(dir->i_mode)) { + printk("smb_create: inode is NULL or not a directory\n"); + iput(dir); + return -ENOENT; + } + + if ((length=strlen(symname))>SMB_MAX_SYMLINK_SIZE-8) { + #ifdef SMBFS_DEBUG_VERBOSE + printk(" symlink too long, raising SMB_MAX_SYMLINK_SIZE might help\n"); + #endif + return -EINVAL; + } + + new_inode_info = smb_kmalloc(sizeof(struct smb_inode_info), GFP_KERNEL); + if (new_inode_info == NULL) + { + iput(dir); + return -ENOMEM; + } + + err=smb_proc_create(dir, name, len, aSYSTEM, CURRENT_TIME); + if (err < 0){ + #ifdef SMBFS_DEBUG_VERBOSE + printk(" could not smb_proc_create, error %i\n",err); + #endif + smb_kfree_s(new_inode_info, sizeof(struct smb_inode_info)); + iput(dir); + return err; + } + + if ((err = smb_proc_getattr(dir, name, len, &entry)) < 0) + { + smb_kfree_s(new_inode_info, sizeof(struct smb_inode_info)); + iput(dir); + return err; + } + entry.f_ino = smb_fresh_inodes(SMB_SERVER(dir), 1); + new_inode_info->finfo = entry; + + if ((inode = smb_iget(dir, new_inode_info)) == NULL) + { + #ifdef SMBFS_DEBUG_VERBOSE + printk(" inode is NULL\n"); + #endif + iput(dir); + return err; + } + + link = (char *) smb_kmalloc(SMB_MAX_SYMLINK_SIZE, GFP_KERNEL); + if (link == NULL) { + #ifdef SMBFS_DEBUG_VERBOSE + printk(" could not malloc link buffer\n"); + #endif + return -ENOMEM; + } + ((__u32 *)link)[0]=SMB_SYMLINK_MAGIC0; + ((__u32 *)link)[1]=SMB_SYMLINK_MAGIC1; + + #ifdef SMBFS_DEBUG_VERBOSE + printk(" cookie,"); + #endif + + if (strncpy(link+8,symname,strlen(symname)) != (link+8)) { + #ifdef SMBFS_DEBUG_VERBOSE + printk("bombed on copy (%s)\n",link+8); + #endif + smb_kfree_s(link,SMB_MAX_SYMLINK_SIZE); + return -EIO; + } + #ifdef SMBFS_DEBUG_VERBOSE + printk(" symname,"); + #endif + + #ifdef SMBFS_DEBUG_VERBOSE + printk(" inode population,"); + #endif + dirent = &(SMB_INOP(inode)->finfo); + DDPRINTK("smb_make_open: dirent->opened = %d\n", dirent->opened); + if ((dirent->opened) == 0) + { + /* tries max. rights */ + if ((err = smb_proc_open(SMB_SERVER(dir), + SMB_INOP(dir), + dirent->name, + dirent->len, + dirent)) < 0) { + #ifdef SMBFS_DEBUG_VERBOSE + printk(" couldn't open\n"); + #endif + smb_kfree_s(link,SMB_MAX_SYMLINK_SIZE); + return err; + } + } + + DPRINTK("preparing to write %i (%s),",length+8,link); + bufsize = SMB_SERVER(inode)->max_xmit - SMB_HEADER_LEN - 5 * 2 - 5; + + if ((err = smb_proc_write_mem(SMB_SERVER(inode), SMB_FINFO(inode), 0, length+8, link)) < 0) { + #ifdef SMBFS_DEBUG_VERBOSE + printk(" bombed on write (%s) error %i\n",link+8, err); + #endif + smb_kfree_s(link,SMB_MAX_SYMLINK_SIZE); + return -EIO; + } + inode->i_size = length+8; + inode->i_dirt = 1; + + inode->i_mode = S_IFLNK | S_IRWXUGO; + inode->i_op = &smb_symlink_inode_operations; + #ifdef SMBFS_DEBUG_VERBOSE + printk(" write,"); + #endif + + dir->i_version = ++event; + #ifdef SMBFS_DEBUG_VERBOSE + printk(" dirversion,"); + #endif + /* dcache_add(dir, name, len, &inode); */ + + iput (dir); + #ifdef SMBFS_DEBUG_VERBOSE + printk(" iput dir,"); + #endif + iput (inode); + #ifdef SMBFS_DEBUG_VERBOSE + printk(" iput inode,"); + #endif + + #ifdef SMBFS_DEBUG_VERBOSE + printk(" success (%s)\n",link+8); + #endif + smb_kfree_s(link,SMB_MAX_SYMLINK_SIZE); + return 0; + } + + /* ----- read a symbolic link ----- */ + + static int smb_readlink (struct inode * inode, char * buffer, int buflen) + { + char * link; + int i, length; + char c; + struct smb_dirent *de; + #ifdef SMBFS_DEBUG_VERBOSE + char dout[SMB_MAX_SYMLINK_SIZE]; + #endif + + #ifdef SMBFS_DEBUG_VERBOSE + printk("smb_readlink: "); + #endif + if (!S_ISLNK(inode->i_mode)) { + iput (inode); + return -EINVAL; + } + de = &(SMB_INOP(inode)->finfo); + #ifdef SMBFS_DEBUG_VERBOSE + printk("(name = %s, size = %i),",de->name, de->f_size); + #endif + + if ((length = smb_proc_open(SMB_SERVER(inode),SMB_INOP(inode)->dir,de->name,de->len, de)) != 0) + return length; + + if (inode->i_blocks) { + #ifdef SMBFS_DEBUG_VERBOSE + printk(" %i blocks to read,",inode->i_blocks); + #endif + link = (char *)smb_kmalloc(SMB_MAX_SYMLINK_SIZE, GFP_KERNEL); + length = smb_proc_read (SMB_SERVER(inode), SMB_FINFO(inode), 0, min(SMB_MAX_SYMLINK_SIZE, de->f_size), link, 0); + if (length < 0) { + #ifdef SMBFS_DEBUG_VERBOSE + printk(" error %i on read\n",length); + #endif + smb_kfree_s(link,SMB_MAX_SYMLINK_SIZE); + iput (inode); + return length; + } + } else { + link = de->name; + length = de->len; + } + + + #ifdef SMBFS_DEBUG_VERBOSE + printk(" read ok,"); + #endif + #ifdef SMBFS_DEBUG_VERBOSE + strncpy(dout,link,SMB_MAX_SYMLINK_SIZE-1); + dout[SMB_MAX_SYMLINK_SIZE-1]='\0'; + printk("(size = %i: %s)",length, dout); + #endif + if ((length < SMB_MIN_SYMLINK_SIZE) || + (length > SMB_MAX_SYMLINK_SIZE)) { + #ifdef SMBFS_DEBUG_VERBOSE + printk(" wrong length\n"); + #endif + + smb_kfree_s(link,SMB_MAX_SYMLINK_SIZE); + iput (inode); + return -EIO; + } + if ((((__u32 *)link)[0] != SMB_SYMLINK_MAGIC0) || + (((__u32 *)link)[1] != SMB_SYMLINK_MAGIC1)) { + #ifdef SMBFS_DEBUG_VERBOSE + printk(" no cookie\n"); + #endif + smb_kfree_s(link,SMB_MAX_SYMLINK_SIZE); + iput (inode); + return -EIO; + } + i = 8; + while (i < length&& (c = link[i])) { + i++; + put_user (c, buffer++); + } + put_user ('\0',buffer); + #ifdef SMBFS_DEBUG_VERBOSE + printk(" copy,"); + #endif + UPDATE_ATIME(inode); + #ifdef SMBFS_DEBUG_VERBOSE + printk(" atime,"); + #endif + iput (inode); + #ifdef SMBFS_DEBUG_VERBOSE + printk(" iput,"); + #endif + smb_kfree_s(link,SMB_MAX_SYMLINK_SIZE); + #ifdef SMBFS_DEBUG_VERBOSE + printk(" success\n"); + #endif + return i; + } + + + struct inode_operations smb_symlink_inode_operations = + { + NULL, /* default file operations */ + NULL, /* create */ + NULL, /* lookup */ + NULL, /* link */ + NULL, /* unlink */ + NULL, /* symlink */ + NULL, /* mkdir */ + NULL, /* rmdir */ + NULL, /* mknod */ + NULL, /* rename */ + smb_readlink, /* readlink */ + smb_follow_link, /* follow_link */ + NULL, /* readpage */ + NULL, /* writepage */ + NULL, /* bmap */ + NULL /* truncate */ + }; diff -r -c --new-file client-2.0.33.orig/fs/smbfs/symlink.h client-2.0.33.s.v0.3/fs/smbfs/symlink.h *** client-2.0.33.orig/fs/smbfs/symlink.h Thu Jan 1 01:00:00 1970 --- client-2.0.33.s.v0.3/fs/smbfs/symlink.h Wed Apr 19 21:18:12 2000 *************** *** 0 **** --- 1,22 ---- + #ifndef _FS_SMBFS_SYMLINK_H + #define _FS_SMBFS_SYMLINK_H + /* these magic numbers must appear in the symlink file -- this makes it a bit + more resilient against the magic attributes being set on random files. + + taken from fs/ncpfs/symlinks.c; this should really be in an include + file so interested parties can just share it + -aoe */ + + #define SMB_SYMLINK_MAGIC0 /*le32_to_cpu*/(0x6c6d7973) /* "symlnk->" */ + #define SMB_SYMLINK_MAGIC1 /*le32_to_cpu*/(0x3e2d6b6e) + + /* adopted from fs/ncpfs/ncplib_kernel.h */ + + #define SMB_MIN_SYMLINK_SIZE 8 + #define SMB_MAX_SYMLINK_SIZE 512 + + #define TWENTY_YEARS 631152043 /* wtf? yields 631152000 on my clock */ + #define TEN_YEARS (TWENTY_YEARS/2) + + #endif /* _FS_SMBFS_SYMLINK_H */ +