From db7bee24d23d82cc55c7cbc9a1f82d07066d6fce Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Mon, 4 Oct 2010 22:28:10 +0200 Subject: autofs3: move to drivers/staging Nobody appears to be interested in fixing autofs3 bugs any more and it uses the BKL, which is going away. Move this to staging for retirement. Unless someone complains until 2.6.38, we can remove it for good. The include/linux/auto_fs.h header file is still used by autofs4, so it remains in place. Signed-off-by: Arnd Bergmann Cc: Ian Kent Cc: autofs@linux.kernel.org Cc: "H. Peter Anvin" Acked-by: H. Peter Anvin Signed-off-by: Greg Kroah-Hartman --- fs/Kconfig | 1 - fs/Makefile | 1 - fs/autofs/Kconfig | 21 -- fs/autofs/Makefile | 7 - fs/autofs/autofs_i.h | 165 ------------- fs/autofs/dirhash.c | 250 -------------------- fs/autofs/init.c | 52 ----- fs/autofs/inode.c | 288 ----------------------- fs/autofs/root.c | 643 --------------------------------------------------- fs/autofs/symlink.c | 26 --- fs/autofs/waitq.c | 205 ---------------- 11 files changed, 1659 deletions(-) delete mode 100644 fs/autofs/Kconfig delete mode 100644 fs/autofs/Makefile delete mode 100644 fs/autofs/autofs_i.h delete mode 100644 fs/autofs/dirhash.c delete mode 100644 fs/autofs/init.c delete mode 100644 fs/autofs/inode.c delete mode 100644 fs/autofs/root.c delete mode 100644 fs/autofs/symlink.c delete mode 100644 fs/autofs/waitq.c (limited to 'fs') diff --git a/fs/Kconfig b/fs/Kconfig index 3d185308ec88..30da8ee16a96 100644 --- a/fs/Kconfig +++ b/fs/Kconfig @@ -59,7 +59,6 @@ source "fs/notify/Kconfig" source "fs/quota/Kconfig" -source "fs/autofs/Kconfig" source "fs/autofs4/Kconfig" source "fs/fuse/Kconfig" diff --git a/fs/Makefile b/fs/Makefile index e6ec1d309b1d..e571feddd7b7 100644 --- a/fs/Makefile +++ b/fs/Makefile @@ -104,7 +104,6 @@ obj-$(CONFIG_UBIFS_FS) += ubifs/ obj-$(CONFIG_AFFS_FS) += affs/ obj-$(CONFIG_ROMFS_FS) += romfs/ obj-$(CONFIG_QNX4FS_FS) += qnx4/ -obj-$(CONFIG_AUTOFS_FS) += autofs/ obj-$(CONFIG_AUTOFS4_FS) += autofs4/ obj-$(CONFIG_ADFS_FS) += adfs/ obj-$(CONFIG_FUSE_FS) += fuse/ diff --git a/fs/autofs/Kconfig b/fs/autofs/Kconfig deleted file mode 100644 index 5f3bea90911e..000000000000 --- a/fs/autofs/Kconfig +++ /dev/null @@ -1,21 +0,0 @@ -config AUTOFS_FS - tristate "Kernel automounter support" - help - The automounter is a tool to automatically mount remote file systems - on demand. This implementation is partially kernel-based to reduce - overhead in the already-mounted case; this is unlike the BSD - automounter (amd), which is a pure user space daemon. - - To use the automounter you need the user-space tools from the autofs - package; you can find the location in . - You also want to answer Y to "NFS file system support", below. - - If you want to use the newer version of the automounter with more - features, say N here and say Y to "Kernel automounter v4 support", - below. - - To compile this support as a module, choose M here: the module will be - called autofs. - - If you are not a part of a fairly large, distributed network, you - probably do not need an automounter, and can say N here. diff --git a/fs/autofs/Makefile b/fs/autofs/Makefile deleted file mode 100644 index 453a60f46d05..000000000000 --- a/fs/autofs/Makefile +++ /dev/null @@ -1,7 +0,0 @@ -# -# Makefile for the linux autofs-filesystem routines. -# - -obj-$(CONFIG_AUTOFS_FS) += autofs.o - -autofs-objs := dirhash.o init.o inode.o root.o symlink.o waitq.o diff --git a/fs/autofs/autofs_i.h b/fs/autofs/autofs_i.h deleted file mode 100644 index 901a3e67ec45..000000000000 --- a/fs/autofs/autofs_i.h +++ /dev/null @@ -1,165 +0,0 @@ -/* -*- linux-c -*- ------------------------------------------------------- * - * - * linux/fs/autofs/autofs_i.h - * - * Copyright 1997-1998 Transmeta Corporation - All Rights Reserved - * - * This file is part of the Linux kernel and is made available under - * the terms of the GNU General Public License, version 2, or at your - * option, any later version, incorporated herein by reference. - * - * ----------------------------------------------------------------------- */ - -/* Internal header file for autofs */ - -#include - -/* This is the range of ioctl() numbers we claim as ours */ -#define AUTOFS_IOC_FIRST AUTOFS_IOC_READY -#define AUTOFS_IOC_COUNT 32 - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -#ifdef DEBUG -#define DPRINTK(D) (printk D) -#else -#define DPRINTK(D) ((void)0) -#endif - -/* - * If the daemon returns a negative response (AUTOFS_IOC_FAIL) then the - * kernel will keep the negative response cached for up to the time given - * here, although the time can be shorter if the kernel throws the dcache - * entry away. This probably should be settable from user space. - */ -#define AUTOFS_NEGATIVE_TIMEOUT (60*HZ) /* 1 minute */ - -/* Structures associated with the root directory hash table */ - -#define AUTOFS_HASH_SIZE 67 - -struct autofs_dir_ent { - int hash; - char *name; - int len; - ino_t ino; - struct dentry *dentry; - /* Linked list of entries */ - struct autofs_dir_ent *next; - struct autofs_dir_ent **back; - /* The following entries are for the expiry system */ - unsigned long last_usage; - struct list_head exp; -}; - -struct autofs_dirhash { - struct autofs_dir_ent *h[AUTOFS_HASH_SIZE]; - struct list_head expiry_head; -}; - -struct autofs_wait_queue { - wait_queue_head_t queue; - struct autofs_wait_queue *next; - autofs_wqt_t wait_queue_token; - /* We use the following to see what we are waiting for */ - int hash; - int len; - char *name; - /* This is for status reporting upon return */ - int status; - int wait_ctr; -}; - -struct autofs_symlink { - char *data; - int len; - time_t mtime; -}; - -#define AUTOFS_MAX_SYMLINKS 256 - -#define AUTOFS_ROOT_INO 1 -#define AUTOFS_FIRST_SYMLINK 2 -#define AUTOFS_FIRST_DIR_INO (AUTOFS_FIRST_SYMLINK+AUTOFS_MAX_SYMLINKS) - -#define AUTOFS_SYMLINK_BITMAP_LEN \ - ((AUTOFS_MAX_SYMLINKS+((sizeof(long)*1)-1))/(sizeof(long)*8)) - -#define AUTOFS_SBI_MAGIC 0x6d4a556d - -struct autofs_sb_info { - u32 magic; - struct file *pipe; - struct pid *oz_pgrp; - int catatonic; - struct super_block *sb; - unsigned long exp_timeout; - ino_t next_dir_ino; - struct autofs_wait_queue *queues; /* Wait queue pointer */ - struct autofs_dirhash dirhash; /* Root directory hash */ - struct autofs_symlink symlink[AUTOFS_MAX_SYMLINKS]; - unsigned long symlink_bitmap[AUTOFS_SYMLINK_BITMAP_LEN]; -}; - -static inline struct autofs_sb_info *autofs_sbi(struct super_block *sb) -{ - return (struct autofs_sb_info *)(sb->s_fs_info); -} - -/* autofs_oz_mode(): do we see the man behind the curtain? (The - processes which do manipulations for us in user space sees the raw - filesystem without "magic".) */ - -static inline int autofs_oz_mode(struct autofs_sb_info *sbi) { - return sbi->catatonic || task_pgrp(current) == sbi->oz_pgrp; -} - -/* Hash operations */ - -void autofs_initialize_hash(struct autofs_dirhash *); -struct autofs_dir_ent *autofs_hash_lookup(const struct autofs_dirhash *,struct qstr *); -void autofs_hash_insert(struct autofs_dirhash *,struct autofs_dir_ent *); -void autofs_hash_delete(struct autofs_dir_ent *); -struct autofs_dir_ent *autofs_hash_enum(const struct autofs_dirhash *,off_t *,struct autofs_dir_ent *); -void autofs_hash_dputall(struct autofs_dirhash *); -void autofs_hash_nuke(struct autofs_sb_info *); - -/* Expiration-handling functions */ - -void autofs_update_usage(struct autofs_dirhash *,struct autofs_dir_ent *); -struct autofs_dir_ent *autofs_expire(struct super_block *,struct autofs_sb_info *, struct vfsmount *mnt); - -/* Operations structures */ - -extern const struct inode_operations autofs_root_inode_operations; -extern const struct inode_operations autofs_symlink_inode_operations; -extern const struct file_operations autofs_root_operations; - -/* Initializing function */ - -int autofs_fill_super(struct super_block *, void *, int); -void autofs_kill_sb(struct super_block *sb); -struct inode *autofs_iget(struct super_block *, unsigned long); - -/* Queue management functions */ - -int autofs_wait(struct autofs_sb_info *,struct qstr *); -int autofs_wait_release(struct autofs_sb_info *,autofs_wqt_t,int); -void autofs_catatonic_mode(struct autofs_sb_info *); - -#ifdef DEBUG -void autofs_say(const char *name, int len); -#else -#define autofs_say(n,l) ((void)0) -#endif diff --git a/fs/autofs/dirhash.c b/fs/autofs/dirhash.c deleted file mode 100644 index e947915109e5..000000000000 --- a/fs/autofs/dirhash.c +++ /dev/null @@ -1,250 +0,0 @@ -/* -*- linux-c -*- --------------------------------------------------------- * - * - * linux/fs/autofs/dirhash.c - * - * Copyright 1997-1998 Transmeta Corporation -- All Rights Reserved - * - * This file is part of the Linux kernel and is made available under - * the terms of the GNU General Public License, version 2, or at your - * option, any later version, incorporated herein by reference. - * - * ------------------------------------------------------------------------- */ - -#include "autofs_i.h" - -/* Functions for maintenance of expiry queue */ - -static void autofs_init_usage(struct autofs_dirhash *dh, - struct autofs_dir_ent *ent) -{ - list_add_tail(&ent->exp, &dh->expiry_head); - ent->last_usage = jiffies; -} - -static void autofs_delete_usage(struct autofs_dir_ent *ent) -{ - list_del(&ent->exp); -} - -void autofs_update_usage(struct autofs_dirhash *dh, - struct autofs_dir_ent *ent) -{ - autofs_delete_usage(ent); /* Unlink from current position */ - autofs_init_usage(dh,ent); /* Relink at queue tail */ -} - -struct autofs_dir_ent *autofs_expire(struct super_block *sb, - struct autofs_sb_info *sbi, - struct vfsmount *mnt) -{ - struct autofs_dirhash *dh = &sbi->dirhash; - struct autofs_dir_ent *ent; - unsigned long timeout = sbi->exp_timeout; - - while (1) { - struct path path; - int umount_ok; - - if ( list_empty(&dh->expiry_head) || sbi->catatonic ) - return NULL; /* No entries */ - /* We keep the list sorted by last_usage and want old stuff */ - ent = list_entry(dh->expiry_head.next, struct autofs_dir_ent, exp); - if (jiffies - ent->last_usage < timeout) - break; - /* Move to end of list in case expiry isn't desirable */ - autofs_update_usage(dh, ent); - - /* Check to see that entry is expirable */ - if ( ent->ino < AUTOFS_FIRST_DIR_INO ) - return ent; /* Symlinks are always expirable */ - - /* Get the dentry for the autofs subdirectory */ - path.dentry = ent->dentry; - - if (!path.dentry) { - /* Should only happen in catatonic mode */ - printk("autofs: dentry == NULL but inode range is directory, entry %s\n", ent->name); - autofs_delete_usage(ent); - continue; - } - - if (!path.dentry->d_inode) { - dput(path.dentry); - printk("autofs: negative dentry on expiry queue: %s\n", - ent->name); - autofs_delete_usage(ent); - continue; - } - - /* Make sure entry is mounted and unused; note that dentry will - point to the mounted-on-top root. */ - if (!S_ISDIR(path.dentry->d_inode->i_mode) || - !d_mountpoint(path.dentry)) { - DPRINTK(("autofs: not expirable (not a mounted directory): %s\n", ent->name)); - continue; - } - path.mnt = mnt; - path_get(&path); - if (!follow_down(&path)) { - path_put(&path); - DPRINTK(("autofs: not expirable (not a mounted directory): %s\n", ent->name)); - continue; - } - while (d_mountpoint(path.dentry) && follow_down(&path)) - ; - umount_ok = may_umount(path.mnt); - path_put(&path); - - if (umount_ok) { - DPRINTK(("autofs: signaling expire on %s\n", ent->name)); - return ent; /* Expirable! */ - } - DPRINTK(("autofs: didn't expire due to may_umount: %s\n", ent->name)); - } - return NULL; /* No expirable entries */ -} - -void autofs_initialize_hash(struct autofs_dirhash *dh) { - memset(&dh->h, 0, AUTOFS_HASH_SIZE*sizeof(struct autofs_dir_ent *)); - INIT_LIST_HEAD(&dh->expiry_head); -} - -struct autofs_dir_ent *autofs_hash_lookup(const struct autofs_dirhash *dh, struct qstr *name) -{ - struct autofs_dir_ent *dhn; - - DPRINTK(("autofs_hash_lookup: hash = 0x%08x, name = ", name->hash)); - autofs_say(name->name,name->len); - - for ( dhn = dh->h[(unsigned) name->hash % AUTOFS_HASH_SIZE] ; dhn ; dhn = dhn->next ) { - if ( name->hash == dhn->hash && - name->len == dhn->len && - !memcmp(name->name, dhn->name, name->len) ) - break; - } - - return dhn; -} - -void autofs_hash_insert(struct autofs_dirhash *dh, struct autofs_dir_ent *ent) -{ - struct autofs_dir_ent **dhnp; - - DPRINTK(("autofs_hash_insert: hash = 0x%08x, name = ", ent->hash)); - autofs_say(ent->name,ent->len); - - autofs_init_usage(dh,ent); - if (ent->dentry) - dget(ent->dentry); - - dhnp = &dh->h[(unsigned) ent->hash % AUTOFS_HASH_SIZE]; - ent->next = *dhnp; - ent->back = dhnp; - *dhnp = ent; - if ( ent->next ) - ent->next->back = &(ent->next); -} - -void autofs_hash_delete(struct autofs_dir_ent *ent) -{ - *(ent->back) = ent->next; - if ( ent->next ) - ent->next->back = ent->back; - - autofs_delete_usage(ent); - - if ( ent->dentry ) - dput(ent->dentry); - kfree(ent->name); - kfree(ent); -} - -/* - * Used by readdir(). We must validate "ptr", so we can't simply make it - * a pointer. Values below 0xffff are reserved; calling with any value - * <= 0x10000 will return the first entry found. - * - * "last" can be NULL or the value returned by the last search *if* we - * want the next sequential entry. - */ -struct autofs_dir_ent *autofs_hash_enum(const struct autofs_dirhash *dh, - off_t *ptr, struct autofs_dir_ent *last) -{ - int bucket, ecount, i; - struct autofs_dir_ent *ent; - - bucket = (*ptr >> 16) - 1; - ecount = *ptr & 0xffff; - - if ( bucket < 0 ) { - bucket = ecount = 0; - } - - DPRINTK(("autofs_hash_enum: bucket %d, entry %d\n", bucket, ecount)); - - ent = last ? last->next : NULL; - - if ( ent ) { - ecount++; - } else { - while ( bucket < AUTOFS_HASH_SIZE ) { - ent = dh->h[bucket]; - for ( i = ecount ; ent && i ; i-- ) - ent = ent->next; - - if (ent) { - ecount++; /* Point to *next* entry */ - break; - } - - bucket++; ecount = 0; - } - } - -#ifdef DEBUG - if ( !ent ) - printk("autofs_hash_enum: nothing found\n"); - else { - printk("autofs_hash_enum: found hash %08x, name", ent->hash); - autofs_say(ent->name,ent->len); - } -#endif - - *ptr = ((bucket+1) << 16) + ecount; - return ent; -} - -/* Iterate over all the ents, and remove all dentry pointers. Used on - entering catatonic mode, in order to make the filesystem unmountable. */ -void autofs_hash_dputall(struct autofs_dirhash *dh) -{ - int i; - struct autofs_dir_ent *ent; - - for ( i = 0 ; i < AUTOFS_HASH_SIZE ; i++ ) { - for ( ent = dh->h[i] ; ent ; ent = ent->next ) { - if ( ent->dentry ) { - dput(ent->dentry); - ent->dentry = NULL; - } - } - } -} - -/* Delete everything. This is used on filesystem destruction, so we - make no attempt to keep the pointers valid */ -void autofs_hash_nuke(struct autofs_sb_info *sbi) -{ - int i; - struct autofs_dir_ent *ent, *nent; - - for ( i = 0 ; i < AUTOFS_HASH_SIZE ; i++ ) { - for ( ent = sbi->dirhash.h[i] ; ent ; ent = nent ) { - nent = ent->next; - if ( ent->dentry ) - dput(ent->dentry); - kfree(ent->name); - kfree(ent); - } - } -} diff --git a/fs/autofs/init.c b/fs/autofs/init.c deleted file mode 100644 index cea5219b4f37..000000000000 --- a/fs/autofs/init.c +++ /dev/null @@ -1,52 +0,0 @@ -/* -*- linux-c -*- --------------------------------------------------------- * - * - * linux/fs/autofs/init.c - * - * Copyright 1997-1998 Transmeta Corporation -- All Rights Reserved - * - * This file is part of the Linux kernel and is made available under - * the terms of the GNU General Public License, version 2, or at your - * option, any later version, incorporated herein by reference. - * - * ------------------------------------------------------------------------- */ - -#include -#include -#include "autofs_i.h" - -static int autofs_get_sb(struct file_system_type *fs_type, - int flags, const char *dev_name, void *data, struct vfsmount *mnt) -{ - return get_sb_nodev(fs_type, flags, data, autofs_fill_super, mnt); -} - -static struct file_system_type autofs_fs_type = { - .owner = THIS_MODULE, - .name = "autofs", - .get_sb = autofs_get_sb, - .kill_sb = autofs_kill_sb, -}; - -static int __init init_autofs_fs(void) -{ - return register_filesystem(&autofs_fs_type); -} - -static void __exit exit_autofs_fs(void) -{ - unregister_filesystem(&autofs_fs_type); -} - -module_init(init_autofs_fs); -module_exit(exit_autofs_fs); - -#ifdef DEBUG -void autofs_say(const char *name, int len) -{ - printk("(%d: ", len); - while ( len-- ) - printk("%c", *name++); - printk(")\n"); -} -#endif -MODULE_LICENSE("GPL"); diff --git a/fs/autofs/inode.c b/fs/autofs/inode.c deleted file mode 100644 index e1734f2d6e26..000000000000 --- a/fs/autofs/inode.c +++ /dev/null @@ -1,288 +0,0 @@ -/* -*- linux-c -*- --------------------------------------------------------- * - * - * linux/fs/autofs/inode.c - * - * Copyright 1997-1998 Transmeta Corporation -- All Rights Reserved - * - * This file is part of the Linux kernel and is made available under - * the terms of the GNU General Public License, version 2, or at your - * option, any later version, incorporated herein by reference. - * - * ------------------------------------------------------------------------- */ - -#include -#include -#include -#include -#include -#include -#include -#include "autofs_i.h" -#include - -void autofs_kill_sb(struct super_block *sb) -{ - struct autofs_sb_info *sbi = autofs_sbi(sb); - unsigned int n; - - /* - * In the event of a failure in get_sb_nodev the superblock - * info is not present so nothing else has been setup, so - * just call kill_anon_super when we are called from - * deactivate_super. - */ - if (!sbi) - goto out_kill_sb; - - if (!sbi->catatonic) - autofs_catatonic_mode(sbi); /* Free wait queues, close pipe */ - - put_pid(sbi->oz_pgrp); - - autofs_hash_nuke(sbi); - for (n = 0; n < AUTOFS_MAX_SYMLINKS; n++) { - if (test_bit(n, sbi->symlink_bitmap)) - kfree(sbi->symlink[n].data); - } - - kfree(sb->s_fs_info); - -out_kill_sb: - DPRINTK(("autofs: shutting down\n")); - kill_anon_super(sb); -} - -static const struct super_operations autofs_sops = { - .statfs = simple_statfs, - .show_options = generic_show_options, -}; - -enum {Opt_err, Opt_fd, Opt_uid, Opt_gid, Opt_pgrp, Opt_minproto, Opt_maxproto}; - -static const match_table_t autofs_tokens = { - {Opt_fd, "fd=%u"}, - {Opt_uid, "uid=%u"}, - {Opt_gid, "gid=%u"}, - {Opt_pgrp, "pgrp=%u"}, - {Opt_minproto, "minproto=%u"}, - {Opt_maxproto, "maxproto=%u"}, - {Opt_err, NULL} -}; - -static int parse_options(char *options, int *pipefd, uid_t *uid, gid_t *gid, - pid_t *pgrp, int *minproto, int *maxproto) -{ - char *p; - substring_t args[MAX_OPT_ARGS]; - int option; - - *uid = current_uid(); - *gid = current_gid(); - *pgrp = task_pgrp_nr(current); - - *minproto = *maxproto = AUTOFS_PROTO_VERSION; - - *pipefd = -1; - - if (!options) - return 1; - - while ((p = strsep(&options, ",")) != NULL) { - int token; - if (!*p) - continue; - - token = match_token(p, autofs_tokens, args); - switch (token) { - case Opt_fd: - if (match_int(&args[0], &option)) - return 1; - *pipefd = option; - break; - case Opt_uid: - if (match_int(&args[0], &option)) - return 1; - *uid = option; - break; - case Opt_gid: - if (match_int(&args[0], &option)) - return 1; - *gid = option; - break; - case Opt_pgrp: - if (match_int(&args[0], &option)) - return 1; - *pgrp = option; - break; - case Opt_minproto: - if (match_int(&args[0], &option)) - return 1; - *minproto = option; - break; - case Opt_maxproto: - if (match_int(&args[0], &option)) - return 1; - *maxproto = option; - break; - default: - return 1; - } - } - return (*pipefd < 0); -} - -int autofs_fill_super(struct super_block *s, void *data, int silent) -{ - struct inode * root_inode; - struct dentry * root; - struct file * pipe; - int pipefd; - struct autofs_sb_info *sbi; - int minproto, maxproto; - pid_t pgid; - - save_mount_options(s, data); - - sbi = kzalloc(sizeof(*sbi), GFP_KERNEL); - if (!sbi) - goto fail_unlock; - DPRINTK(("autofs: starting up, sbi = %p\n",sbi)); - - s->s_fs_info = sbi; - sbi->magic = AUTOFS_SBI_MAGIC; - sbi->pipe = NULL; - sbi->catatonic = 1; - sbi->exp_timeout = 0; - autofs_initialize_hash(&sbi->dirhash); - sbi->queues = NULL; - memset(sbi->symlink_bitmap, 0, sizeof(long)*AUTOFS_SYMLINK_BITMAP_LEN); - sbi->next_dir_ino = AUTOFS_FIRST_DIR_INO; - s->s_blocksize = 1024; - s->s_blocksize_bits = 10; - s->s_magic = AUTOFS_SUPER_MAGIC; - s->s_op = &autofs_sops; - s->s_time_gran = 1; - sbi->sb = s; - - root_inode = autofs_iget(s, AUTOFS_ROOT_INO); - if (IS_ERR(root_inode)) - goto fail_free; - root = d_alloc_root(root_inode); - pipe = NULL; - - if (!root) - goto fail_iput; - - /* Can this call block? - WTF cares? s is locked. */ - if (parse_options(data, &pipefd, &root_inode->i_uid, - &root_inode->i_gid, &pgid, &minproto, - &maxproto)) { - printk("autofs: called with bogus options\n"); - goto fail_dput; - } - - /* Couldn't this be tested earlier? */ - if (minproto > AUTOFS_PROTO_VERSION || - maxproto < AUTOFS_PROTO_VERSION) { - printk("autofs: kernel does not match daemon version\n"); - goto fail_dput; - } - - DPRINTK(("autofs: pipe fd = %d, pgrp = %u\n", pipefd, pgid)); - sbi->oz_pgrp = find_get_pid(pgid); - - if (!sbi->oz_pgrp) { - printk("autofs: could not find process group %d\n", pgid); - goto fail_dput; - } - - pipe = fget(pipefd); - - if (!pipe) { - printk("autofs: could not open pipe file descriptor\n"); - goto fail_put_pid; - } - - if (!pipe->f_op || !pipe->f_op->write) - goto fail_fput; - sbi->pipe = pipe; - sbi->catatonic = 0; - - /* - * Success! Install the root dentry now to indicate completion. - */ - s->s_root = root; - return 0; - -fail_fput: - printk("autofs: pipe file descriptor does not contain proper ops\n"); - fput(pipe); -fail_put_pid: - put_pid(sbi->oz_pgrp); -fail_dput: - dput(root); - goto fail_free; -fail_iput: - printk("autofs: get root dentry failed\n"); - iput(root_inode); -fail_free: - kfree(sbi); - s->s_fs_info = NULL; -fail_unlock: - return -EINVAL; -} - -struct inode *autofs_iget(struct super_block *sb, unsigned long ino) -{ - unsigned int n; - struct autofs_sb_info *sbi = autofs_sbi(sb); - struct inode *inode; - - inode = iget_locked(sb, ino); - if (!inode) - return ERR_PTR(-ENOMEM); - if (!(inode->i_state & I_NEW)) - return inode; - - /* Initialize to the default case (stub directory) */ - - inode->i_op = &simple_dir_inode_operations; - inode->i_fop = &simple_dir_operations; - inode->i_mode = S_IFDIR | S_IRUGO | S_IXUGO; - inode->i_nlink = 2; - inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME; - - if (ino == AUTOFS_ROOT_INO) { - inode->i_mode = S_IFDIR | S_IRUGO | S_IXUGO | S_IWUSR; - inode->i_op = &autofs_root_inode_operations; - inode->i_fop = &autofs_root_operations; - goto done; - } - - inode->i_uid = inode->i_sb->s_root->d_inode->i_uid; - inode->i_gid = inode->i_sb->s_root->d_inode->i_gid; - - if (ino >= AUTOFS_FIRST_SYMLINK && ino < AUTOFS_FIRST_DIR_INO) { - /* Symlink inode - should be in symlink list */ - struct autofs_symlink *sl; - - n = ino - AUTOFS_FIRST_SYMLINK; - if (n >= AUTOFS_MAX_SYMLINKS || !test_bit(n,sbi->symlink_bitmap)) { - printk("autofs: Looking for bad symlink inode %u\n", (unsigned int) ino); - goto done; - } - - inode->i_op = &autofs_symlink_inode_operations; - sl = &sbi->symlink[n]; - inode->i_private = sl; - inode->i_mode = S_IFLNK | S_IRWXUGO; - inode->i_mtime.tv_sec = inode->i_ctime.tv_sec = sl->mtime; - inode->i_mtime.tv_nsec = inode->i_ctime.tv_nsec = 0; - inode->i_size = sl->len; - inode->i_nlink = 1; - } - -done: - unlock_new_inode(inode); - return inode; -} diff --git a/fs/autofs/root.c b/fs/autofs/root.c deleted file mode 100644 index 11b1ea786d00..000000000000 --- a/fs/autofs/root.c +++ /dev/null @@ -1,643 +0,0 @@ -/* -*- linux-c -*- --------------------------------------------------------- * - * - * linux/fs/autofs/root.c - * - * Copyright 1997-1998 Transmeta Corporation -- All Rights Reserved - * - * This file is part of the Linux kernel and is made available under - * the terms of the GNU General Public License, version 2, or at your - * option, any later version, incorporated herein by reference. - * - * ------------------------------------------------------------------------- */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include "autofs_i.h" - -static int autofs_root_readdir(struct file *,void *,filldir_t); -static struct dentry *autofs_root_lookup(struct inode *,struct dentry *, struct nameidata *); -static int autofs_root_symlink(struct inode *,struct dentry *,const char *); -static int autofs_root_unlink(struct inode *,struct dentry *); -static int autofs_root_rmdir(struct inode *,struct dentry *); -static int autofs_root_mkdir(struct inode *,struct dentry *,int); -static long autofs_root_ioctl(struct file *,unsigned int,unsigned long); -static long autofs_root_compat_ioctl(struct file *,unsigned int,unsigned long); - -const struct file_operations autofs_root_operations = { - .llseek = generic_file_llseek, - .read = generic_read_dir, - .readdir = autofs_root_readdir, - .unlocked_ioctl = autofs_root_ioctl, -#ifdef CONFIG_COMPAT - .compat_ioctl = autofs_root_compat_ioctl, -#endif -}; - -const struct inode_operations autofs_root_inode_operations = { - .lookup = autofs_root_lookup, - .unlink = autofs_root_unlink, - .symlink = autofs_root_symlink, - .mkdir = autofs_root_mkdir, - .rmdir = autofs_root_rmdir, -}; - -static int autofs_root_readdir(struct file *filp, void *dirent, filldir_t filldir) -{ - struct autofs_dir_ent *ent = NULL; - struct autofs_dirhash *dirhash; - struct autofs_sb_info *sbi; - struct inode * inode = filp->f_path.dentry->d_inode; - off_t onr, nr; - - lock_kernel(); - - sbi = autofs_sbi(inode->i_sb); - dirhash = &sbi->dirhash; - nr = filp->f_pos; - - switch(nr) - { - case 0: - if (filldir(dirent, ".", 1, nr, inode->i_ino, DT_DIR) < 0) - goto out; - filp->f_pos = ++nr; - /* fall through */ - case 1: - if (filldir(dirent, "..", 2, nr, inode->i_ino, DT_DIR) < 0) - goto out; - filp->f_pos = ++nr; - /* fall through */ - default: - while (onr = nr, ent = autofs_hash_enum(dirhash,&nr,ent)) { - if (!ent->dentry || d_mountpoint(ent->dentry)) { - if (filldir(dirent,ent->name,ent->len,onr,ent->ino,DT_UNKNOWN) < 0) - goto out; - filp->f_pos = nr; - } - } - break; - } - -out: - unlock_kernel(); - return 0; -} - -static int try_to_fill_dentry(struct dentry *dentry, struct super_block *sb, struct autofs_sb_info *sbi) -{ - struct inode * inode; - struct autofs_dir_ent *ent; - int status = 0; - - if (!(ent = autofs_hash_lookup(&sbi->dirhash, &dentry->d_name))) { - do { - if (status && dentry->d_inode) { - if (status != -ENOENT) - printk("autofs warning: lookup failure on positive dentry, status = %d, name = %s\n", status, dentry->d_name.name); - return 0; /* Try to get the kernel to invalidate this dentry */ - } - - /* Turn this into a real negative dentry? */ - if (status == -ENOENT) { - dentry->d_time = jiffies + AUTOFS_NEGATIVE_TIMEOUT; - dentry->d_flags &= ~DCACHE_AUTOFS_PENDING; - return 1; - } else if (status) { - /* Return a negative dentry, but leave it "pending" */ - return 1; - } - status = autofs_wait(sbi, &dentry->d_name); - } while (!(ent = autofs_hash_lookup(&sbi->dirhash, &dentry->d_name))); - } - - /* Abuse this field as a pointer to the directory entry, used to - find the expire list pointers */ - dentry->d_time = (unsigned long) ent; - - if (!dentry->d_inode) { - inode = autofs_iget(sb, ent->ino); - if (IS_ERR(inode)) { - /* Failed, but leave pending for next time */ - return 1; - } - dentry->d_inode = inode; - } - - /* If this is a directory that isn't a mount point, bitch at the - daemon and fix it in user space */ - if (S_ISDIR(dentry->d_inode->i_mode) && !d_mountpoint(dentry)) { - return !autofs_wait(sbi, &dentry->d_name); - } - - /* We don't update the usages for the autofs daemon itself, this - is necessary for recursive autofs mounts */ - if (!autofs_oz_mode(sbi)) { - autofs_update_usage(&sbi->dirhash,ent); - } - - dentry->d_flags &= ~DCACHE_AUTOFS_PENDING; - return 1; -} - - -/* - * Revalidate is called on every cache lookup. Some of those - * cache lookups may actually happen while the dentry is not - * yet completely filled in, and revalidate has to delay such - * lookups.. - */ -static int autofs_revalidate(struct dentry * dentry, struct nameidata *nd) -{ - struct inode * dir; - struct autofs_sb_info *sbi; - struct autofs_dir_ent *ent; - int res; - - lock_kernel(); - dir = dentry->d_parent->d_inode; - sbi = autofs_sbi(dir->i_sb); - - /* Pending dentry */ - if (dentry->d_flags & DCACHE_AUTOFS_PENDING) { - if (autofs_oz_mode(sbi)) - res = 1; - else - res = try_to_fill_dentry(dentry, dir->i_sb, sbi); - unlock_kernel(); - return res; - } - - /* Negative dentry.. invalidate if "old" */ - if (!dentry->d_inode) { - unlock_kernel(); - return (dentry->d_time - jiffies <= AUTOFS_NEGATIVE_TIMEOUT); - } - - /* Check for a non-mountpoint directory */ - if (S_ISDIR(dentry->d_inode->i_mode) && !d_mountpoint(dentry)) { - if (autofs_oz_mode(sbi)) - res = 1; - else - res = try_to_fill_dentry(dentry, dir->i_sb, sbi); - unlock_kernel(); - return res; - } - - /* Update the usage list */ - if (!autofs_oz_mode(sbi)) { - ent = (struct autofs_dir_ent *) dentry->d_time; - if (ent) - autofs_update_usage(&sbi->dirhash,ent); - } - unlock_kernel(); - return 1; -} - -static const struct dentry_operations autofs_dentry_operations = { - .d_revalidate = autofs_revalidate, -}; - -static struct dentry *autofs_root_lookup(struct inode *dir, struct dentry *dentry, struct nameidata *nd) -{ - struct autofs_sb_info *sbi; - int oz_mode; - - DPRINTK(("autofs_root_lookup: name = ")); - lock_kernel(); - autofs_say(dentry->d_name.name,dentry->d_name.len); - - if (dentry->d_name.len > NAME_MAX) { - unlock_kernel(); - return ERR_PTR(-ENAMETOOLONG);/* File name too long to exist */ - } - - sbi = autofs_sbi(dir->i_sb); - - oz_mode = autofs_oz_mode(sbi); - DPRINTK(("autofs_lookup: pid = %u, pgrp = %u, catatonic = %d, " - "oz_mode = %d\n", task_pid_nr(current), - task_pgrp_nr(current), sbi->catatonic, - oz_mode)); - - /* - * Mark the dentry incomplete, but add it. This is needed so - * that the VFS layer knows about the dentry, and we can count - * on catching any lookups through the revalidate. - * - * Let all the hard work be done by the revalidate function that - * needs to be able to do this anyway.. - * - * We need to do this before we release the directory semaphore. - */ - dentry->d_op = &autofs_dentry_operations; - dentry->d_flags |= DCACHE_AUTOFS_PENDING; - d_add(dentry, NULL); - - mutex_unlock(&dir->i_mutex); - autofs_revalidate(dentry, nd); - mutex_lock(&dir->i_mutex); - - /* - * If we are still pending, check if we had to handle - * a signal. If so we can force a restart.. - */ - if (dentry->d_flags & DCACHE_AUTOFS_PENDING) { - /* See if we were interrupted */ - if (signal_pending(current)) { - sigset_t *sigset = ¤t->pending.signal; - if (sigismember (sigset, SIGKILL) || - sigismember (sigset, SIGQUIT) || - sigismember (sigset, SIGINT)) { - unlock_kernel(); - return ERR_PTR(-ERESTARTNOINTR); - } - } - } - unlock_kernel(); - - /* - * If this dentry is unhashed, then we shouldn't honour this - * lookup even if the dentry is positive. Returning ENOENT here - * doesn't do the right thing for all system calls, but it should - * be OK for the operations we permit from an autofs. - */ - if (dentry->d_inode && d_unhashed(dentry)) - return ERR_PTR(-ENOENT); - - return NULL; -} - -static int autofs_root_symlink(struct inode *dir, struct dentry *dentry, const char *symname) -{ - struct autofs_sb_info *sbi = autofs_sbi(dir->i_sb); - struct autofs_dirhash *dh = &sbi->dirhash; - struct autofs_dir_ent *ent; - unsigned int n; - int slsize; - struct autofs_symlink *sl; - struct inode *inode; - - DPRINTK(("autofs_root_symlink: %s <- ", symname)); - autofs_say(dentry->d_name.name,dentry->d_name.len); - - lock_kernel(); - if (!autofs_oz_mode(sbi)) { - unlock_kernel(); - return -EACCES; - } - - if (autofs_hash_lookup(dh, &dentry->d_name)) { - unlock_kernel(); - return -EEXIST; - } - - n = find_first_zero_bit(sbi->symlink_bitmap,AUTOFS_MAX_SYMLINKS); - if (n >= AUTOFS_MAX_SYMLINKS) { - unlock_kernel(); - return -ENOSPC; - } - - set_bit(n,sbi->symlink_bitmap); - sl = &sbi->symlink[n]; - sl->len = strlen(symname); - sl->data = kmalloc(slsize = sl->len+1, GFP_KERNEL); - if (!sl->data) { - clear_bit(n,sbi->symlink_bitmap); - unlock_kernel(); - return -ENOSPC; - } - - ent = kmalloc(sizeof(struct autofs_dir_ent), GFP_KERNEL); - if (!ent) { - kfree(sl->data); - clear_bit(n,sbi->symlink_bitmap); - unlock_kernel(); - return -ENOSPC; - } - - ent->name = kmalloc(dentry->d_name.len+1, GFP_KERNEL); - if (!ent->name) { - kfree(sl->data); - kfree(ent); - clear_bit(n,sbi->symlink_bitmap); - unlock_kernel(); - return -ENOSPC; - } - - memcpy(sl->data,symname,slsize); - sl->mtime = get_seconds(); - - ent->ino = AUTOFS_FIRST_SYMLINK + n; - ent->hash = dentry->d_name.hash; - memcpy(ent->name, dentry->d_name.name, 1+(ent->len = dentry->d_name.len)); - ent->dentry = NULL; /* We don't keep the dentry for symlinks */ - - autofs_hash_insert(dh,ent); - - inode = autofs_iget(dir->i_sb, ent->ino); - if (IS_ERR(inode)) - return PTR_ERR(inode); - - d_instantiate(dentry, inode); - unlock_kernel(); - return 0; -} - -/* - * NOTE! - * - * Normal filesystems would do a "d_delete()" to tell the VFS dcache - * that the file no longer exists. However, doing that means that the - * VFS layer can turn the dentry into a negative dentry, which we - * obviously do not want (we're dropping the entry not because it - * doesn't exist, but because it has timed out). - * - * Also see autofs_root_rmdir().. - */ -static int autofs_root_unlink(struct inode *dir, struct dentry *dentry) -{ - struct autofs_sb_info *sbi = autofs_sbi(dir->i_sb); - struct autofs_dirhash *dh = &sbi->dirhash; - struct autofs_dir_ent *ent; - unsigned int n; - - /* This allows root to remove symlinks */ - lock_kernel(); - if (!autofs_oz_mode(sbi) && !capable(CAP_SYS_ADMIN)) { - unlock_kernel(); - return -EACCES; - } - - ent = autofs_hash_lookup(dh, &dentry->d_name); - if (!ent) { - unlock_kernel(); - return -ENOENT; - } - - n = ent->ino - AUTOFS_FIRST_SYMLINK; - if (n >= AUTOFS_MAX_SYMLINKS) { - unlock_kernel(); - return -EISDIR; /* It's a directory, dummy */ - } - if (!test_bit(n,sbi->symlink_bitmap)) { - unlock_kernel(); - return -EINVAL; /* Nonexistent symlink? Shouldn't happen */ - } - - dentry->d_time = (unsigned long)(struct autofs_dirhash *)NULL; - autofs_hash_delete(ent); - clear_bit(n,sbi->symlink_bitmap); - kfree(sbi->symlink[n].data); - d_drop(dentry); - - unlock_kernel(); - return 0; -} - -static int autofs_root_rmdir(struct inode *dir, struct dentry *dentry) -{ - struct autofs_sb_info *sbi = autofs_sbi(dir->i_sb); - struct autofs_dirhash *dh = &sbi->dirhash; - struct autofs_dir_ent *ent; - - lock_kernel(); - if (!autofs_oz_mode(sbi)) { - unlock_kernel(); - return -EACCES; - } - - ent = autofs_hash_lookup(dh, &dentry->d_name); - if (!ent) { - unlock_kernel(); - return -ENOENT; - } - - if ((unsigned int)ent->ino < AUTOFS_FIRST_DIR_INO) { - unlock_kernel(); - return -ENOTDIR; /* Not a directory */ - } - - if (ent->dentry != dentry) { - printk("autofs_rmdir: odentry != dentry for entry %s\n", dentry->d_name.name); - } - - dentry->d_time = (unsigned long)(struct autofs_dir_ent *)NULL; - autofs_hash_delete(ent); - drop_nlink(dir); - d_drop(dentry); - unlock_kernel(); - - return 0; -} - -static int autofs_root_mkdir(struct inode *dir, struct dentry *dentry, int mode) -{ - struct autofs_sb_info *sbi = autofs_sbi(dir->i_sb); - struct autofs_dirhash *dh = &sbi->dirhash; - struct autofs_dir_ent *ent; - struct inode *inode; - ino_t ino; - - lock_kernel(); - if (!autofs_oz_mode(sbi)) { - unlock_kernel(); - return -EACCES; - } - - ent = autofs_hash_lookup(dh, &dentry->d_name); - if (ent) { - unlock_kernel(); - return -EEXIST; - } - - if (sbi->next_dir_ino < AUTOFS_FIRST_DIR_INO) { - printk("autofs: Out of inode numbers -- what the heck did you do??\n"); - unlock_kernel(); - return -ENOSPC; - } - ino = sbi->next_dir_ino++; - - ent = kmalloc(sizeof(struct autofs_dir_ent), GFP_KERNEL); - if (!ent) { - unlock_kernel(); - return -ENOSPC; - } - - ent->name = kmalloc(dentry->d_name.len+1, GFP_KERNEL); - if (!ent->name) { - kfree(ent); - unlock_kernel(); - return -ENOSPC; - } - - ent->hash = dentry->d_name.hash; - memcpy(ent->name, dentry->d_name.name, 1+(ent->len = dentry->d_name.len)); - ent->ino = ino; - ent->dentry = dentry; - autofs_hash_insert(dh,ent); - - inc_nlink(dir); - - inode = autofs_iget(dir->i_sb, ino); - if (IS_ERR(inode)) { - drop_nlink(dir); - return PTR_ERR(inode); - } - - d_instantiate(dentry, inode); - unlock_kernel(); - - return 0; -} - -/* Get/set timeout ioctl() operation */ -#ifdef CONFIG_COMPAT -static inline int autofs_compat_get_set_timeout(struct autofs_sb_info *sbi, - unsigned int __user *p) -{ - unsigned long ntimeout; - - if (get_user(ntimeout, p) || - put_user(sbi->exp_timeout / HZ, p)) - return -EFAULT; - - if (ntimeout > UINT_MAX/HZ) - sbi->exp_timeout = 0; - else - sbi->exp_timeout = ntimeout * HZ; - - return 0; -} -#endif - -static inline int autofs_get_set_timeout(struct autofs_sb_info *sbi, - unsigned long __user *p) -{ - unsigned long ntimeout; - - if (get_user(ntimeout, p) || - put_user(sbi->exp_timeout / HZ, p)) - return -EFAULT; - - if (ntimeout > ULONG_MAX/HZ) - sbi->exp_timeout = 0; - else - sbi->exp_timeout = ntimeout * HZ; - - return 0; -} - -/* Return protocol version */ -static inline int autofs_get_protover(int __user *p) -{ - return put_user(AUTOFS_PROTO_VERSION, p); -} - -/* Perform an expiry operation */ -static inline int autofs_expire_run(struct super_block *sb, - struct autofs_sb_info *sbi, - struct vfsmount *mnt, - struct autofs_packet_expire __user *pkt_p) -{ - struct autofs_dir_ent *ent; - struct autofs_packet_expire pkt; - - memset(&pkt,0,sizeof pkt); - - pkt.hdr.proto_version = AUTOFS_PROTO_VERSION; - pkt.hdr.type = autofs_ptype_expire; - - if (!sbi->exp_timeout || !(ent = autofs_expire(sb,sbi,mnt))) - return -EAGAIN; - - pkt.len = ent->len; - memcpy(pkt.name, ent->name, pkt.len); - pkt.name[pkt.len] = '\0'; - - if (copy_to_user(pkt_p, &pkt, sizeof(struct autofs_packet_expire))) - return -EFAULT; - - return 0; -} - -/* - * ioctl()'s on the root directory is the chief method for the daemon to - * generate kernel reactions - */ -static int autofs_do_root_ioctl(struct inode *inode, struct file *filp, - unsigned int cmd, unsigned long arg) -{ - struct autofs_sb_info *sbi = autofs_sbi(inode->i_sb); - void __user *argp = (void __user *)arg; - - DPRINTK(("autofs_ioctl: cmd = 0x%08x, arg = 0x%08lx, sbi = %p, pgrp = %u\n",cmd,arg,sbi,task_pgrp_nr(current))); - - if (_IOC_TYPE(cmd) != _IOC_TYPE(AUTOFS_IOC_FIRST) || - _IOC_NR(cmd) - _IOC_NR(AUTOFS_IOC_FIRST) >= AUTOFS_IOC_COUNT) - return -ENOTTY; - - if (!autofs_oz_mode(sbi) && !capable(CAP_SYS_ADMIN)) - return -EPERM; - - switch(cmd) { - case AUTOFS_IOC_READY: /* Wait queue: go ahead and retry */ - return autofs_wait_release(sbi,(autofs_wqt_t)arg,0); - case AUTOFS_IOC_FAIL: /* Wait queue: fail with ENOENT */ - return autofs_wait_release(sbi,(autofs_wqt_t)arg,-ENOENT); - case AUTOFS_IOC_CATATONIC: /* Enter catatonic mode (daemon shutdown) */ - autofs_catatonic_mode(sbi); - return 0; - case AUTOFS_IOC_PROTOVER: /* Get protocol version */ - return autofs_get_protover(argp); -#ifdef CONFIG_COMPAT - case AUTOFS_IOC_SETTIMEOUT32: - return autofs_compat_get_set_timeout(sbi, argp); -#endif - case AUTOFS_IOC_SETTIMEOUT: - return autofs_get_set_timeout(sbi, argp); - case AUTOFS_IOC_EXPIRE: - return autofs_expire_run(inode->i_sb, sbi, filp->f_path.mnt, - argp); - default: - return -ENOSYS; - } - -} - -static long autofs_root_ioctl(struct file *filp, - unsigned int cmd, unsigned long arg) -{ - int ret; - - lock_kernel(); - ret = autofs_do_root_ioctl(filp->f_path.dentry->d_inode, - filp, cmd, arg); - unlock_kernel(); - - return ret; -} - -#ifdef CONFIG_COMPAT -static long autofs_root_compat_ioctl(struct file *filp, - unsigned int cmd, unsigned long arg) -{ - struct inode *inode = filp->f_path.dentry->d_inode; - int ret; - - lock_kernel(); - if (cmd == AUTOFS_IOC_READY || cmd == AUTOFS_IOC_FAIL) - ret = autofs_do_root_ioctl(inode, filp, cmd, arg); - else - ret = autofs_do_root_ioctl(inode, filp, cmd, - (unsigned long)compat_ptr(arg)); - unlock_kernel(); - - return ret; -} -#endif diff --git a/fs/autofs/symlink.c b/fs/autofs/symlink.c deleted file mode 100644 index 7ce9cb2c9ce2..000000000000 --- a/fs/autofs/symlink.c +++ /dev/null @@ -1,26 +0,0 @@ -/* -*- linux-c -*- --------------------------------------------------------- * - * - * linux/fs/autofs/symlink.c - * - * Copyright 1997-1998 Transmeta Corporation -- All Rights Reserved - * - * This file is part of the Linux kernel and is made available under - * the terms of the GNU General Public License, version 2, or at your - * option, any later version, incorporated herein by reference. - * - * ------------------------------------------------------------------------- */ - -#include "autofs_i.h" - -/* Nothing to release.. */ -static void *autofs_follow_link(struct dentry *dentry, struct nameidata *nd) -{ - char *s=((struct autofs_symlink *)dentry->d_inode->i_private)->data; - nd_set_link(nd, s); - return NULL; -} - -const struct inode_operations autofs_symlink_inode_operations = { - .readlink = generic_readlink, - .follow_link = autofs_follow_link -}; diff --git a/fs/autofs/waitq.c b/fs/autofs/waitq.c deleted file mode 100644 index be46805972f0..000000000000 --- a/fs/autofs/waitq.c +++ /dev/null @@ -1,205 +0,0 @@ -/* -*- linux-c -*- --------------------------------------------------------- * - * - * linux/fs/autofs/waitq.c - * - * Copyright 1997-1998 Transmeta Corporation -- All Rights Reserved - * - * This file is part of the Linux kernel and is made available under - * the terms of the GNU General Public License, version 2, or at your - * option, any later version, incorporated herein by reference. - * - * ------------------------------------------------------------------------- */ - -#include -#include -#include -#include -#include "autofs_i.h" - -/* We make this a static variable rather than a part of the superblock; it - is better if we don't reassign numbers easily even across filesystems */ -static autofs_wqt_t autofs_next_wait_queue = 1; - -/* These are the signals we allow interrupting a pending mount */ -#define SHUTDOWN_SIGS (sigmask(SIGKILL) | sigmask(SIGINT) | sigmask(SIGQUIT)) - -void autofs_catatonic_mode(struct autofs_sb_info *sbi) -{ - struct autofs_wait_queue *wq, *nwq; - - DPRINTK(("autofs: entering catatonic mode\n")); - - sbi->catatonic = 1; - wq = sbi->queues; - sbi->queues = NULL; /* Erase all wait queues */ - while ( wq ) { - nwq = wq->next; - wq->status = -ENOENT; /* Magic is gone - report failure */ - kfree(wq->name); - wq->name = NULL; - wake_up(&wq->queue); - wq = nwq; - } - fput(sbi->pipe); /* Close the pipe */ - sbi->pipe = NULL; - autofs_hash_dputall(&sbi->dirhash); /* Remove all dentry pointers */ -} - -static int autofs_write(struct file *file, const void *addr, int bytes) -{ - unsigned long sigpipe, flags; - mm_segment_t fs; - const char *data = (const char *)addr; - ssize_t wr = 0; - - /** WARNING: this is not safe for writing more than PIPE_BUF bytes! **/ - - sigpipe = sigismember(¤t->pending.signal, SIGPIPE); - - /* Save pointer to user space and point back to kernel space */ - fs = get_fs(); - set_fs(KERNEL_DS); - - while (bytes && - (wr = file->f_op->write(file,data,bytes,&file->f_pos)) > 0) { - data += wr; - bytes -= wr; - } - - set_fs(fs); - - /* Keep the currently executing process from receiving a - SIGPIPE unless it was already supposed to get one */ - if (wr == -EPIPE && !sigpipe) { - spin_lock_irqsave(¤t->sighand->siglock, flags); - sigdelset(¤t->pending.signal, SIGPIPE); - recalc_sigpending(); - spin_unlock_irqrestore(¤t->sighand->siglock, flags); - } - - return (bytes > 0); -} - -static void autofs_notify_daemon(struct autofs_sb_info *sbi, struct autofs_wait_queue *wq) -{ - struct autofs_packet_missing pkt; - - DPRINTK(("autofs_wait: wait id = 0x%08lx, name = ", wq->wait_queue_token)); - autofs_say(wq->name,wq->len); - - memset(&pkt,0,sizeof pkt); /* For security reasons */ - - pkt.hdr.proto_version = AUTOFS_PROTO_VERSION; - pkt.hdr.type = autofs_ptype_missing; - pkt.wait_queue_token = wq->wait_queue_token; - pkt.len = wq->len; - memcpy(pkt.name, wq->name, pkt.len); - pkt.name[pkt.len] = '\0'; - - if ( autofs_write(sbi->pipe,&pkt,sizeof(struct autofs_packet_missing)) ) - autofs_catatonic_mode(sbi); -} - -int autofs_wait(struct autofs_sb_info *sbi, struct qstr *name) -{ - struct autofs_wait_queue *wq; - int status; - - /* In catatonic mode, we don't wait for nobody */ - if ( sbi->catatonic ) - return -ENOENT; - - /* We shouldn't be able to get here, but just in case */ - if ( name->len > NAME_MAX ) - return -ENOENT; - - for ( wq = sbi->queues ; wq ; wq = wq->next ) { - if ( wq->hash == name->hash && - wq->len == name->len && - wq->name && !memcmp(wq->name,name->name,name->len) ) - break; - } - - if ( !wq ) { - /* Create a new wait queue */ - wq = kmalloc(sizeof(struct autofs_wait_queue),GFP_KERNEL); - if ( !wq ) - return -ENOMEM; - - wq->name = kmalloc(name->len,GFP_KERNEL); - if ( !wq->name ) { - kfree(wq); - return -ENOMEM; - } - wq->wait_queue_token = autofs_next_wait_queue++; - init_waitqueue_head(&wq->queue); - wq->hash = name->hash; - wq->len = name->len; - wq->status = -EINTR; /* Status return if interrupted */ - memcpy(wq->name, name->name, name->len); - wq->next = sbi->queues; - sbi->queues = wq; - - /* autofs_notify_daemon() may block */ - wq->wait_ctr = 2; - autofs_notify_daemon(sbi,wq); - } else - wq->wait_ctr++; - - /* wq->name is NULL if and only if the lock is already released */ - - if ( sbi->catatonic ) { - /* We might have slept, so check again for catatonic mode */ - wq->status = -ENOENT; - kfree(wq->name); - wq->name = NULL; - } - - if ( wq->name ) { - /* Block all but "shutdown" signals while waiting */ - sigset_t sigmask; - - siginitsetinv(&sigmask, SHUTDOWN_SIGS); - sigprocmask(SIG_BLOCK, &sigmask, &sigmask); - - interruptible_sleep_on(&wq->queue); - - sigprocmask(SIG_SETMASK, &sigmask, NULL); - } else { - DPRINTK(("autofs_wait: skipped sleeping\n")); - } - - status = wq->status; - - if ( ! --wq->wait_ctr ) /* Are we the last process to need status? */ - kfree(wq); - - return status; -} - - -int autofs_wait_release(struct autofs_sb_info *sbi, autofs_wqt_t wait_queue_token, int status) -{ - struct autofs_wait_queue *wq, **wql; - - for (wql = &sbi->queues; (wq = *wql) != NULL; wql = &wq->next) { - if ( wq->wait_queue_token == wait_queue_token ) - break; - } - if ( !wq ) - return -EINVAL; - - *wql = wq->next; /* Unlink from chain */ - kfree(wq->name); - wq->name = NULL; /* Do not wait on this queue */ - - wq->status = status; - - if ( ! --wq->wait_ctr ) /* Is anyone still waiting for this guy? */ - kfree(wq); - else - wake_up(&wq->queue); - - return 0; -} - -- cgit v1.2.3 From 2116b7a473bf1c8d26998b477c294e7fe294921f Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Mon, 4 Oct 2010 22:55:57 +0200 Subject: smbfs: move to drivers/staging smbfs has been scheduled for removal in 2.6.27, so maybe we can now move it to drivers/staging on the way out. smbfs still uses the big kernel lock and nobody is going to fix that, so we should be getting rid of it soon. This removes the 32 bit compat mount and ioctl handling code, which is implemented in common fs code, and moves all smbfs related files into drivers/staging/smbfs. Signed-off-by: Arnd Bergmann Acked-by: Jeff Layton Signed-off-by: Greg Kroah-Hartman --- Documentation/filesystems/00-INDEX | 2 - Documentation/filesystems/smbfs.txt | 8 - Documentation/ioctl/ioctl-number.txt | 2 +- drivers/staging/Kconfig | 2 + drivers/staging/Makefile | 1 + drivers/staging/smbfs/Kconfig | 55 + drivers/staging/smbfs/Makefile | 18 + drivers/staging/smbfs/TODO | 8 + drivers/staging/smbfs/cache.c | 208 ++ drivers/staging/smbfs/dir.c | 702 +++++++ drivers/staging/smbfs/file.c | 453 +++++ drivers/staging/smbfs/getopt.c | 64 + drivers/staging/smbfs/getopt.h | 14 + drivers/staging/smbfs/inode.c | 839 ++++++++ drivers/staging/smbfs/ioctl.c | 68 + drivers/staging/smbfs/proc.c | 3506 +++++++++++++++++++++++++++++++++ drivers/staging/smbfs/proto.h | 87 + drivers/staging/smbfs/request.c | 817 ++++++++ drivers/staging/smbfs/request.h | 70 + drivers/staging/smbfs/smb.h | 118 ++ drivers/staging/smbfs/smb_debug.h | 34 + drivers/staging/smbfs/smb_fs.h | 153 ++ drivers/staging/smbfs/smb_fs_i.h | 37 + drivers/staging/smbfs/smb_fs_sb.h | 100 + drivers/staging/smbfs/smb_mount.h | 65 + drivers/staging/smbfs/smbfs.txt | 8 + drivers/staging/smbfs/smbiod.c | 343 ++++ drivers/staging/smbfs/smbno.h | 363 ++++ drivers/staging/smbfs/sock.c | 385 ++++ drivers/staging/smbfs/symlink.c | 67 + fs/Kconfig | 1 - fs/Makefile | 1 - fs/compat.c | 31 +- fs/compat_ioctl.c | 26 - fs/smbfs/Kconfig | 55 - fs/smbfs/Makefile | 18 - fs/smbfs/cache.c | 208 -- fs/smbfs/dir.c | 702 ------- fs/smbfs/file.c | 454 ----- fs/smbfs/getopt.c | 64 - fs/smbfs/getopt.h | 14 - fs/smbfs/inode.c | 839 -------- fs/smbfs/ioctl.c | 69 - fs/smbfs/proc.c | 3507 ---------------------------------- fs/smbfs/proto.h | 87 - fs/smbfs/request.c | 818 -------- fs/smbfs/request.h | 70 - fs/smbfs/smb_debug.h | 34 - fs/smbfs/smbiod.c | 344 ---- fs/smbfs/sock.c | 386 ---- fs/smbfs/symlink.c | 68 - include/linux/Kbuild | 4 - include/linux/smb.h | 118 -- include/linux/smb_fs.h | 153 -- include/linux/smb_fs_i.h | 37 - include/linux/smb_fs_sb.h | 100 - include/linux/smb_mount.h | 65 - include/linux/smbno.h | 363 ---- 58 files changed, 8587 insertions(+), 8646 deletions(-) delete mode 100644 Documentation/filesystems/smbfs.txt create mode 100644 drivers/staging/smbfs/Kconfig create mode 100644 drivers/staging/smbfs/Makefile create mode 100644 drivers/staging/smbfs/TODO create mode 100644 drivers/staging/smbfs/cache.c create mode 100644 drivers/staging/smbfs/dir.c create mode 100644 drivers/staging/smbfs/file.c create mode 100644 drivers/staging/smbfs/getopt.c create mode 100644 drivers/staging/smbfs/getopt.h create mode 100644 drivers/staging/smbfs/inode.c create mode 100644 drivers/staging/smbfs/ioctl.c create mode 100644 drivers/staging/smbfs/proc.c create mode 100644 drivers/staging/smbfs/proto.h create mode 100644 drivers/staging/smbfs/request.c create mode 100644 drivers/staging/smbfs/request.h create mode 100644 drivers/staging/smbfs/smb.h create mode 100644 drivers/staging/smbfs/smb_debug.h create mode 100644 drivers/staging/smbfs/smb_fs.h create mode 100644 drivers/staging/smbfs/smb_fs_i.h create mode 100644 drivers/staging/smbfs/smb_fs_sb.h create mode 100644 drivers/staging/smbfs/smb_mount.h create mode 100644 drivers/staging/smbfs/smbfs.txt create mode 100644 drivers/staging/smbfs/smbiod.c create mode 100644 drivers/staging/smbfs/smbno.h create mode 100644 drivers/staging/smbfs/sock.c create mode 100644 drivers/staging/smbfs/symlink.c delete mode 100644 fs/smbfs/Kconfig delete mode 100644 fs/smbfs/Makefile delete mode 100644 fs/smbfs/cache.c delete mode 100644 fs/smbfs/dir.c delete mode 100644 fs/smbfs/file.c delete mode 100644 fs/smbfs/getopt.c delete mode 100644 fs/smbfs/getopt.h delete mode 100644 fs/smbfs/inode.c delete mode 100644 fs/smbfs/ioctl.c delete mode 100644 fs/smbfs/proc.c delete mode 100644 fs/smbfs/proto.h delete mode 100644 fs/smbfs/request.c delete mode 100644 fs/smbfs/request.h delete mode 100644 fs/smbfs/smb_debug.h delete mode 100644 fs/smbfs/smbiod.c delete mode 100644 fs/smbfs/sock.c delete mode 100644 fs/smbfs/symlink.c delete mode 100644 include/linux/smb.h delete mode 100644 include/linux/smb_fs.h delete mode 100644 include/linux/smb_fs_i.h delete mode 100644 include/linux/smb_fs_sb.h delete mode 100644 include/linux/smb_mount.h delete mode 100644 include/linux/smbno.h (limited to 'fs') diff --git a/Documentation/filesystems/00-INDEX b/Documentation/filesystems/00-INDEX index 4303614b5add..8c624a18f67d 100644 --- a/Documentation/filesystems/00-INDEX +++ b/Documentation/filesystems/00-INDEX @@ -96,8 +96,6 @@ seq_file.txt - how to use the seq_file API sharedsubtree.txt - a description of shared subtrees for namespaces. -smbfs.txt - - info on using filesystems with the SMB protocol (Win 3.11 and NT). spufs.txt - info and mount options for the SPU filesystem used on Cell. sysfs-pci.txt diff --git a/Documentation/filesystems/smbfs.txt b/Documentation/filesystems/smbfs.txt deleted file mode 100644 index 194fb0decd2c..000000000000 --- a/Documentation/filesystems/smbfs.txt +++ /dev/null @@ -1,8 +0,0 @@ -Smbfs is a filesystem that implements the SMB protocol, which is the -protocol used by Windows for Workgroups, Windows 95 and Windows NT. -Smbfs was inspired by Samba, the program written by Andrew Tridgell -that turns any Unix host into a file server for DOS or Windows clients. - -Smbfs is a SMB client, but uses parts of samba for its operation. For -more info on samba, including documentation, please go to -http://www.samba.org/ and then on to your nearest mirror. diff --git a/Documentation/ioctl/ioctl-number.txt b/Documentation/ioctl/ioctl-number.txt index 33223ff121d8..d15834a6e461 100644 --- a/Documentation/ioctl/ioctl-number.txt +++ b/Documentation/ioctl/ioctl-number.txt @@ -259,7 +259,7 @@ Code Seq#(hex) Include File Comments 't' 00-7F linux/if_ppp.h 't' 80-8F linux/isdn_ppp.h 't' 90 linux/toshiba.h -'u' 00-1F linux/smb_fs.h +'u' 00-1F linux/smb_fs.h gone 'v' all linux/videodev.h conflict! 'v' 00-1F linux/ext2_fs.h conflict! 'v' 00-1F linux/fs.h conflict! diff --git a/drivers/staging/Kconfig b/drivers/staging/Kconfig index 01503536e457..4a9190808b73 100644 --- a/drivers/staging/Kconfig +++ b/drivers/staging/Kconfig @@ -149,6 +149,8 @@ source "drivers/staging/msm/Kconfig" source "drivers/staging/lirc/Kconfig" +source "drivers/staging/smbfs/Kconfig" + source "drivers/staging/easycap/Kconfig" source "drivers/staging/solo6x10/Kconfig" diff --git a/drivers/staging/Makefile b/drivers/staging/Makefile index de5c0e5a99ed..7cb02a8e9f77 100644 --- a/drivers/staging/Makefile +++ b/drivers/staging/Makefile @@ -32,6 +32,7 @@ obj-$(CONFIG_AUTOFS_FS) += autofs/ obj-$(CONFIG_IDE_PHISON) += phison/ obj-$(CONFIG_LINE6_USB) += line6/ obj-$(CONFIG_USB_SERIAL_QUATECH2) += serqt_usb2/ +obj-$(CONFIG_SMB_FS) += smbfs/ obj-$(CONFIG_USB_SERIAL_QUATECH_USB2) += quatech_usb2/ obj-$(CONFIG_OCTEON_ETHERNET) += octeon/ obj-$(CONFIG_VT6655) += vt6655/ diff --git a/drivers/staging/smbfs/Kconfig b/drivers/staging/smbfs/Kconfig new file mode 100644 index 000000000000..e668127c8b2e --- /dev/null +++ b/drivers/staging/smbfs/Kconfig @@ -0,0 +1,55 @@ +config SMB_FS + tristate "SMB file system support (OBSOLETE, please use CIFS)" + depends on INET + select NLS + help + SMB (Server Message Block) is the protocol Windows for Workgroups + (WfW), Windows 95/98, Windows NT and OS/2 Lan Manager use to share + files and printers over local networks. Saying Y here allows you to + mount their file systems (often called "shares" in this context) and + access them just like any other Unix directory. Currently, this + works only if the Windows machines use TCP/IP as the underlying + transport protocol, and not NetBEUI. For details, read + and the SMB-HOWTO, + available from . + + Note: if you just want your box to act as an SMB *server* and make + files and printing services available to Windows clients (which need + to have a TCP/IP stack), you don't need to say Y here; you can use + the program SAMBA (available from ) + for that. + + General information about how to connect Linux, Windows machines and + Macs is on the WWW at . + + To compile the SMB support as a module, choose M here: + the module will be called smbfs. Most people say N, however. + +config SMB_NLS_DEFAULT + bool "Use a default NLS" + depends on SMB_FS + help + Enabling this will make smbfs use nls translations by default. You + need to specify the local charset (CONFIG_NLS_DEFAULT) in the nls + settings and you need to give the default nls for the SMB server as + CONFIG_SMB_NLS_REMOTE. + + The nls settings can be changed at mount time, if your smbmount + supports that, using the codepage and iocharset parameters. + + smbmount from samba 2.2.0 or later supports this. + +config SMB_NLS_REMOTE + string "Default Remote NLS Option" + depends on SMB_NLS_DEFAULT + default "cp437" + help + This setting allows you to specify a default value for which + codepage the server uses. If this field is left blank no + translations will be done by default. The local codepage/charset + default to CONFIG_NLS_DEFAULT. + + The nls settings can be changed at mount time, if your smbmount + supports that, using the codepage and iocharset parameters. + + smbmount from samba 2.2.0 or later supports this. diff --git a/drivers/staging/smbfs/Makefile b/drivers/staging/smbfs/Makefile new file mode 100644 index 000000000000..4faf8c4722c3 --- /dev/null +++ b/drivers/staging/smbfs/Makefile @@ -0,0 +1,18 @@ +# +# Makefile for the linux smb-filesystem routines. +# + +obj-$(CONFIG_SMB_FS) += smbfs.o + +smbfs-objs := proc.o dir.o cache.o sock.o inode.o file.o ioctl.o getopt.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. + +EXTRA_CFLAGS += -DSMBFS_PARANOIA +#EXTRA_CFLAGS += -DSMBFS_DEBUG +#EXTRA_CFLAGS += -DSMBFS_DEBUG_VERBOSE +#EXTRA_CFLAGS += -DDEBUG_SMB_TIMESTAMP +#EXTRA_CFLAGS += -Werror + diff --git a/drivers/staging/smbfs/TODO b/drivers/staging/smbfs/TODO new file mode 100644 index 000000000000..24f4d29d53ac --- /dev/null +++ b/drivers/staging/smbfs/TODO @@ -0,0 +1,8 @@ +smbfs is on its way out of the kernel, it has been replaced +by cifs several years ago. + +The smbfs code uses the big kernel lock which +is getting deprecated. + +Users that find smbfs to work but not cifs should contact +the CIFS developers on linux-cifs@vger.kernel.org. diff --git a/drivers/staging/smbfs/cache.c b/drivers/staging/smbfs/cache.c new file mode 100644 index 000000000000..dbb98658148b --- /dev/null +++ b/drivers/staging/smbfs/cache.c @@ -0,0 +1,208 @@ +/* + * cache.c + * + * Copyright (C) 1997 by Bill Hawes + * + * Routines to support directory cacheing using the page cache. + * This cache code is almost directly taken from ncpfs. + * + * Please add a note about your changes to smbfs in the ChangeLog file. + */ + +#include +#include +#include +#include +#include +#include + +#include + +#include "smb_fs.h" +#include "smb_debug.h" +#include "proto.h" + +/* + * Force the next attempt to use the cache to be a timeout. + * If we can't find the page that's fine, it will cause a refresh. + */ +void +smb_invalid_dir_cache(struct inode * dir) +{ + struct smb_sb_info *server = server_from_inode(dir); + union smb_dir_cache *cache = NULL; + struct page *page = NULL; + + page = grab_cache_page(&dir->i_data, 0); + if (!page) + goto out; + + if (!PageUptodate(page)) + goto out_unlock; + + cache = kmap(page); + cache->head.time = jiffies - SMB_MAX_AGE(server); + + kunmap(page); + SetPageUptodate(page); +out_unlock: + unlock_page(page); + page_cache_release(page); +out: + return; +} + +/* + * Mark all dentries for 'parent' as invalid, forcing them to be re-read + */ +void +smb_invalidate_dircache_entries(struct dentry *parent) +{ + struct smb_sb_info *server = server_from_dentry(parent); + struct list_head *next; + struct dentry *dentry; + + spin_lock(&dcache_lock); + next = parent->d_subdirs.next; + while (next != &parent->d_subdirs) { + dentry = list_entry(next, struct dentry, d_u.d_child); + dentry->d_fsdata = NULL; + smb_age_dentry(server, dentry); + next = next->next; + } + spin_unlock(&dcache_lock); +} + +/* + * dget, but require that fpos and parent matches what the dentry contains. + * dentry is not known to be a valid pointer at entry. + */ +struct dentry * +smb_dget_fpos(struct dentry *dentry, struct dentry *parent, unsigned long fpos) +{ + struct dentry *dent = dentry; + struct list_head *next; + + if (d_validate(dent, parent)) { + if (dent->d_name.len <= SMB_MAXNAMELEN && + (unsigned long)dent->d_fsdata == fpos) { + if (!dent->d_inode) { + dput(dent); + dent = NULL; + } + return dent; + } + dput(dent); + } + + /* If a pointer is invalid, we search the dentry. */ + spin_lock(&dcache_lock); + next = parent->d_subdirs.next; + while (next != &parent->d_subdirs) { + dent = list_entry(next, struct dentry, d_u.d_child); + if ((unsigned long)dent->d_fsdata == fpos) { + if (dent->d_inode) + dget_locked(dent); + else + dent = NULL; + goto out_unlock; + } + next = next->next; + } + dent = NULL; +out_unlock: + spin_unlock(&dcache_lock); + return dent; +} + + +/* + * Create dentry/inode for this file and add it to the dircache. + */ +int +smb_fill_cache(struct file *filp, void *dirent, filldir_t filldir, + struct smb_cache_control *ctrl, struct qstr *qname, + struct smb_fattr *entry) +{ + struct dentry *newdent, *dentry = filp->f_path.dentry; + struct inode *newino, *inode = dentry->d_inode; + struct smb_cache_control ctl = *ctrl; + int valid = 0; + int hashed = 0; + ino_t ino = 0; + + qname->hash = full_name_hash(qname->name, qname->len); + + if (dentry->d_op && dentry->d_op->d_hash) + if (dentry->d_op->d_hash(dentry, qname) != 0) + goto end_advance; + + newdent = d_lookup(dentry, qname); + + if (!newdent) { + newdent = d_alloc(dentry, qname); + if (!newdent) + goto end_advance; + } else { + hashed = 1; + memcpy((char *) newdent->d_name.name, qname->name, + newdent->d_name.len); + } + + if (!newdent->d_inode) { + smb_renew_times(newdent); + entry->f_ino = iunique(inode->i_sb, 2); + newino = smb_iget(inode->i_sb, entry); + if (newino) { + smb_new_dentry(newdent); + d_instantiate(newdent, newino); + if (!hashed) + d_rehash(newdent); + } + } else + smb_set_inode_attr(newdent->d_inode, entry); + + if (newdent->d_inode) { + ino = newdent->d_inode->i_ino; + newdent->d_fsdata = (void *) ctl.fpos; + smb_new_dentry(newdent); + } + + if (ctl.idx >= SMB_DIRCACHE_SIZE) { + if (ctl.page) { + kunmap(ctl.page); + SetPageUptodate(ctl.page); + unlock_page(ctl.page); + page_cache_release(ctl.page); + } + ctl.cache = NULL; + ctl.idx -= SMB_DIRCACHE_SIZE; + ctl.ofs += 1; + ctl.page = grab_cache_page(&inode->i_data, ctl.ofs); + if (ctl.page) + ctl.cache = kmap(ctl.page); + } + if (ctl.cache) { + ctl.cache->dentry[ctl.idx] = newdent; + valid = 1; + } + dput(newdent); + +end_advance: + if (!valid) + ctl.valid = 0; + if (!ctl.filled && (ctl.fpos == filp->f_pos)) { + if (!ino) + ino = find_inode_number(dentry, qname); + if (!ino) + ino = iunique(inode->i_sb, 2); + ctl.filled = filldir(dirent, qname->name, qname->len, + filp->f_pos, ino, DT_UNKNOWN); + if (!ctl.filled) + filp->f_pos += 1; + } + ctl.fpos += 1; + ctl.idx += 1; + *ctrl = ctl; + return (ctl.valid || !ctl.filled); +} diff --git a/drivers/staging/smbfs/dir.c b/drivers/staging/smbfs/dir.c new file mode 100644 index 000000000000..936b3bb2099c --- /dev/null +++ b/drivers/staging/smbfs/dir.c @@ -0,0 +1,702 @@ +/* + * dir.c + * + * Copyright (C) 1995, 1996 by Paal-Kr. Engstad and Volker Lendecke + * Copyright (C) 1997 by Volker Lendecke + * + * Please add a note about your changes to smbfs in the ChangeLog file. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "smb_fs.h" +#include "smb_mount.h" +#include "smbno.h" + +#include "smb_debug.h" +#include "proto.h" + +static int smb_readdir(struct file *, void *, filldir_t); +static int smb_dir_open(struct inode *, struct file *); + +static struct dentry *smb_lookup(struct inode *, struct dentry *, struct nameidata *); +static int smb_create(struct inode *, struct dentry *, int, struct nameidata *); +static int smb_mkdir(struct inode *, struct dentry *, int); +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,dev_t); +static int smb_link(struct dentry *, struct inode *, struct dentry *); + +const struct file_operations smb_dir_operations = +{ + .llseek = generic_file_llseek, + .read = generic_read_dir, + .readdir = smb_readdir, + .unlocked_ioctl = smb_ioctl, + .open = smb_dir_open, +}; + +const struct inode_operations smb_dir_inode_operations = +{ + .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, +}; + +const 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. + * + * The cache code is almost directly taken from ncpfs + */ +static int +smb_readdir(struct file *filp, void *dirent, filldir_t filldir) +{ + struct dentry *dentry = filp->f_path.dentry; + struct inode *dir = dentry->d_inode; + struct smb_sb_info *server = server_from_dentry(dentry); + union smb_dir_cache *cache = NULL; + struct smb_cache_control ctl; + struct page *page = NULL; + int result; + + ctl.page = NULL; + ctl.cache = NULL; + + VERBOSE("reading %s/%s, f_pos=%d\n", + DENTRY_PATH(dentry), (int) filp->f_pos); + + result = 0; + + lock_kernel(); + + switch ((unsigned int) filp->f_pos) { + case 0: + if (filldir(dirent, ".", 1, 0, dir->i_ino, DT_DIR) < 0) + goto out; + filp->f_pos = 1; + /* fallthrough */ + case 1: + if (filldir(dirent, "..", 2, 1, parent_ino(dentry), DT_DIR) < 0) + goto out; + filp->f_pos = 2; + } + + /* + * Make sure our inode is up-to-date. + */ + result = smb_revalidate_inode(dentry); + if (result) + goto out; + + + page = grab_cache_page(&dir->i_data, 0); + if (!page) + goto read_really; + + ctl.cache = cache = kmap(page); + ctl.head = cache->head; + + if (!PageUptodate(page) || !ctl.head.eof) { + VERBOSE("%s/%s, page uptodate=%d, eof=%d\n", + DENTRY_PATH(dentry), PageUptodate(page),ctl.head.eof); + goto init_cache; + } + + if (filp->f_pos == 2) { + if (jiffies - ctl.head.time >= SMB_MAX_AGE(server)) + goto init_cache; + + /* + * N.B. ncpfs checks mtime of dentry too here, we don't. + * 1. common smb servers do not update mtime on dir changes + * 2. it requires an extra smb request + * (revalidate has the same timeout as ctl.head.time) + * + * Instead smbfs invalidates its own cache on local changes + * and remote changes are not seen until timeout. + */ + } + + if (filp->f_pos > ctl.head.end) + goto finished; + + ctl.fpos = filp->f_pos + (SMB_DIRCACHE_START - 2); + ctl.ofs = ctl.fpos / SMB_DIRCACHE_SIZE; + ctl.idx = ctl.fpos % SMB_DIRCACHE_SIZE; + + for (;;) { + if (ctl.ofs != 0) { + ctl.page = find_lock_page(&dir->i_data, ctl.ofs); + if (!ctl.page) + goto invalid_cache; + ctl.cache = kmap(ctl.page); + if (!PageUptodate(ctl.page)) + goto invalid_cache; + } + while (ctl.idx < SMB_DIRCACHE_SIZE) { + struct dentry *dent; + int res; + + dent = smb_dget_fpos(ctl.cache->dentry[ctl.idx], + dentry, filp->f_pos); + if (!dent) + goto invalid_cache; + + res = filldir(dirent, dent->d_name.name, + dent->d_name.len, filp->f_pos, + dent->d_inode->i_ino, DT_UNKNOWN); + dput(dent); + if (res) + goto finished; + filp->f_pos += 1; + ctl.idx += 1; + if (filp->f_pos > ctl.head.end) + goto finished; + } + if (ctl.page) { + kunmap(ctl.page); + SetPageUptodate(ctl.page); + unlock_page(ctl.page); + page_cache_release(ctl.page); + ctl.page = NULL; + } + ctl.idx = 0; + ctl.ofs += 1; + } +invalid_cache: + if (ctl.page) { + kunmap(ctl.page); + unlock_page(ctl.page); + page_cache_release(ctl.page); + ctl.page = NULL; + } + ctl.cache = cache; +init_cache: + smb_invalidate_dircache_entries(dentry); + ctl.head.time = jiffies; + ctl.head.eof = 0; + ctl.fpos = 2; + ctl.ofs = 0; + ctl.idx = SMB_DIRCACHE_START; + ctl.filled = 0; + ctl.valid = 1; +read_really: + result = server->ops->readdir(filp, dirent, filldir, &ctl); + if (result == -ERESTARTSYS && page) + ClearPageUptodate(page); + if (ctl.idx == -1) + goto invalid_cache; /* retry */ + ctl.head.end = ctl.fpos - 1; + ctl.head.eof = ctl.valid; +finished: + if (page) { + cache->head = ctl.head; + kunmap(page); + if (result != -ERESTARTSYS) + SetPageUptodate(page); + unlock_page(page); + page_cache_release(page); + } + if (ctl.page) { + kunmap(ctl.page); + SetPageUptodate(ctl.page); + unlock_page(ctl.page); + page_cache_release(ctl.page); + } +out: + unlock_kernel(); + return result; +} + +static int +smb_dir_open(struct inode *dir, struct file *file) +{ + struct dentry *dentry = file->f_path.dentry; + struct smb_sb_info *server; + int error = 0; + + VERBOSE("(%s/%s)\n", dentry->d_parent->d_name.name, + file->f_path.dentry->d_name.name); + + /* + * Directory timestamps in the core protocol aren't updated + * when a file is added, so we give them a very short TTL. + */ + lock_kernel(); + server = server_from_dentry(dentry); + if (server->opt.protocol < SMB_PROTOCOL_LANMAN2) { + unsigned long age = jiffies - SMB_I(dir)->oldmtime; + if (age > 2*HZ) + smb_invalid_dir_cache(dir); + } + + /* + * Note: in order to allow the smbmount process to open the + * mount point, we only revalidate if the connection is valid or + * if the process is trying to access something other than the root. + */ + if (server->state == CONN_VALID || !IS_ROOT(dentry)) + error = smb_revalidate_inode(dentry); + unlock_kernel(); + return error; +} + +/* + * Dentry operations routines + */ +static int smb_lookup_validate(struct dentry *, struct nameidata *); +static int smb_hash_dentry(struct dentry *, struct qstr *); +static int smb_compare_dentry(struct dentry *, struct qstr *, struct qstr *); +static int smb_delete_dentry(struct dentry *); + +static const struct dentry_operations smbfs_dentry_operations = +{ + .d_revalidate = smb_lookup_validate, + .d_hash = smb_hash_dentry, + .d_compare = smb_compare_dentry, + .d_delete = smb_delete_dentry, +}; + +static const struct dentry_operations smbfs_dentry_operations_case = +{ + .d_revalidate = smb_lookup_validate, + .d_delete = smb_delete_dentry, +}; + + +/* + * This is the callback when the dcache has a lookup hit. + */ +static int +smb_lookup_validate(struct dentry * dentry, struct nameidata *nd) +{ + struct smb_sb_info *server = server_from_dentry(dentry); + struct inode * inode = dentry->d_inode; + unsigned long age = jiffies - dentry->d_time; + int valid; + + /* + * The default validation is based on dentry age: + * we believe in dentries for a few seconds. (But each + * successful server lookup renews the timestamp.) + */ + valid = (age <= SMB_MAX_AGE(server)); +#ifdef SMBFS_DEBUG_VERBOSE + if (!valid) + VERBOSE("%s/%s not valid, age=%lu\n", + DENTRY_PATH(dentry), age); +#endif + + if (inode) { + lock_kernel(); + if (is_bad_inode(inode)) { + PARANOIA("%s/%s has dud inode\n", DENTRY_PATH(dentry)); + valid = 0; + } else if (!valid) + valid = (smb_revalidate_inode(dentry) == 0); + unlock_kernel(); + } else { + /* + * What should we do for negative dentries? + */ + } + return valid; +} + +static int +smb_hash_dentry(struct dentry *dir, struct qstr *this) +{ + unsigned long hash; + int i; + + hash = init_name_hash(); + for (i=0; i < this->len ; i++) + hash = partial_name_hash(tolower(this->name[i]), hash); + this->hash = end_name_hash(hash); + + return 0; +} + +static int +smb_compare_dentry(struct dentry *dir, struct qstr *a, struct qstr *b) +{ + int i, result = 1; + + if (a->len != b->len) + goto out; + for (i=0; i < a->len; i++) { + if (tolower(a->name[i]) != tolower(b->name[i])) + goto out; + } + result = 0; +out: + return result; +} + +/* + * This is the callback from dput() when d_count is going to 0. + * We use this to unhash dentries with bad inodes. + */ +static int +smb_delete_dentry(struct dentry * dentry) +{ + if (dentry->d_inode) { + if (is_bad_inode(dentry->d_inode)) { + PARANOIA("bad inode, unhashing %s/%s\n", + DENTRY_PATH(dentry)); + return 1; + } + } else { + /* N.B. Unhash negative dentries? */ + } + return 0; +} + +/* + * Initialize a new dentry + */ +void +smb_new_dentry(struct dentry *dentry) +{ + struct smb_sb_info *server = server_from_dentry(dentry); + + if (server->mnt->flags & SMB_MOUNT_CASE) + dentry->d_op = &smbfs_dentry_operations_case; + else + dentry->d_op = &smbfs_dentry_operations; + dentry->d_time = jiffies; +} + + +/* + * Whenever a lookup succeeds, we know the parent directories + * are all valid, so we want to update the dentry timestamps. + * N.B. Move this to dcache? + */ +void +smb_renew_times(struct dentry * dentry) +{ + dget(dentry); + spin_lock(&dentry->d_lock); + for (;;) { + struct dentry *parent; + + dentry->d_time = jiffies; + if (IS_ROOT(dentry)) + break; + parent = dentry->d_parent; + dget(parent); + spin_unlock(&dentry->d_lock); + dput(dentry); + dentry = parent; + spin_lock(&dentry->d_lock); + } + spin_unlock(&dentry->d_lock); + dput(dentry); +} + +static struct dentry * +smb_lookup(struct inode *dir, struct dentry *dentry, struct nameidata *nd) +{ + struct smb_fattr finfo; + struct inode *inode; + int error; + struct smb_sb_info *server; + + error = -ENAMETOOLONG; + if (dentry->d_name.len > SMB_MAXNAMELEN) + goto out; + + /* Do not allow lookup of names with backslashes in */ + error = -EINVAL; + if (memchr(dentry->d_name.name, '\\', dentry->d_name.len)) + goto out; + + lock_kernel(); + error = smb_proc_getattr(dentry, &finfo); +#ifdef SMBFS_PARANOIA + if (error && error != -ENOENT) + PARANOIA("find %s/%s failed, error=%d\n", + DENTRY_PATH(dentry), error); +#endif + + inode = NULL; + if (error == -ENOENT) + goto add_entry; + if (!error) { + error = -EACCES; + finfo.f_ino = iunique(dentry->d_sb, 2); + inode = smb_iget(dir->i_sb, &finfo); + if (inode) { + add_entry: + server = server_from_dentry(dentry); + if (server->mnt->flags & SMB_MOUNT_CASE) + dentry->d_op = &smbfs_dentry_operations_case; + else + dentry->d_op = &smbfs_dentry_operations; + + d_add(dentry, inode); + smb_renew_times(dentry); + error = 0; + } + } + unlock_kernel(); +out: + return ERR_PTR(error); +} + +/* + * This code is common to all routines creating a new inode. + */ +static int +smb_instantiate(struct dentry *dentry, __u16 fileid, int have_id) +{ + struct smb_sb_info *server = server_from_dentry(dentry); + struct inode *inode; + int error; + struct smb_fattr fattr; + + VERBOSE("file %s/%s, fileid=%u\n", DENTRY_PATH(dentry), fileid); + + error = smb_proc_getattr(dentry, &fattr); + if (error) + goto out_close; + + smb_renew_times(dentry); + fattr.f_ino = iunique(dentry->d_sb, 2); + inode = smb_iget(dentry->d_sb, &fattr); + if (!inode) + goto out_no_inode; + + if (have_id) { + struct smb_inode_info *ei = SMB_I(inode); + ei->fileid = fileid; + ei->access = SMB_O_RDWR; + ei->open = server->generation; + } + d_instantiate(dentry, inode); +out: + return error; + +out_no_inode: + error = -EACCES; +out_close: + if (have_id) { + PARANOIA("%s/%s failed, error=%d, closing %u\n", + DENTRY_PATH(dentry), error, fileid); + smb_close_fileid(dentry, fileid); + } + goto out; +} + +/* N.B. How should the mode argument be used? */ +static int +smb_create(struct inode *dir, struct dentry *dentry, int mode, + struct nameidata *nd) +{ + 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); + + lock_kernel(); + smb_invalid_dir_cache(dir); + error = smb_proc_create(dentry, 0, get_seconds(), &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", + DENTRY_PATH(dentry), error); + } + unlock_kernel(); + return error; +} + +/* N.B. How should the mode argument be used? */ +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(); + return error; +} + +static int +smb_rmdir(struct inode *dir, struct dentry *dentry) +{ + struct inode *inode = dentry->d_inode; + int error; + + /* + * Close the directory if it's open. + */ + lock_kernel(); + smb_close(inode); + + /* + * Check that nobody else is using the directory.. + */ + error = -EBUSY; + if (!d_unhashed(dentry)) + goto out; + + smb_invalid_dir_cache(dir); + error = smb_proc_rmdir(dentry); + +out: + unlock_kernel(); + return error; +} + +static int +smb_unlink(struct inode *dir, struct dentry *dentry) +{ + int error; + + /* + * Close the file if it's open. + */ + lock_kernel(); + smb_close(dentry->d_inode); + + smb_invalid_dir_cache(dir); + error = smb_proc_unlink(dentry); + if (!error) + smb_renew_times(dentry); + unlock_kernel(); + return error; +} + +static int +smb_rename(struct inode *old_dir, struct dentry *old_dentry, + struct inode *new_dir, struct dentry *new_dentry) +{ + int error; + + /* + * Close any open files, and check whether to delete the + * target before attempting the rename. + */ + lock_kernel(); + if (old_dentry->d_inode) + smb_close(old_dentry->d_inode); + if (new_dentry->d_inode) { + smb_close(new_dentry->d_inode); + error = smb_proc_unlink(new_dentry); + if (error) { + VERBOSE("unlink %s/%s, error=%d\n", + DENTRY_PATH(new_dentry), error); + goto out; + } + /* FIXME */ + d_delete(new_dentry); + } + + smb_invalid_dir_cache(old_dir); + smb_invalid_dir_cache(new_dir); + error = smb_proc_mv(old_dentry, new_dentry); + if (!error) { + smb_renew_times(old_dentry); + smb_renew_times(new_dentry); + } +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, dev_t dev) +{ + int error; + struct iattr attr; + + attr.ia_valid = ATTR_MODE | ATTR_UID | ATTR_GID; + attr.ia_mode = mode; + current_euid_egid(&attr.ia_uid, &attr.ia_gid); + + if (!new_valid_dev(dev)) + return -EINVAL; + + 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/drivers/staging/smbfs/file.c b/drivers/staging/smbfs/file.c new file mode 100644 index 000000000000..5dcd19c60eb9 --- /dev/null +++ b/drivers/staging/smbfs/file.c @@ -0,0 +1,453 @@ +/* + * file.c + * + * Copyright (C) 1995, 1996, 1997 by Paal-Kr. Engstad and Volker Lendecke + * Copyright (C) 1997 by Volker Lendecke + * + * 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 "smbno.h" +#include "smb_fs.h" +#include "smb_debug.h" +#include "proto.h" + +static int +smb_fsync(struct file *file, int datasync) +{ + struct dentry *dentry = file->f_path.dentry; + struct smb_sb_info *server = server_from_dentry(dentry); + int result; + + VERBOSE("sync file %s/%s\n", DENTRY_PATH(dentry)); + + /* + * The VFS will writepage() all dirty pages for us, but we + * should send a SMBflush to the server, letting it know that + * we want things synchronized with actual storage. + * + * Note: this function requires all pages to have been written already + * (should be ok with writepage_sync) + */ + result = smb_proc_flush(server, SMB_I(dentry->d_inode)->fileid); + return result; +} + +/* + * Read a page synchronously. + */ +static int +smb_readpage_sync(struct dentry *dentry, struct page *page) +{ + char *buffer = kmap(page); + loff_t offset = (loff_t)page->index << PAGE_CACHE_SHIFT; + struct smb_sb_info *server = server_from_dentry(dentry); + unsigned int rsize = smb_get_rsize(server); + int count = PAGE_SIZE; + int result; + + VERBOSE("file %s/%s, count=%d@%Ld, rsize=%d\n", + DENTRY_PATH(dentry), count, offset, rsize); + + result = smb_open(dentry, SMB_O_RDONLY); + if (result < 0) + goto io_error; + + do { + if (count < rsize) + rsize = count; + + result = server->ops->read(dentry->d_inode,offset,rsize,buffer); + if (result < 0) + goto io_error; + + count -= result; + offset += result; + buffer += result; + dentry->d_inode->i_atime = + current_fs_time(dentry->d_inode->i_sb); + if (result < rsize) + break; + } while (count); + + memset(buffer, 0, count); + flush_dcache_page(page); + SetPageUptodate(page); + result = 0; + +io_error: + kunmap(page); + unlock_page(page); + return result; +} + +/* + * We are called with the page locked and we unlock it when done. + */ +static int +smb_readpage(struct file *file, struct page *page) +{ + int error; + struct dentry *dentry = file->f_path.dentry; + + page_cache_get(page); + error = smb_readpage_sync(dentry, page); + page_cache_release(page); + return error; +} + +/* + * Write a page synchronously. + * Offset is the data offset within the page. + */ +static int +smb_writepage_sync(struct inode *inode, struct page *page, + unsigned long pageoffset, unsigned int count) +{ + loff_t offset; + char *buffer = kmap(page) + pageoffset; + struct smb_sb_info *server = server_from_inode(inode); + unsigned int wsize = smb_get_wsize(server); + int ret = 0; + + offset = ((loff_t)page->index << PAGE_CACHE_SHIFT) + pageoffset; + VERBOSE("file ino=%ld, fileid=%d, count=%d@%Ld, wsize=%d\n", + inode->i_ino, SMB_I(inode)->fileid, count, offset, wsize); + + do { + int write_ret; + + if (count < wsize) + wsize = count; + + write_ret = server->ops->write(inode, offset, wsize, buffer); + if (write_ret < 0) { + PARANOIA("failed write, wsize=%d, write_ret=%d\n", + wsize, write_ret); + ret = write_ret; + break; + } + /* N.B. what if result < wsize?? */ +#ifdef SMBFS_PARANOIA + if (write_ret < wsize) + PARANOIA("short write, wsize=%d, write_ret=%d\n", + wsize, write_ret); +#endif + buffer += wsize; + offset += wsize; + count -= wsize; + /* + * Update the inode now rather than waiting for a refresh. + */ + inode->i_mtime = inode->i_atime = current_fs_time(inode->i_sb); + SMB_I(inode)->flags |= SMB_F_LOCALWRITE; + if (offset > inode->i_size) + inode->i_size = offset; + } while (count); + + kunmap(page); + return ret; +} + +/* + * Write a page to the server. This will be used for NFS swapping only + * (for now), and we currently do this synchronously only. + * + * We are called with the page locked and we unlock it when done. + */ +static int +smb_writepage(struct page *page, struct writeback_control *wbc) +{ + struct address_space *mapping = page->mapping; + struct inode *inode; + unsigned long end_index; + unsigned offset = PAGE_CACHE_SIZE; + int err; + + BUG_ON(!mapping); + inode = mapping->host; + BUG_ON(!inode); + + end_index = inode->i_size >> PAGE_CACHE_SHIFT; + + /* easy case */ + if (page->index < end_index) + goto do_it; + /* things got complicated... */ + offset = inode->i_size & (PAGE_CACHE_SIZE-1); + /* OK, are we completely out? */ + if (page->index >= end_index+1 || !offset) + return 0; /* truncated - don't care */ +do_it: + page_cache_get(page); + err = smb_writepage_sync(inode, page, 0, offset); + SetPageUptodate(page); + unlock_page(page); + page_cache_release(page); + return err; +} + +static int +smb_updatepage(struct file *file, struct page *page, unsigned long offset, + unsigned int count) +{ + struct dentry *dentry = file->f_path.dentry; + + DEBUG1("(%s/%s %d@%lld)\n", DENTRY_PATH(dentry), count, + ((unsigned long long)page->index << PAGE_CACHE_SHIFT) + offset); + + return smb_writepage_sync(dentry->d_inode, page, offset, count); +} + +static ssize_t +smb_file_aio_read(struct kiocb *iocb, const struct iovec *iov, + unsigned long nr_segs, loff_t pos) +{ + struct file * file = iocb->ki_filp; + struct dentry * dentry = file->f_path.dentry; + ssize_t status; + + VERBOSE("file %s/%s, count=%lu@%lu\n", DENTRY_PATH(dentry), + (unsigned long) iocb->ki_left, (unsigned long) pos); + + status = smb_revalidate_inode(dentry); + if (status) { + PARANOIA("%s/%s validation failed, error=%Zd\n", + DENTRY_PATH(dentry), status); + goto out; + } + + VERBOSE("before read, size=%ld, flags=%x, atime=%ld\n", + (long)dentry->d_inode->i_size, + dentry->d_inode->i_flags, dentry->d_inode->i_atime.tv_sec); + + status = generic_file_aio_read(iocb, iov, nr_segs, pos); +out: + return status; +} + +static int +smb_file_mmap(struct file * file, struct vm_area_struct * vma) +{ + struct dentry * dentry = file->f_path.dentry; + int status; + + VERBOSE("file %s/%s, address %lu - %lu\n", + DENTRY_PATH(dentry), vma->vm_start, vma->vm_end); + + status = smb_revalidate_inode(dentry); + if (status) { + PARANOIA("%s/%s validation failed, error=%d\n", + DENTRY_PATH(dentry), status); + goto out; + } + status = generic_file_mmap(file, vma); +out: + return status; +} + +static ssize_t +smb_file_splice_read(struct file *file, loff_t *ppos, + struct pipe_inode_info *pipe, size_t count, + unsigned int flags) +{ + struct dentry *dentry = file->f_path.dentry; + ssize_t status; + + VERBOSE("file %s/%s, pos=%Ld, count=%lu\n", + DENTRY_PATH(dentry), *ppos, count); + + status = smb_revalidate_inode(dentry); + if (status) { + PARANOIA("%s/%s validation failed, error=%Zd\n", + DENTRY_PATH(dentry), status); + goto out; + } + status = generic_file_splice_read(file, ppos, pipe, count, flags); +out: + return status; +} + +/* + * This does the "real" work of the write. The generic routine has + * allocated the page, locked it, done all the page alignment stuff + * calculations etc. Now we should just copy the data from user + * space and write it back to the real medium.. + * + * If the writer ends up delaying the write, the writer needs to + * increment the page use counts until he is done with the page. + */ +static int smb_write_begin(struct file *file, struct address_space *mapping, + loff_t pos, unsigned len, unsigned flags, + struct page **pagep, void **fsdata) +{ + pgoff_t index = pos >> PAGE_CACHE_SHIFT; + *pagep = grab_cache_page_write_begin(mapping, index, flags); + if (!*pagep) + return -ENOMEM; + return 0; +} + +static int smb_write_end(struct file *file, struct address_space *mapping, + loff_t pos, unsigned len, unsigned copied, + struct page *page, void *fsdata) +{ + int status; + unsigned offset = pos & (PAGE_CACHE_SIZE - 1); + + lock_kernel(); + status = smb_updatepage(file, page, offset, copied); + unlock_kernel(); + + if (!status) { + if (!PageUptodate(page) && copied == PAGE_CACHE_SIZE) + SetPageUptodate(page); + status = copied; + } + + unlock_page(page); + page_cache_release(page); + + return status; +} + +const struct address_space_operations smb_file_aops = { + .readpage = smb_readpage, + .writepage = smb_writepage, + .write_begin = smb_write_begin, + .write_end = smb_write_end, +}; + +/* + * Write to a file (through the page cache). + */ +static ssize_t +smb_file_aio_write(struct kiocb *iocb, const struct iovec *iov, + unsigned long nr_segs, loff_t pos) +{ + struct file * file = iocb->ki_filp; + struct dentry * dentry = file->f_path.dentry; + ssize_t result; + + VERBOSE("file %s/%s, count=%lu@%lu\n", + DENTRY_PATH(dentry), + (unsigned long) iocb->ki_left, (unsigned long) pos); + + result = smb_revalidate_inode(dentry); + if (result) { + PARANOIA("%s/%s validation failed, error=%Zd\n", + DENTRY_PATH(dentry), result); + goto out; + } + + result = smb_open(dentry, SMB_O_WRONLY); + if (result) + goto out; + + if (iocb->ki_left > 0) { + result = generic_file_aio_write(iocb, iov, nr_segs, pos); + VERBOSE("pos=%ld, size=%ld, mtime=%ld, atime=%ld\n", + (long) file->f_pos, (long) dentry->d_inode->i_size, + dentry->d_inode->i_mtime.tv_sec, + dentry->d_inode->i_atime.tv_sec); + } +out: + return result; +} + +static int +smb_file_open(struct inode *inode, struct file * file) +{ + int result; + struct dentry *dentry = file->f_path.dentry; + int smb_mode = (file->f_mode & O_ACCMODE) - 1; + + lock_kernel(); + result = smb_open(dentry, smb_mode); + if (result) + goto out; + SMB_I(inode)->openers++; +out: + unlock_kernel(); + return result; +} + +static int +smb_file_release(struct inode *inode, struct file * file) +{ + lock_kernel(); + if (!--SMB_I(inode)->openers) { + /* We must flush any dirty pages now as we won't be able to + write anything after close. mmap can trigger this. + "openers" should perhaps include mmap'ers ... */ + filemap_write_and_wait(inode->i_mapping); + smb_close(inode); + } + unlock_kernel(); + return 0; +} + +/* + * Check whether the required access is compatible with + * an inode's permission. SMB doesn't recognize superuser + * privileges, so we need our own check for this. + */ +static int +smb_file_permission(struct inode *inode, int mask) +{ + int mode = inode->i_mode; + int error = 0; + + VERBOSE("mode=%x, mask=%x\n", mode, mask); + + /* Look at user permissions */ + mode >>= 6; + if (mask & ~mode & (MAY_READ | MAY_WRITE | MAY_EXEC)) + error = -EACCES; + return error; +} + +static loff_t smb_remote_llseek(struct file *file, loff_t offset, int origin) +{ + loff_t ret; + lock_kernel(); + ret = generic_file_llseek_unlocked(file, offset, origin); + unlock_kernel(); + return ret; +} + +const struct file_operations smb_file_operations = +{ + .llseek = smb_remote_llseek, + .read = do_sync_read, + .aio_read = smb_file_aio_read, + .write = do_sync_write, + .aio_write = smb_file_aio_write, + .unlocked_ioctl = smb_ioctl, + .mmap = smb_file_mmap, + .open = smb_file_open, + .release = smb_file_release, + .fsync = smb_fsync, + .splice_read = smb_file_splice_read, +}; + +const struct inode_operations smb_file_inode_operations = +{ + .permission = smb_file_permission, + .getattr = smb_getattr, + .setattr = smb_notify_change, +}; diff --git a/drivers/staging/smbfs/getopt.c b/drivers/staging/smbfs/getopt.c new file mode 100644 index 000000000000..7ae0f5273ab1 --- /dev/null +++ b/drivers/staging/smbfs/getopt.c @@ -0,0 +1,64 @@ +/* + * getopt.c + */ + +#include +#include +#include + +#include "getopt.h" + +/** + * smb_getopt - option parser + * @caller: name of the caller, for error messages + * @options: the options string + * @opts: an array of &struct option entries controlling parser operations + * @optopt: output; will contain the current option + * @optarg: output; will contain the value (if one exists) + * @flag: output; may be NULL; should point to a long for or'ing flags + * @value: output; may be NULL; will be overwritten with the integer value + * of the current argument. + * + * Helper to parse options on the format used by mount ("a=b,c=d,e,f"). + * Returns opts->val if a matching entry in the 'opts' array is found, + * 0 when no more tokens are found, -1 if an error is encountered. + */ +int smb_getopt(char *caller, char **options, struct option *opts, + char **optopt, char **optarg, unsigned long *flag, + unsigned long *value) +{ + char *token; + char *val; + int i; + + do { + if ((token = strsep(options, ",")) == NULL) + return 0; + } while (*token == '\0'); + *optopt = token; + + *optarg = NULL; + if ((val = strchr (token, '=')) != NULL) { + *val++ = 0; + if (value) + *value = simple_strtoul(val, NULL, 0); + *optarg = val; + } + + for (i = 0; opts[i].name != NULL; i++) { + if (!strcmp(opts[i].name, token)) { + if (!opts[i].flag && (!val || !*val)) { + printk("%s: the %s option requires an argument\n", + caller, token); + return -1; + } + + if (flag && opts[i].flag) + *flag |= opts[i].flag; + + return opts[i].val; + } + } + printk("%s: Unrecognized mount option %s\n", caller, token); + return -1; +} diff --git a/drivers/staging/smbfs/getopt.h b/drivers/staging/smbfs/getopt.h new file mode 100644 index 000000000000..146219ac7c46 --- /dev/null +++ b/drivers/staging/smbfs/getopt.h @@ -0,0 +1,14 @@ +#ifndef _LINUX_GETOPT_H +#define _LINUX_GETOPT_H + +struct option { + const char *name; + unsigned long flag; + int val; +}; + +extern int smb_getopt(char *caller, char **options, struct option *opts, + char **optopt, char **optarg, unsigned long *flag, + unsigned long *value); + +#endif /* _LINUX_GETOPT_H */ diff --git a/drivers/staging/smbfs/inode.c b/drivers/staging/smbfs/inode.c new file mode 100644 index 000000000000..9287599ddb50 --- /dev/null +++ b/drivers/staging/smbfs/inode.c @@ -0,0 +1,839 @@ +/* + * inode.c + * + * Copyright (C) 1995, 1996 by Paal-Kr. Engstad and Volker Lendecke + * Copyright (C) 1997 by Volker Lendecke + * + * 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 +#include +#include +#include +#include +#include + +#include +#include + +#include "smb_fs.h" +#include "smbno.h" +#include "smb_mount.h" +#include "smb_debug.h" +#include "getopt.h" +#include "proto.h" + +/* Always pick a default string */ +#ifdef CONFIG_SMB_NLS_REMOTE +#define SMB_NLS_REMOTE CONFIG_SMB_NLS_REMOTE +#else +#define SMB_NLS_REMOTE "" +#endif + +#define SMB_TTL_DEFAULT 1000 + +static void smb_evict_inode(struct inode *); +static void smb_put_super(struct super_block *); +static int smb_statfs(struct dentry *, struct kstatfs *); +static int smb_show_options(struct seq_file *, struct vfsmount *); + +static struct kmem_cache *smb_inode_cachep; + +static struct inode *smb_alloc_inode(struct super_block *sb) +{ + struct smb_inode_info *ei; + ei = (struct smb_inode_info *)kmem_cache_alloc(smb_inode_cachep, GFP_KERNEL); + if (!ei) + return NULL; + return &ei->vfs_inode; +} + +static void smb_destroy_inode(struct inode *inode) +{ + kmem_cache_free(smb_inode_cachep, SMB_I(inode)); +} + +static void init_once(void *foo) +{ + struct smb_inode_info *ei = (struct smb_inode_info *) foo; + + inode_init_once(&ei->vfs_inode); +} + +static int init_inodecache(void) +{ + smb_inode_cachep = kmem_cache_create("smb_inode_cache", + sizeof(struct smb_inode_info), + 0, (SLAB_RECLAIM_ACCOUNT| + SLAB_MEM_SPREAD), + init_once); + if (smb_inode_cachep == NULL) + return -ENOMEM; + return 0; +} + +static void destroy_inodecache(void) +{ + kmem_cache_destroy(smb_inode_cachep); +} + +static int smb_remount(struct super_block *sb, int *flags, char *data) +{ + *flags |= MS_NODIRATIME; + return 0; +} + +static const struct super_operations smb_sops = +{ + .alloc_inode = smb_alloc_inode, + .destroy_inode = smb_destroy_inode, + .drop_inode = generic_delete_inode, + .evict_inode = smb_evict_inode, + .put_super = smb_put_super, + .statfs = smb_statfs, + .show_options = smb_show_options, + .remount_fs = smb_remount, +}; + + +/* We are always generating a new inode here */ +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); + + result = new_inode(sb); + if (!result) + return result; + result->i_ino = fattr->f_ino; + SMB_I(result)->open = 0; + SMB_I(result)->fileid = 0; + SMB_I(result)->access = 0; + SMB_I(result)->flags = 0; + SMB_I(result)->closed = 0; + SMB_I(result)->openers = 0; + smb_set_inode_attr(result, fattr); + if (S_ISREG(result->i_mode)) { + result->i_op = &smb_file_inode_operations; + result->i_fop = &smb_file_operations; + result->i_data.a_ops = &smb_file_aops; + } else if (S_ISDIR(result->i_mode)) { + 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; +} + +/* + * Copy the inode data to a smb_fattr structure. + */ +void +smb_get_inode_attr(struct inode *inode, struct smb_fattr *fattr) +{ + memset(fattr, 0, sizeof(struct smb_fattr)); + fattr->f_mode = inode->i_mode; + fattr->f_nlink = inode->i_nlink; + fattr->f_ino = inode->i_ino; + fattr->f_uid = inode->i_uid; + fattr->f_gid = inode->i_gid; + fattr->f_size = inode->i_size; + fattr->f_mtime = inode->i_mtime; + fattr->f_ctime = inode->i_ctime; + fattr->f_atime = inode->i_atime; + fattr->f_blocks = inode->i_blocks; + + fattr->attr = SMB_I(inode)->attr; + /* + * Keep the attributes in sync with the inode permissions. + */ + if (fattr->f_mode & S_IWUSR) + fattr->attr &= ~aRONLY; + else + fattr->attr |= aRONLY; +} + +/* + * Update the inode, possibly causing it to invalidate its pages if mtime/size + * is different from last time. + */ +void +smb_set_inode_attr(struct inode *inode, struct smb_fattr *fattr) +{ + struct smb_inode_info *ei = SMB_I(inode); + + /* + * A size change should have a different mtime, or same mtime + * but different size. + */ + time_t last_time = inode->i_mtime.tv_sec; + loff_t last_sz = inode->i_size; + + inode->i_mode = fattr->f_mode; + inode->i_nlink = fattr->f_nlink; + inode->i_uid = fattr->f_uid; + inode->i_gid = fattr->f_gid; + inode->i_ctime = fattr->f_ctime; + inode->i_blocks = fattr->f_blocks; + inode->i_size = fattr->f_size; + inode->i_mtime = fattr->f_mtime; + inode->i_atime = fattr->f_atime; + ei->attr = fattr->attr; + + /* + * Update the "last time refreshed" field for revalidation. + */ + ei->oldmtime = jiffies; + + if (inode->i_mtime.tv_sec != last_time || inode->i_size != last_sz) { + VERBOSE("%ld changed, old=%ld, new=%ld, oz=%ld, nz=%ld\n", + inode->i_ino, + (long) last_time, (long) inode->i_mtime.tv_sec, + (long) last_sz, (long) inode->i_size); + + if (!S_ISDIR(inode->i_mode)) + invalidate_remote_inode(inode); + } +} + +/* + * This is called if the connection has gone bad ... + * try to kill off all the current inodes. + */ +void +smb_invalidate_inodes(struct smb_sb_info *server) +{ + VERBOSE("\n"); + shrink_dcache_sb(SB_of(server)); + invalidate_inodes(SB_of(server)); +} + +/* + * This is called to update the inode attributes after + * we've made changes to a file or directory. + */ +static int +smb_refresh_inode(struct dentry *dentry) +{ + struct inode *inode = dentry->d_inode; + int error; + struct smb_fattr fattr; + + error = smb_proc_getattr(dentry, &fattr); + if (!error) { + smb_renew_times(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 { + /* + * Big trouble! The inode has become a new object, + * so any operations attempted on it are invalid. + * + * To limit damage, mark the inode as bad so that + * subsequent lookup validations will fail. + */ + PARANOIA("%s/%s changed mode, %07o to %07o\n", + DENTRY_PATH(dentry), + inode->i_mode, fattr.f_mode); + + fattr.f_mode = inode->i_mode; /* save mode */ + make_bad_inode(inode); + inode->i_mode = fattr.f_mode; /* restore mode */ + /* + * No need to worry about unhashing the dentry: the + * lookup validation will see that the inode is bad. + * But we do want to invalidate the caches ... + */ + if (!S_ISDIR(inode->i_mode)) + invalidate_remote_inode(inode); + else + smb_invalid_dir_cache(inode); + error = -EIO; + } + } + return error; +} + +/* + * This is called when we want to check whether the inode + * has changed on the server. If it has changed, we must + * invalidate our local caches. + */ +int +smb_revalidate_inode(struct dentry *dentry) +{ + struct smb_sb_info *s = server_from_dentry(dentry); + struct inode *inode = dentry->d_inode; + int error = 0; + + DEBUG1("smb_revalidate_inode\n"); + lock_kernel(); + + /* + * Check whether we've recently refreshed the inode. + */ + if (time_before(jiffies, SMB_I(inode)->oldmtime + SMB_MAX_AGE(s))) { + VERBOSE("up-to-date, ino=%ld, jiffies=%lu, oldtime=%lu\n", + inode->i_ino, jiffies, SMB_I(inode)->oldmtime); + goto out; + } + + error = smb_refresh_inode(dentry); +out: + unlock_kernel(); + return error; +} + +/* + * This routine is called when i_nlink == 0 and i_count goes to 0. + * All blocking cleanup operations need to go here to avoid races. + */ +static void +smb_evict_inode(struct inode *ino) +{ + DEBUG1("ino=%ld\n", ino->i_ino); + truncate_inode_pages(&ino->i_data, 0); + end_writeback(ino); + lock_kernel(); + if (smb_close(ino)) + PARANOIA("could not close inode %ld\n", ino->i_ino); + unlock_kernel(); +} + +static struct option opts[] = { + { "version", 0, 'v' }, + { "win95", SMB_MOUNT_WIN95, 1 }, + { "oldattr", SMB_MOUNT_OLDATTR, 1 }, + { "dirattr", SMB_MOUNT_DIRATTR, 1 }, + { "case", SMB_MOUNT_CASE, 1 }, + { "uid", 0, 'u' }, + { "gid", 0, 'g' }, + { "file_mode", 0, 'f' }, + { "dir_mode", 0, 'd' }, + { "iocharset", 0, 'i' }, + { "codepage", 0, 'c' }, + { "ttl", 0, 't' }, + { NULL, 0, 0} +}; + +static int +parse_options(struct smb_mount_data_kernel *mnt, char *options) +{ + int c; + unsigned long flags; + unsigned long value; + char *optarg; + char *optopt; + + flags = 0; + while ( (c = smb_getopt("smbfs", &options, opts, + &optopt, &optarg, &flags, &value)) > 0) { + + VERBOSE("'%s' -> '%s'\n", optopt, optarg ? optarg : ""); + switch (c) { + case 1: + /* got a "flag" option */ + break; + case 'v': + if (value != SMB_MOUNT_VERSION) { + printk ("smbfs: Bad mount version %ld, expected %d\n", + value, SMB_MOUNT_VERSION); + return 0; + } + mnt->version = value; + break; + case 'u': + mnt->uid = value; + flags |= SMB_MOUNT_UID; + break; + case 'g': + mnt->gid = value; + flags |= SMB_MOUNT_GID; + break; + case 'f': + mnt->file_mode = (value & S_IRWXUGO) | S_IFREG; + flags |= SMB_MOUNT_FMODE; + break; + case 'd': + mnt->dir_mode = (value & S_IRWXUGO) | S_IFDIR; + flags |= SMB_MOUNT_DMODE; + break; + case 'i': + strlcpy(mnt->codepage.local_name, optarg, + SMB_NLS_MAXNAMELEN); + break; + case 'c': + strlcpy(mnt->codepage.remote_name, optarg, + SMB_NLS_MAXNAMELEN); + break; + case 't': + mnt->ttl = value; + break; + default: + printk ("smbfs: Unrecognized mount option %s\n", + optopt); + return -1; + } + } + mnt->flags = flags; + return c; +} + +/* + * smb_show_options() is for displaying mount options in /proc/mounts. + * It tries to avoid showing settings that were not changed from their + * defaults. + */ +static int +smb_show_options(struct seq_file *s, struct vfsmount *m) +{ + struct smb_mount_data_kernel *mnt = SMB_SB(m->mnt_sb)->mnt; + int i; + + for (i = 0; opts[i].name != NULL; i++) + if (mnt->flags & opts[i].flag) + seq_printf(s, ",%s", opts[i].name); + + if (mnt->flags & SMB_MOUNT_UID) + seq_printf(s, ",uid=%d", mnt->uid); + if (mnt->flags & SMB_MOUNT_GID) + seq_printf(s, ",gid=%d", mnt->gid); + if (mnt->mounted_uid != 0) + seq_printf(s, ",mounted_uid=%d", mnt->mounted_uid); + + /* + * Defaults for file_mode and dir_mode are unknown to us; they + * depend on the current umask of the user doing the mount. + */ + if (mnt->flags & SMB_MOUNT_FMODE) + seq_printf(s, ",file_mode=%04o", mnt->file_mode & S_IRWXUGO); + if (mnt->flags & SMB_MOUNT_DMODE) + seq_printf(s, ",dir_mode=%04o", mnt->dir_mode & S_IRWXUGO); + + if (strcmp(mnt->codepage.local_name, CONFIG_NLS_DEFAULT)) + seq_printf(s, ",iocharset=%s", mnt->codepage.local_name); + if (strcmp(mnt->codepage.remote_name, SMB_NLS_REMOTE)) + seq_printf(s, ",codepage=%s", mnt->codepage.remote_name); + + if (mnt->ttl != SMB_TTL_DEFAULT) + seq_printf(s, ",ttl=%d", mnt->ttl); + + return 0; +} + +static void +smb_unload_nls(struct smb_sb_info *server) +{ + unload_nls(server->remote_nls); + unload_nls(server->local_nls); +} + +static void +smb_put_super(struct super_block *sb) +{ + struct smb_sb_info *server = SMB_SB(sb); + + lock_kernel(); + + smb_lock_server(server); + server->state = CONN_INVALID; + smbiod_unregister_server(server); + + smb_close_socket(server); + + if (server->conn_pid) + kill_pid(server->conn_pid, SIGTERM, 1); + + bdi_destroy(&server->bdi); + kfree(server->ops); + smb_unload_nls(server); + sb->s_fs_info = NULL; + smb_unlock_server(server); + put_pid(server->conn_pid); + kfree(server); + + unlock_kernel(); +} + +static int smb_fill_super(struct super_block *sb, void *raw_data, int silent) +{ + struct smb_sb_info *server; + struct smb_mount_data_kernel *mnt; + struct smb_mount_data *oldmnt; + struct inode *root_inode; + struct smb_fattr root; + int ver; + void *mem; + static int warn_count; + + if (warn_count < 5) { + warn_count++; + printk(KERN_EMERG "smbfs is deprecated and will be removed" + " from the 2.6.37 kernel. Please migrate to cifs\n"); + } + + if (!raw_data) + goto out_no_data; + + oldmnt = (struct smb_mount_data *) raw_data; + ver = oldmnt->version; + if (ver != SMB_MOUNT_OLDVERSION && cpu_to_be32(ver) != SMB_MOUNT_ASCII) + goto out_wrong_data; + + sb->s_flags |= MS_NODIRATIME; + sb->s_blocksize = 1024; /* Eh... Is this correct? */ + sb->s_blocksize_bits = 10; + sb->s_magic = SMB_SUPER_MAGIC; + sb->s_op = &smb_sops; + sb->s_time_gran = 100; + + server = kzalloc(sizeof(struct smb_sb_info), GFP_KERNEL); + if (!server) + goto out_no_server; + sb->s_fs_info = server; + + if (bdi_setup_and_register(&server->bdi, "smbfs", BDI_CAP_MAP_COPY)) + goto out_bdi; + + sb->s_bdi = &server->bdi; + + server->super_block = sb; + server->mnt = NULL; + server->sock_file = NULL; + init_waitqueue_head(&server->conn_wq); + init_MUTEX(&server->sem); + INIT_LIST_HEAD(&server->entry); + INIT_LIST_HEAD(&server->xmitq); + INIT_LIST_HEAD(&server->recvq); + server->conn_error = 0; + server->conn_pid = NULL; + server->state = CONN_INVALID; /* no connection yet */ + server->generation = 0; + + /* Allocate the global temp buffer and some superblock helper structs */ + /* FIXME: move these to the smb_sb_info struct */ + VERBOSE("alloc chunk = %lu\n", sizeof(struct smb_ops) + + sizeof(struct smb_mount_data_kernel)); + mem = kmalloc(sizeof(struct smb_ops) + + sizeof(struct smb_mount_data_kernel), GFP_KERNEL); + if (!mem) + goto out_no_mem; + + server->ops = mem; + smb_install_null_ops(server->ops); + server->mnt = mem + sizeof(struct smb_ops); + + /* Setup NLS stuff */ + server->remote_nls = NULL; + server->local_nls = NULL; + + mnt = server->mnt; + + memset(mnt, 0, sizeof(struct smb_mount_data_kernel)); + strlcpy(mnt->codepage.local_name, CONFIG_NLS_DEFAULT, + SMB_NLS_MAXNAMELEN); + strlcpy(mnt->codepage.remote_name, SMB_NLS_REMOTE, + SMB_NLS_MAXNAMELEN); + + mnt->ttl = SMB_TTL_DEFAULT; + if (ver == SMB_MOUNT_OLDVERSION) { + mnt->version = oldmnt->version; + + SET_UID(mnt->uid, oldmnt->uid); + SET_GID(mnt->gid, oldmnt->gid); + + mnt->file_mode = (oldmnt->file_mode & S_IRWXUGO) | S_IFREG; + mnt->dir_mode = (oldmnt->dir_mode & S_IRWXUGO) | S_IFDIR; + + mnt->flags = (oldmnt->file_mode >> 9) | SMB_MOUNT_UID | + SMB_MOUNT_GID | SMB_MOUNT_FMODE | SMB_MOUNT_DMODE; + } else { + mnt->file_mode = S_IRWXU | S_IRGRP | S_IXGRP | + S_IROTH | S_IXOTH | S_IFREG; + mnt->dir_mode = S_IRWXU | S_IRGRP | S_IXGRP | + S_IROTH | S_IXOTH | S_IFDIR; + if (parse_options(mnt, raw_data)) + goto out_bad_option; + } + mnt->mounted_uid = current_uid(); + smb_setcodepage(server, &mnt->codepage); + + /* + * Display the enabled options + * Note: smb_proc_getattr uses these in 2.4 (but was changed in 2.2) + */ + if (mnt->flags & SMB_MOUNT_OLDATTR) + printk("SMBFS: Using core getattr (Win 95 speedup)\n"); + else if (mnt->flags & SMB_MOUNT_DIRATTR) + printk("SMBFS: Using dir ff getattr\n"); + + if (smbiod_register_server(server) < 0) { + printk(KERN_ERR "smbfs: failed to start smbiod\n"); + goto out_no_smbiod; + } + + /* + * Keep the super block locked while we get the root inode. + */ + smb_init_root_dirent(server, &root, sb); + root_inode = smb_iget(sb, &root); + if (!root_inode) + goto out_no_root; + + sb->s_root = d_alloc_root(root_inode); + if (!sb->s_root) + goto out_no_root; + + smb_new_dentry(sb->s_root); + + return 0; + +out_no_root: + iput(root_inode); +out_no_smbiod: + smb_unload_nls(server); +out_bad_option: + kfree(mem); +out_no_mem: + bdi_destroy(&server->bdi); +out_bdi: + if (!server->mnt) + printk(KERN_ERR "smb_fill_super: allocation failure\n"); + sb->s_fs_info = NULL; + kfree(server); + goto out_fail; +out_wrong_data: + printk(KERN_ERR "smbfs: mount_data version %d is not supported\n", ver); + goto out_fail; +out_no_data: + printk(KERN_ERR "smb_fill_super: missing data argument\n"); +out_fail: + return -EINVAL; +out_no_server: + printk(KERN_ERR "smb_fill_super: cannot allocate struct smb_sb_info\n"); + return -ENOMEM; +} + +static int +smb_statfs(struct dentry *dentry, struct kstatfs *buf) +{ + int result; + + lock_kernel(); + + result = smb_proc_dskattr(dentry, buf); + + unlock_kernel(); + + buf->f_type = SMB_SUPER_MAGIC; + buf->f_namelen = SMB_MAXPATHLEN; + return result; +} + +int smb_getattr(struct vfsmount *mnt, struct dentry *dentry, struct kstat *stat) +{ + int err = smb_revalidate_inode(dentry); + if (!err) + generic_fillattr(dentry->d_inode, stat); + return err; +} + +int +smb_notify_change(struct dentry *dentry, struct iattr *attr) +{ + struct inode *inode = dentry->d_inode; + struct smb_sb_info *server = server_from_dentry(dentry); + unsigned int mask = (S_IFREG | S_IFDIR | S_IRWXUGO); + int error, changed, refresh = 0; + struct smb_fattr fattr; + + lock_kernel(); + + 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; + + 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); + + filemap_write_and_wait(inode->i_mapping); + + error = smb_open(dentry, O_WRONLY); + if (error) + goto out; + error = server->ops->truncate(inode, attr->ia_size); + if (error) + goto out; + truncate_setsize(inode, attr->ia_size); + 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 + * change time, so we don't attempt to change it. + */ + smb_get_inode_attr(inode, &fattr); + + 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) + 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; + +out: + if (refresh) + smb_refresh_inode(dentry); + unlock_kernel(); + return error; +} + +static int smb_get_sb(struct file_system_type *fs_type, + int flags, const char *dev_name, void *data, struct vfsmount *mnt) +{ + return get_sb_nodev(fs_type, flags, data, smb_fill_super, mnt); +} + +static struct file_system_type smb_fs_type = { + .owner = THIS_MODULE, + .name = "smbfs", + .get_sb = smb_get_sb, + .kill_sb = kill_anon_super, + .fs_flags = FS_BINARY_MOUNTDATA, +}; + +static int __init init_smb_fs(void) +{ + int err; + DEBUG1("registering ...\n"); + + err = init_inodecache(); + if (err) + goto out_inode; + err = smb_init_request_cache(); + if (err) + goto out_request; + err = register_filesystem(&smb_fs_type); + if (err) + goto out; + return 0; +out: + smb_destroy_request_cache(); +out_request: + destroy_inodecache(); +out_inode: + return err; +} + +static void __exit exit_smb_fs(void) +{ + DEBUG1("unregistering ...\n"); + unregister_filesystem(&smb_fs_type); + smb_destroy_request_cache(); + destroy_inodecache(); +} + +module_init(init_smb_fs) +module_exit(exit_smb_fs) +MODULE_LICENSE("GPL"); diff --git a/drivers/staging/smbfs/ioctl.c b/drivers/staging/smbfs/ioctl.c new file mode 100644 index 000000000000..2da169267470 --- /dev/null +++ b/drivers/staging/smbfs/ioctl.c @@ -0,0 +1,68 @@ +/* + * ioctl.c + * + * Copyright (C) 1995, 1996 by Volker Lendecke + * Copyright (C) 1997 by Volker Lendecke + * + * Please add a note about your changes to smbfs in the ChangeLog file. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "smb_fs.h" +#include "smb_mount.h" +#include "proto.h" + +long +smb_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) +{ + struct smb_sb_info *server = server_from_inode(filp->f_path.dentry->d_inode); + struct smb_conn_opt opt; + int result = -EINVAL; + + lock_kernel(); + switch (cmd) { + uid16_t uid16; + uid_t uid32; + case SMB_IOC_GETMOUNTUID: + SET_UID(uid16, server->mnt->mounted_uid); + result = put_user(uid16, (uid16_t __user *) arg); + break; + case SMB_IOC_GETMOUNTUID32: + SET_UID(uid32, server->mnt->mounted_uid); + result = put_user(uid32, (uid_t __user *) arg); + break; + + case SMB_IOC_NEWCONN: + /* arg is smb_conn_opt, or NULL if no connection was made */ + if (!arg) { + result = 0; + smb_lock_server(server); + server->state = CONN_RETRIED; + printk(KERN_ERR "Connection attempt failed! [%d]\n", + server->conn_error); + smbiod_flush(server); + smb_unlock_server(server); + break; + } + + result = -EFAULT; + if (!copy_from_user(&opt, (void __user *)arg, sizeof(opt))) + result = smb_newconn(server, &opt); + break; + default: + break; + } + unlock_kernel(); + + return result; +} diff --git a/drivers/staging/smbfs/proc.c b/drivers/staging/smbfs/proc.c new file mode 100644 index 000000000000..2fb079c37f25 --- /dev/null +++ b/drivers/staging/smbfs/proc.c @@ -0,0 +1,3506 @@ +/* + * proc.c + * + * Copyright (C) 1995, 1996 by Paal-Kr. Engstad and Volker Lendecke + * Copyright (C) 1997 by Volker Lendecke + * + * 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 + +#include +#include + +#include "smb_fs.h" +#include "smbno.h" +#include "smb_mount.h" +#include "smb_debug.h" +#include "proto.h" +#include "request.h" + + +/* Features. Undefine if they cause problems, this should perhaps be a + config option. */ +#define SMBFS_POSIX_UNLINK 1 + +/* Allow smb_retry to be interrupted. */ +#define SMB_RETRY_INTR + +#define SMB_VWV(packet) ((packet) + SMB_HEADER_LEN) +#define SMB_CMD(packet) (*(packet+8)) +#define SMB_WCT(packet) (*(packet+SMB_HEADER_LEN - 1)) + +#define SMB_DIRINFO_SIZE 43 +#define SMB_STATUS_SIZE 21 + +#define SMB_ST_BLKSIZE (PAGE_SIZE) +#define SMB_ST_BLKSHIFT (PAGE_SHIFT) + +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 struct smb_ops smb_ops_null; + +static void +smb_init_dirent(struct smb_sb_info *server, struct smb_fattr *fattr); +static void +smb_finish_dirent(struct smb_sb_info *server, struct smb_fattr *fattr); +static int +smb_proc_getattr_core(struct smb_sb_info *server, struct dentry *dir, + struct smb_fattr *fattr); +static int +smb_proc_getattr_ff(struct smb_sb_info *server, struct dentry *dentry, + struct smb_fattr *fattr); +static int +smb_proc_setattr_core(struct smb_sb_info *server, struct dentry *dentry, + u16 attr); +static int +smb_proc_setattr_ext(struct smb_sb_info *server, + struct inode *inode, struct smb_fattr *fattr); +static int +smb_proc_query_cifsunix(struct smb_sb_info *server); +static void +install_ops(struct smb_ops *dst, struct smb_ops *src); + + +static void +str_upper(char *name, int len) +{ + while (len--) + { + if (*name >= 'a' && *name <= 'z') + *name -= ('a' - 'A'); + name++; + } +} + +#if 0 +static void +str_lower(char *name, int len) +{ + while (len--) + { + if (*name >= 'A' && *name <= 'Z') + *name += ('a' - 'A'); + name++; + } +} +#endif + +/* reverse a string inline. This is used by the dircache walking routines */ +static void reverse_string(char *buf, int len) +{ + char c; + char *end = buf+len-1; + + while(buf < end) { + c = *buf; + *(buf++) = *end; + *(end--) = c; + } +} + +/* no conversion, just a wrapper for memcpy. */ +static int convert_memcpy(unsigned char *output, int olen, + const unsigned char *input, int ilen, + struct nls_table *nls_from, + struct nls_table *nls_to) +{ + if (olen < ilen) + return -ENAMETOOLONG; + memcpy(output, input, ilen); + return ilen; +} + +static inline int write_char(unsigned char ch, char *output, int olen) +{ + if (olen < 4) + return -ENAMETOOLONG; + sprintf(output, ":x%02x", ch); + return 4; +} + +static inline int write_unichar(wchar_t ch, char *output, int olen) +{ + if (olen < 5) + return -ENAMETOOLONG; + sprintf(output, ":%04x", ch); + return 5; +} + +/* convert from one "codepage" to another (possibly being utf8). */ +static int convert_cp(unsigned char *output, int olen, + const unsigned char *input, int ilen, + struct nls_table *nls_from, + struct nls_table *nls_to) +{ + int len = 0; + int n; + wchar_t ch; + + while (ilen > 0) { + /* convert by changing to unicode and back to the new cp */ + n = nls_from->char2uni(input, ilen, &ch); + if (n == -EINVAL) { + ilen--; + n = write_char(*input++, output, olen); + if (n < 0) + goto fail; + output += n; + olen -= n; + len += n; + continue; + } else if (n < 0) + goto fail; + input += n; + ilen -= n; + + n = nls_to->uni2char(ch, output, olen); + if (n == -EINVAL) + n = write_unichar(ch, output, olen); + if (n < 0) + goto fail; + output += n; + olen -= n; + + len += n; + } + return len; +fail: + return n; +} + +/* ----------------------------------------------------------- */ + +/* + * nls_unicode + * + * This encodes/decodes little endian unicode format + */ + +static int uni2char(wchar_t uni, unsigned char *out, int boundlen) +{ + if (boundlen < 2) + return -EINVAL; + *out++ = uni & 0xff; + *out++ = uni >> 8; + return 2; +} + +static int char2uni(const unsigned char *rawstring, int boundlen, wchar_t *uni) +{ + if (boundlen < 2) + return -EINVAL; + *uni = (rawstring[1] << 8) | rawstring[0]; + return 2; +} + +static struct nls_table unicode_table = { + .charset = "unicode", + .uni2char = uni2char, + .char2uni = char2uni, +}; + +/* ----------------------------------------------------------- */ + +static int setcodepage(struct nls_table **p, char *name) +{ + struct nls_table *nls; + + if (!name || !*name) { + nls = NULL; + } else if ( (nls = load_nls(name)) == NULL) { + printk (KERN_ERR "smbfs: failed to load nls '%s'\n", name); + return -EINVAL; + } + + /* if already set, unload the previous one. */ + if (*p && *p != &unicode_table) + unload_nls(*p); + *p = nls; + + return 0; +} + +/* Handles all changes to codepage settings. */ +int smb_setcodepage(struct smb_sb_info *server, struct smb_nls_codepage *cp) +{ + int n = 0; + + smb_lock_server(server); + + /* Don't load any nls_* at all, if no remote is requested */ + if (!*cp->remote_name) + goto out; + + /* local */ + n = setcodepage(&server->local_nls, cp->local_name); + if (n != 0) + goto out; + + /* remote */ + if (!strcmp(cp->remote_name, "unicode")) { + server->remote_nls = &unicode_table; + } else { + n = setcodepage(&server->remote_nls, cp->remote_name); + if (n != 0) + setcodepage(&server->local_nls, NULL); + } + +out: + if (server->local_nls != NULL && server->remote_nls != NULL) + server->ops->convert = convert_cp; + else + server->ops->convert = convert_memcpy; + + smb_unlock_server(server); + return n; +} + + +/*****************************************************************************/ +/* */ +/* Encoding/Decoding section */ +/* */ +/*****************************************************************************/ + +static __u8 * +smb_encode_smb_length(__u8 * p, __u32 len) +{ + *p = 0; + *(p+1) = 0; + *(p+2) = (len & 0xFF00) >> 8; + *(p+3) = (len & 0xFF); + if (len > 0xFFFF) + { + *(p+1) = 1; + } + return p + 4; +} + +/* + * smb_build_path: build the path to entry and name storing it in buf. + * The path returned will have the trailing '\0'. + */ +static int smb_build_path(struct smb_sb_info *server, unsigned char *buf, + int maxlen, + struct dentry *entry, struct qstr *name) +{ + unsigned char *path = buf; + int len; + int unicode = (server->mnt->flags & SMB_MOUNT_UNICODE) != 0; + + if (maxlen < (2< SMB_MAXPATHLEN + 1) + maxlen = SMB_MAXPATHLEN + 1; + + if (entry == NULL) + goto test_name_and_out; + + /* + * If IS_ROOT, we have to do no walking at all. + */ + if (IS_ROOT(entry) && !name) { + *path++ = '\\'; + if (unicode) *path++ = '\0'; + *path++ = '\0'; + if (unicode) *path++ = '\0'; + return path-buf; + } + + /* + * Build the path string walking the tree backward from end to ROOT + * and store it in reversed order [see reverse_string()] + */ + dget(entry); + spin_lock(&entry->d_lock); + while (!IS_ROOT(entry)) { + struct dentry *parent; + + if (maxlen < (3<d_lock); + dput(entry); + return -ENAMETOOLONG; + } + + len = server->ops->convert(path, maxlen-2, + entry->d_name.name, entry->d_name.len, + server->local_nls, server->remote_nls); + if (len < 0) { + spin_unlock(&entry->d_lock); + dput(entry); + return len; + } + reverse_string(path, len); + path += len; + if (unicode) { + /* Note: reverse order */ + *path++ = '\0'; + maxlen--; + } + *path++ = '\\'; + maxlen -= len+1; + + parent = entry->d_parent; + dget(parent); + spin_unlock(&entry->d_lock); + dput(entry); + entry = parent; + spin_lock(&entry->d_lock); + } + spin_unlock(&entry->d_lock); + dput(entry); + reverse_string(buf, path-buf); + + /* maxlen has space for at least one char */ +test_name_and_out: + if (name) { + if (maxlen < (3<ops->convert(path, maxlen-2, + name->name, name->len, + server->local_nls, server->remote_nls); + if (len < 0) + return len; + path += len; + maxlen -= len+1; + } + /* maxlen has space for at least one char */ + *path++ = '\0'; + if (unicode) *path++ = '\0'; + return path-buf; +} + +static int smb_encode_path(struct smb_sb_info *server, char *buf, int maxlen, + struct dentry *dir, struct qstr *name) +{ + int result; + + result = smb_build_path(server, buf, maxlen, dir, name); + if (result < 0) + goto out; + if (server->opt.protocol <= SMB_PROTOCOL_COREPLUS) + str_upper(buf, result); +out: + return result; +} + +/* encode_path for non-trans2 request SMBs */ +static int smb_simple_encode_path(struct smb_request *req, char **p, + struct dentry * entry, struct qstr * name) +{ + struct smb_sb_info *server = req->rq_server; + char *s = *p; + int res; + int maxlen = ((char *)req->rq_buffer + req->rq_bufsize) - s; + int unicode = (server->mnt->flags & SMB_MOUNT_UNICODE); + + if (!maxlen) + return -ENAMETOOLONG; + *s++ = 4; /* ASCII data format */ + + /* + * SMB Unicode strings must be 16bit aligned relative the start of the + * packet. If they are not they must be padded with 0. + */ + if (unicode) { + int align = s - (char *)req->rq_buffer; + if (!(align & 1)) { + *s++ = '\0'; + maxlen--; + } + } + + res = smb_encode_path(server, s, maxlen-1, entry, name); + if (res < 0) + return res; + *p = s + res; + return 0; +} + +/* The following are taken directly from msdos-fs */ + +/* Linear day numbers of the respective 1sts in non-leap years. */ + +static int day_n[] = +{0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 0, 0, 0, 0}; + /* JanFebMarApr May Jun Jul Aug Sep Oct Nov Dec */ + + +static time_t +utc2local(struct smb_sb_info *server, time_t time) +{ + return time - server->opt.serverzone*60; +} + +static time_t +local2utc(struct smb_sb_info *server, time_t time) +{ + return time + server->opt.serverzone*60; +} + +/* Convert a MS-DOS time/date pair to a UNIX date (seconds since 1 1 70). */ + +static time_t +date_dos2unix(struct smb_sb_info *server, __u16 date, __u16 time) +{ + int month, year; + time_t secs; + + /* first subtract and mask after that... Otherwise, if + date == 0, bad things happen */ + month = ((date >> 5) - 1) & 15; + year = date >> 9; + secs = (time & 31) * 2 + 60 * ((time >> 5) & 63) + (time >> 11) * 3600 + 86400 * + ((date & 31) - 1 + day_n[month] + (year / 4) + year * 365 - ((year & 3) == 0 && + month < 2 ? 1 : 0) + 3653); + /* days since 1.1.70 plus 80's leap day */ + return local2utc(server, secs); +} + + +/* Convert linear UNIX date to a MS-DOS time/date pair. */ + +static void +date_unix2dos(struct smb_sb_info *server, + int unix_date, __u16 *date, __u16 *time) +{ + int day, year, nl_day, month; + + unix_date = utc2local(server, unix_date); + if (unix_date < 315532800) + unix_date = 315532800; + + *time = (unix_date % 60) / 2 + + (((unix_date / 60) % 60) << 5) + + (((unix_date / 3600) % 24) << 11); + + day = unix_date / 86400 - 3652; + year = day / 365; + if ((year + 3) / 4 + 365 * year > day) + year--; + day -= (year + 3) / 4 + 365 * year; + if (day == 59 && !(year & 3)) { + nl_day = day; + month = 2; + } else { + nl_day = (year & 3) || day <= 59 ? day : day - 1; + for (month = 1; month < 12; month++) + if (day_n[month] > nl_day) + break; + } + *date = nl_day - day_n[month - 1] + 1 + (month << 5) + (year << 9); +} + +/* The following are taken from fs/ntfs/util.c */ + +#define NTFS_TIME_OFFSET ((u64)(369*365 + 89) * 24 * 3600 * 10000000) + +/* + * Convert the NT UTC (based 1601-01-01, in hundred nanosecond units) + * into Unix UTC (based 1970-01-01, in seconds). + */ +static struct timespec +smb_ntutc2unixutc(u64 ntutc) +{ + struct timespec ts; + /* FIXME: what about the timezone difference? */ + /* Subtract the NTFS time offset, then convert to 1s intervals. */ + u64 t = ntutc - NTFS_TIME_OFFSET; + ts.tv_nsec = do_div(t, 10000000) * 100; + ts.tv_sec = t; + return ts; +} + +/* Convert the Unix UTC into NT time */ +static u64 +smb_unixutc2ntutc(struct timespec ts) +{ + /* Note: timezone conversion is probably wrong. */ + /* return ((u64)utc2local(server, t)) * 10000000 + NTFS_TIME_OFFSET; */ + return ((u64)ts.tv_sec) * 10000000 + ts.tv_nsec/100 + 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 (S_ISREG(mode)) + return UNIX_TYPE_FILE; + if (S_ISDIR(mode)) + return UNIX_TYPE_DIR; + if (S_ISLNK(mode)) + return UNIX_TYPE_SYMLINK; + if (S_ISCHR(mode)) + return UNIX_TYPE_CHARDEV; + if (S_ISBLK(mode)) + return UNIX_TYPE_BLKDEV; + if (S_ISFIFO(mode)) + return UNIX_TYPE_FIFO; + if (S_ISSOCK(mode)) + return UNIX_TYPE_SOCKET; + return UNIX_TYPE_UNKNOWN; +} + + +/*****************************************************************************/ +/* */ +/* Support section. */ +/* */ +/*****************************************************************************/ + +__u32 +smb_len(__u8 * p) +{ + return ((*(p+1) & 0x1) << 16L) | (*(p+2) << 8L) | *(p+3); +} + +static __u16 +smb_bcc(__u8 * packet) +{ + int pos = SMB_HEADER_LEN + SMB_WCT(packet) * sizeof(__u16); + return WVAL(packet, pos); +} + +/* smb_valid_packet: We check if packet fulfills the basic + requirements of a smb packet */ + +static int +smb_valid_packet(__u8 * packet) +{ + return (packet[4] == 0xff + && packet[5] == 'S' + && packet[6] == 'M' + && packet[7] == 'B' + && (smb_len(packet) + 4 == SMB_HEADER_LEN + + SMB_WCT(packet) * 2 + smb_bcc(packet))); +} + +/* smb_verify: We check if we got the answer we expected, and if we + got enough data. If bcc == -1, we don't care. */ + +static int +smb_verify(__u8 * packet, int command, int wct, int bcc) +{ + if (SMB_CMD(packet) != command) + goto bad_command; + if (SMB_WCT(packet) < wct) + goto bad_wct; + if (bcc != -1 && smb_bcc(packet) < bcc) + goto bad_bcc; + return 0; + +bad_command: + printk(KERN_ERR "smb_verify: command=%x, SMB_CMD=%x??\n", + command, SMB_CMD(packet)); + goto fail; +bad_wct: + printk(KERN_ERR "smb_verify: command=%x, wct=%d, SMB_WCT=%d??\n", + command, wct, SMB_WCT(packet)); + goto fail; +bad_bcc: + printk(KERN_ERR "smb_verify: command=%x, bcc=%d, SMB_BCC=%d??\n", + command, bcc, smb_bcc(packet)); +fail: + return -EIO; +} + +/* + * Returns the maximum read or write size for the "payload". Making all of the + * packet fit within the negotiated max_xmit size. + * + * N.B. Since this value is usually computed before locking the server, + * the server's packet size must never be decreased! + */ +static inline int +smb_get_xmitsize(struct smb_sb_info *server, int overhead) +{ + return server->opt.max_xmit - overhead; +} + +/* + * Calculate the maximum read size + */ +int +smb_get_rsize(struct smb_sb_info *server) +{ + /* readX has 12 parameters, read has 5 */ + int overhead = SMB_HEADER_LEN + 12 * sizeof(__u16) + 2 + 1 + 2; + int size = smb_get_xmitsize(server, overhead); + + VERBOSE("xmit=%d, size=%d\n", server->opt.max_xmit, size); + + return size; +} + +/* + * Calculate the maximum write size + */ +int +smb_get_wsize(struct smb_sb_info *server) +{ + /* writeX has 14 parameters, write has 5 */ + int overhead = SMB_HEADER_LEN + 14 * sizeof(__u16) + 2 + 1 + 2; + int size = smb_get_xmitsize(server, overhead); + + VERBOSE("xmit=%d, size=%d\n", server->opt.max_xmit, size); + + return size; +} + +/* + * Convert SMB error codes to -E... errno values. + */ +int +smb_errno(struct smb_request *req) +{ + int errcls = req->rq_rcls; + int error = req->rq_err; + char *class = "Unknown"; + + VERBOSE("errcls %d code %d from command 0x%x\n", + errcls, error, SMB_CMD(req->rq_header)); + + if (errcls == ERRDOS) { + switch (error) { + case ERRbadfunc: + return -EINVAL; + case ERRbadfile: + case ERRbadpath: + return -ENOENT; + case ERRnofids: + return -EMFILE; + case ERRnoaccess: + return -EACCES; + case ERRbadfid: + return -EBADF; + case ERRbadmcb: + return -EREMOTEIO; + case ERRnomem: + return -ENOMEM; + case ERRbadmem: + return -EFAULT; + case ERRbadenv: + case ERRbadformat: + return -EREMOTEIO; + case ERRbadaccess: + return -EACCES; + case ERRbaddata: + return -E2BIG; + case ERRbaddrive: + return -ENXIO; + case ERRremcd: + return -EREMOTEIO; + case ERRdiffdevice: + return -EXDEV; + case ERRnofiles: + return -ENOENT; + case ERRbadshare: + return -ETXTBSY; + case ERRlock: + return -EDEADLK; + case ERRfilexists: + return -EEXIST; + case ERROR_INVALID_PARAMETER: + return -EINVAL; + case ERROR_DISK_FULL: + return -ENOSPC; + case ERROR_INVALID_NAME: + return -ENOENT; + case ERROR_DIR_NOT_EMPTY: + return -ENOTEMPTY; + case ERROR_NOT_LOCKED: + return -ENOLCK; + case ERROR_ALREADY_EXISTS: + return -EEXIST; + default: + class = "ERRDOS"; + goto err_unknown; + } + } else if (errcls == ERRSRV) { + switch (error) { + /* N.B. This is wrong ... EIO ? */ + case ERRerror: + return -ENFILE; + case ERRbadpw: + return -EINVAL; + case ERRbadtype: + case ERRtimeout: + return -EIO; + case ERRaccess: + return -EACCES; + /* + * This is a fatal error, as it means the "tree ID" + * for this connection is no longer valid. We map + * to a special error code and get a new connection. + */ + case ERRinvnid: + return -EBADSLT; + default: + class = "ERRSRV"; + goto err_unknown; + } + } else if (errcls == ERRHRD) { + switch (error) { + case ERRnowrite: + return -EROFS; + case ERRbadunit: + return -ENODEV; + case ERRnotready: + return -EUCLEAN; + case ERRbadcmd: + case ERRdata: + return -EIO; + case ERRbadreq: + return -ERANGE; + case ERRbadshare: + return -ETXTBSY; + case ERRlock: + return -EDEADLK; + case ERRdiskfull: + return -ENOSPC; + default: + class = "ERRHRD"; + goto err_unknown; + } + } else if (errcls == ERRCMD) { + class = "ERRCMD"; + } else if (errcls == SUCCESS) { + return 0; /* This is the only valid 0 return */ + } + +err_unknown: + printk(KERN_ERR "smb_errno: class %s, code %d from command 0x%x\n", + class, error, SMB_CMD(req->rq_header)); + return -EIO; +} + +/* smb_request_ok: We expect the server to be locked. Then we do the + request and check the answer completely. When smb_request_ok + returns 0, you can be quite sure that everything went well. When + the answer is <=0, the returned number is a valid unix errno. */ + +static int +smb_request_ok(struct smb_request *req, int command, int wct, int bcc) +{ + int result; + + req->rq_resp_wct = wct; + req->rq_resp_bcc = bcc; + + result = smb_add_request(req); + if (result != 0) { + DEBUG1("smb_request failed\n"); + goto out; + } + + if (smb_valid_packet(req->rq_header) != 0) { + PARANOIA("invalid packet!\n"); + goto out; + } + + result = smb_verify(req->rq_header, command, wct, bcc); + +out: + return result; +} + +/* + * This implements the NEWCONN ioctl. It installs the server pid, + * sets server->state to CONN_VALID, and wakes up the waiting process. + */ +int +smb_newconn(struct smb_sb_info *server, struct smb_conn_opt *opt) +{ + struct file *filp; + struct sock *sk; + int error; + + VERBOSE("fd=%d, pid=%d\n", opt->fd, current->pid); + + smb_lock_server(server); + + /* + * Make sure we don't already have a valid connection ... + */ + error = -EINVAL; + if (server->state == CONN_VALID) + goto out; + + error = -EACCES; + if (current_uid() != server->mnt->mounted_uid && + !capable(CAP_SYS_ADMIN)) + goto out; + + error = -EBADF; + filp = fget(opt->fd); + if (!filp) + goto out; + if (!smb_valid_socket(filp->f_path.dentry->d_inode)) + goto out_putf; + + server->sock_file = filp; + server->conn_pid = get_pid(task_pid(current)); + server->opt = *opt; + server->generation += 1; + server->state = CONN_VALID; + error = 0; + + if (server->conn_error) { + /* + * conn_error is the returncode we originally decided to + * drop the old connection on. This message should be positive + * and not make people ask questions on why smbfs is printing + * error messages ... + */ + printk(KERN_INFO "SMB connection re-established (%d)\n", + server->conn_error); + server->conn_error = 0; + } + + /* + * Store the server in sock user_data (Only used by sunrpc) + */ + sk = SOCKET_I(filp->f_path.dentry->d_inode)->sk; + sk->sk_user_data = server; + + /* chain into the data_ready callback */ + server->data_ready = xchg(&sk->sk_data_ready, smb_data_ready); + + /* check if we have an old smbmount that uses seconds for the + serverzone */ + if (server->opt.serverzone > 12*60 || server->opt.serverzone < -12*60) + server->opt.serverzone /= 60; + + /* now that we have an established connection we can detect the server + type and enable bug workarounds */ + if (server->opt.protocol < SMB_PROTOCOL_LANMAN2) + install_ops(server->ops, &smb_ops_core); + else if (server->opt.protocol == SMB_PROTOCOL_LANMAN2) + install_ops(server->ops, &smb_ops_os2); + else if (server->opt.protocol == SMB_PROTOCOL_NT1 && + (server->opt.max_xmit < 0x1000) && + !(server->opt.capabilities & SMB_CAP_NT_SMBS)) { + /* FIXME: can we kill the WIN95 flag now? */ + server->mnt->flags |= SMB_MOUNT_WIN95; + VERBOSE("detected WIN95 server\n"); + install_ops(server->ops, &smb_ops_win95); + } else { + /* + * Samba has max_xmit 65535 + * NT4spX has max_xmit 4536 (or something like that) + * win2k has ... + */ + VERBOSE("detected NT1 (Samba, NT4/5) server\n"); + install_ops(server->ops, &smb_ops_winNT); + } + + /* FIXME: the win9x code wants to modify these ... (seek/trunc bug) */ + if (server->mnt->flags & SMB_MOUNT_OLDATTR) { + server->ops->getattr = smb_proc_getattr_core; + } else if (server->mnt->flags & SMB_MOUNT_DIRATTR) { + server->ops->getattr = smb_proc_getattr_ff; + } + + /* Decode server capabilities */ + if (server->opt.capabilities & SMB_CAP_LARGE_FILES) { + /* Should be ok to set this now, as no one can access the + mount until the connection has been established. */ + SB_of(server)->s_maxbytes = ~0ULL >> 1; + VERBOSE("LFS enabled\n"); + } + if (server->opt.capabilities & SMB_CAP_UNICODE) { + server->mnt->flags |= SMB_MOUNT_UNICODE; + VERBOSE("Unicode enabled\n"); + } else { + server->mnt->flags &= ~SMB_MOUNT_UNICODE; + } +#if 0 + /* flags we may test for other patches ... */ + if (server->opt.capabilities & SMB_CAP_LARGE_READX) { + VERBOSE("Large reads enabled\n"); + } + if (server->opt.capabilities & SMB_CAP_LARGE_WRITEX) { + 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, + pid_nr(server->conn_pid), server->opt.capabilities); + + /* FIXME: this really should be done by smbmount. */ + if (server->opt.max_xmit > SMB_MAX_PACKET_SIZE) { + 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); + + server->conn_complete++; + wake_up_interruptible_all(&server->conn_wq); + return error; + +out: + smb_unlock_server(server); + smbiod_wake_up(); + return error; + +out_putf: + fput(filp); + goto out; +} + +/* smb_setup_header: We completely set up the packet. You only have to + insert the command-specific fields */ + +__u8 * +smb_setup_header(struct smb_request *req, __u8 command, __u16 wct, __u16 bcc) +{ + __u32 xmit_len = SMB_HEADER_LEN + wct * sizeof(__u16) + bcc + 2; + __u8 *p = req->rq_header; + struct smb_sb_info *server = req->rq_server; + + p = smb_encode_smb_length(p, xmit_len - 4); + + *p++ = 0xff; + *p++ = 'S'; + *p++ = 'M'; + *p++ = 'B'; + *p++ = command; + + memset(p, '\0', 19); + p += 19; + p += 8; + + if (server->opt.protocol > SMB_PROTOCOL_CORE) { + int flags = SMB_FLAGS_CASELESS_PATHNAMES; + int flags2 = SMB_FLAGS2_LONG_PATH_COMPONENTS | + SMB_FLAGS2_EXTENDED_ATTRIBUTES; /* EA? not really ... */ + + *(req->rq_header + smb_flg) = flags; + if (server->mnt->flags & SMB_MOUNT_UNICODE) + flags2 |= SMB_FLAGS2_UNICODE_STRINGS; + WSET(req->rq_header, smb_flg2, flags2); + } + *p++ = wct; /* wct */ + p += 2 * wct; + WSET(p, 0, bcc); + + /* Include the header in the data to send */ + req->rq_iovlen = 1; + req->rq_iov[0].iov_base = req->rq_header; + req->rq_iov[0].iov_len = xmit_len - bcc; + + return req->rq_buffer; +} + +static void +smb_setup_bcc(struct smb_request *req, __u8 *p) +{ + u16 bcc = p - req->rq_buffer; + u8 *pbcc = req->rq_header + SMB_HEADER_LEN + 2*SMB_WCT(req->rq_header); + + WSET(pbcc, 0, bcc); + + smb_encode_smb_length(req->rq_header, SMB_HEADER_LEN + + 2*SMB_WCT(req->rq_header) - 2 + bcc); + + /* Include the "bytes" in the data to send */ + req->rq_iovlen = 2; + req->rq_iov[1].iov_base = req->rq_buffer; + req->rq_iov[1].iov_len = bcc; +} + +static int +smb_proc_seek(struct smb_sb_info *server, __u16 fileid, + __u16 mode, off_t offset) +{ + int result; + struct smb_request *req; + + result = -ENOMEM; + if (! (req = smb_alloc_request(server, 0))) + goto out; + + smb_setup_header(req, SMBlseek, 4, 0); + WSET(req->rq_header, smb_vwv0, fileid); + WSET(req->rq_header, smb_vwv1, mode); + DSET(req->rq_header, smb_vwv2, offset); + req->rq_flags |= SMB_REQ_NORETRY; + + result = smb_request_ok(req, SMBlseek, 2, 0); + if (result < 0) { + result = 0; + goto out_free; + } + + result = DVAL(req->rq_header, smb_vwv0); +out_free: + smb_rput(req); +out: + return result; +} + +static int +smb_proc_open(struct smb_sb_info *server, struct dentry *dentry, int wish) +{ + struct inode *ino = dentry->d_inode; + struct smb_inode_info *ei = SMB_I(ino); + int mode, read_write = 0x42, read_only = 0x40; + int res; + char *p; + struct smb_request *req; + + /* + * Attempt to open r/w, unless there are no write privileges. + */ + mode = read_write; + if (!(ino->i_mode & (S_IWUSR | S_IWGRP | S_IWOTH))) + mode = read_only; +#if 0 + /* FIXME: why is this code not in? below we fix it so that a caller + wanting RO doesn't get RW. smb_revalidate_inode does some + optimization based on access mode. tail -f needs it to be correct. + + We must open rw since we don't do the open if called a second time + with different 'wish'. Is that not supported by smb servers? */ + if (!(wish & (O_WRONLY | O_RDWR))) + mode = read_only; +#endif + + res = -ENOMEM; + if (! (req = smb_alloc_request(server, PAGE_SIZE))) + goto out; + + retry: + p = smb_setup_header(req, SMBopen, 2, 0); + WSET(req->rq_header, smb_vwv0, mode); + WSET(req->rq_header, smb_vwv1, aSYSTEM | aHIDDEN | aDIR); + res = smb_simple_encode_path(req, &p, dentry, NULL); + if (res < 0) + goto out_free; + smb_setup_bcc(req, p); + + res = smb_request_ok(req, SMBopen, 7, 0); + if (res != 0) { + if (mode == read_write && + (res == -EACCES || res == -ETXTBSY || res == -EROFS)) + { + VERBOSE("%s/%s R/W failed, error=%d, retrying R/O\n", + DENTRY_PATH(dentry), res); + mode = read_only; + req->rq_flags = 0; + goto retry; + } + goto out_free; + } + /* We should now have data in vwv[0..6]. */ + + ei->fileid = WVAL(req->rq_header, smb_vwv0); + ei->attr = WVAL(req->rq_header, smb_vwv1); + /* smb_vwv2 has mtime */ + /* smb_vwv4 has size */ + ei->access = (WVAL(req->rq_header, smb_vwv6) & SMB_ACCMASK); + ei->open = server->generation; + +out_free: + smb_rput(req); +out: + return res; +} + +/* + * Make sure the file is open, and check that the access + * is compatible with the desired access. + */ +int +smb_open(struct dentry *dentry, int wish) +{ + struct inode *inode = dentry->d_inode; + int result; + __u16 access; + + result = -ENOENT; + if (!inode) { + printk(KERN_ERR "smb_open: no inode for dentry %s/%s\n", + DENTRY_PATH(dentry)); + goto out; + } + + if (!smb_is_open(inode)) { + struct smb_sb_info *server = server_from_inode(inode); + result = 0; + if (!smb_is_open(inode)) + result = smb_proc_open(server, dentry, wish); + if (result) + goto out; + /* + * A successful open means the path is still valid ... + */ + smb_renew_times(dentry); + } + + /* + * Check whether the access is compatible with the desired mode. + */ + result = 0; + access = SMB_I(inode)->access; + if (access != wish && access != SMB_O_RDWR) { + PARANOIA("%s/%s access denied, access=%x, wish=%x\n", + DENTRY_PATH(dentry), access, wish); + result = -EACCES; + } +out: + return result; +} + +static int +smb_proc_close(struct smb_sb_info *server, __u16 fileid, __u32 mtime) +{ + struct smb_request *req; + int result = -ENOMEM; + + if (! (req = smb_alloc_request(server, 0))) + goto out; + + smb_setup_header(req, SMBclose, 3, 0); + WSET(req->rq_header, smb_vwv0, fileid); + DSET(req->rq_header, smb_vwv1, utc2local(server, mtime)); + req->rq_flags |= SMB_REQ_NORETRY; + result = smb_request_ok(req, SMBclose, 0, 0); + + smb_rput(req); +out: + return result; +} + +/* + * Win NT 4.0 has an apparent bug in that it fails to update the + * modify time when writing to a file. As a workaround, we update + * both modify and access time locally, and post the times to the + * server when closing the file. + */ +static int +smb_proc_close_inode(struct smb_sb_info *server, struct inode * ino) +{ + struct smb_inode_info *ei = SMB_I(ino); + int result = 0; + if (smb_is_open(ino)) + { + /* + * We clear the open flag in advance, in case another + * process observes the value while we block below. + */ + ei->open = 0; + + /* + * Kludge alert: SMB timestamps are accurate only to + * two seconds ... round the times to avoid needless + * cache invalidations! + */ + if (ino->i_mtime.tv_sec & 1) { + ino->i_mtime.tv_sec--; + ino->i_mtime.tv_nsec = 0; + } + if (ino->i_atime.tv_sec & 1) { + ino->i_atime.tv_sec--; + ino->i_atime.tv_nsec = 0; + } + /* + * If the file is open with write permissions, + * update the time stamps to sync mtime and atime. + */ + if ((server->opt.capabilities & SMB_CAP_UNIX) == 0 && + (server->opt.protocol >= SMB_PROTOCOL_LANMAN2) && + !(ei->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, ei->fileid, ino->i_mtime.tv_sec); + /* + * Force a revalidation after closing ... some servers + * don't post the size until the file has been closed. + */ + if (server->opt.protocol < SMB_PROTOCOL_NT1) + ei->oldmtime = 0; + ei->closed = jiffies; + } + return result; +} + +int +smb_close(struct inode *ino) +{ + int result = 0; + + if (smb_is_open(ino)) { + struct smb_sb_info *server = server_from_inode(ino); + result = smb_proc_close_inode(server, ino); + } + return result; +} + +/* + * This is used to close a file following a failed instantiate. + * Since we don't have an inode, we can't use any of the above. + */ +int +smb_close_fileid(struct dentry *dentry, __u16 fileid) +{ + struct smb_sb_info *server = server_from_dentry(dentry); + int result; + + result = smb_proc_close(server, fileid, get_seconds()); + return result; +} + +/* In smb_proc_read and smb_proc_write we do not retry, because the + file-id would not be valid after a reconnection. */ + +static void +smb_proc_read_data(struct smb_request *req) +{ + req->rq_iov[0].iov_base = req->rq_buffer; + req->rq_iov[0].iov_len = 3; + + req->rq_iov[1].iov_base = req->rq_page; + req->rq_iov[1].iov_len = req->rq_rsize; + req->rq_iovlen = 2; + + req->rq_rlen = smb_len(req->rq_header) + 4 - req->rq_bytes_recvd; +} + +static int +smb_proc_read(struct inode *inode, loff_t offset, int count, char *data) +{ + struct smb_sb_info *server = server_from_inode(inode); + __u16 returned_count, data_len; + unsigned char *buf; + int result; + struct smb_request *req; + u8 rbuf[4]; + + result = -ENOMEM; + if (! (req = smb_alloc_request(server, 0))) + goto out; + + smb_setup_header(req, SMBread, 5, 0); + buf = req->rq_header; + WSET(buf, smb_vwv0, SMB_I(inode)->fileid); + WSET(buf, smb_vwv1, count); + DSET(buf, smb_vwv2, offset); + WSET(buf, smb_vwv4, 0); + + req->rq_page = data; + req->rq_rsize = count; + req->rq_callback = smb_proc_read_data; + req->rq_buffer = rbuf; + req->rq_flags |= SMB_REQ_NORETRY | SMB_REQ_STATIC; + + result = smb_request_ok(req, SMBread, 5, -1); + if (result < 0) + goto out_free; + returned_count = WVAL(req->rq_header, smb_vwv0); + + data_len = WVAL(rbuf, 1); + + if (returned_count != data_len) { + printk(KERN_NOTICE "smb_proc_read: returned != data_len\n"); + printk(KERN_NOTICE "smb_proc_read: ret_c=%d, data_len=%d\n", + returned_count, data_len); + } + result = data_len; + +out_free: + smb_rput(req); +out: + VERBOSE("ino=%ld, fileid=%d, count=%d, result=%d\n", + inode->i_ino, SMB_I(inode)->fileid, count, result); + return result; +} + +static int +smb_proc_write(struct inode *inode, loff_t offset, int count, const char *data) +{ + struct smb_sb_info *server = server_from_inode(inode); + int result; + u16 fileid = SMB_I(inode)->fileid; + u8 buf[4]; + struct smb_request *req; + + result = -ENOMEM; + if (! (req = smb_alloc_request(server, 0))) + goto out; + + VERBOSE("ino=%ld, fileid=%d, count=%d@%Ld\n", + inode->i_ino, fileid, count, offset); + + smb_setup_header(req, SMBwrite, 5, count + 3); + WSET(req->rq_header, smb_vwv0, fileid); + WSET(req->rq_header, smb_vwv1, count); + DSET(req->rq_header, smb_vwv2, offset); + WSET(req->rq_header, smb_vwv4, 0); + + buf[0] = 1; + WSET(buf, 1, count); /* yes, again ... */ + req->rq_iov[1].iov_base = buf; + req->rq_iov[1].iov_len = 3; + req->rq_iov[2].iov_base = (char *) data; + req->rq_iov[2].iov_len = count; + req->rq_iovlen = 3; + req->rq_flags |= SMB_REQ_NORETRY; + + result = smb_request_ok(req, SMBwrite, 1, 0); + if (result >= 0) + result = WVAL(req->rq_header, smb_vwv0); + + smb_rput(req); +out: + return result; +} + +/* + * In smb_proc_readX and smb_proc_writeX we do not retry, because the + * file-id would not be valid after a reconnection. + */ + +#define SMB_READX_MAX_PAD 64 +static void +smb_proc_readX_data(struct smb_request *req) +{ + /* header length, excluding the netbios length (-4) */ + int hdrlen = SMB_HEADER_LEN + req->rq_resp_wct*2 - 2; + int data_off = WVAL(req->rq_header, smb_vwv6); + + /* + * Some genius made the padding to the data bytes arbitrary. + * So we must first calculate the amount of padding used by the server. + */ + data_off -= hdrlen; + if (data_off > SMB_READX_MAX_PAD || data_off < 0) { + PARANOIA("offset is larger than SMB_READX_MAX_PAD or negative!\n"); + PARANOIA("%d > %d || %d < 0\n", data_off, SMB_READX_MAX_PAD, data_off); + req->rq_rlen = req->rq_bufsize + 1; + return; + } + req->rq_iov[0].iov_base = req->rq_buffer; + req->rq_iov[0].iov_len = data_off; + + req->rq_iov[1].iov_base = req->rq_page; + req->rq_iov[1].iov_len = req->rq_rsize; + req->rq_iovlen = 2; + + req->rq_rlen = smb_len(req->rq_header) + 4 - req->rq_bytes_recvd; +} + +static int +smb_proc_readX(struct inode *inode, loff_t offset, int count, char *data) +{ + struct smb_sb_info *server = server_from_inode(inode); + unsigned char *buf; + int result; + struct smb_request *req; + static char pad[SMB_READX_MAX_PAD]; + + result = -ENOMEM; + if (! (req = smb_alloc_request(server, 0))) + goto out; + + smb_setup_header(req, SMBreadX, 12, 0); + buf = req->rq_header; + WSET(buf, smb_vwv0, 0x00ff); + WSET(buf, smb_vwv1, 0); + WSET(buf, smb_vwv2, SMB_I(inode)->fileid); + DSET(buf, smb_vwv3, (u32)offset); /* low 32 bits */ + WSET(buf, smb_vwv5, count); + WSET(buf, smb_vwv6, 0); + DSET(buf, smb_vwv7, 0); + WSET(buf, smb_vwv9, 0); + DSET(buf, smb_vwv10, (u32)(offset >> 32)); /* high 32 bits */ + WSET(buf, smb_vwv11, 0); + + req->rq_page = data; + req->rq_rsize = count; + req->rq_callback = smb_proc_readX_data; + req->rq_buffer = pad; + req->rq_bufsize = SMB_READX_MAX_PAD; + req->rq_flags |= SMB_REQ_STATIC | SMB_REQ_NORETRY; + + result = smb_request_ok(req, SMBreadX, 12, -1); + if (result < 0) + goto out_free; + result = WVAL(req->rq_header, smb_vwv5); + +out_free: + smb_rput(req); +out: + VERBOSE("ino=%ld, fileid=%d, count=%d, result=%d\n", + inode->i_ino, SMB_I(inode)->fileid, count, result); + return result; +} + +static int +smb_proc_writeX(struct inode *inode, loff_t offset, int count, const char *data) +{ + struct smb_sb_info *server = server_from_inode(inode); + int result; + u8 *p; + static u8 pad[4]; + struct smb_request *req; + + result = -ENOMEM; + if (! (req = smb_alloc_request(server, 0))) + goto out; + + VERBOSE("ino=%ld, fileid=%d, count=%d@%Ld\n", + inode->i_ino, SMB_I(inode)->fileid, count, offset); + + p = smb_setup_header(req, SMBwriteX, 14, count + 1); + WSET(req->rq_header, smb_vwv0, 0x00ff); + WSET(req->rq_header, smb_vwv1, 0); + WSET(req->rq_header, smb_vwv2, SMB_I(inode)->fileid); + DSET(req->rq_header, smb_vwv3, (u32)offset); /* low 32 bits */ + DSET(req->rq_header, smb_vwv5, 0); + WSET(req->rq_header, smb_vwv7, 0); /* write mode */ + WSET(req->rq_header, smb_vwv8, 0); + WSET(req->rq_header, smb_vwv9, 0); + WSET(req->rq_header, smb_vwv10, count); /* data length */ + WSET(req->rq_header, smb_vwv11, smb_vwv12 + 2 + 1); + DSET(req->rq_header, smb_vwv12, (u32)(offset >> 32)); + + req->rq_iov[1].iov_base = pad; + req->rq_iov[1].iov_len = 1; + req->rq_iov[2].iov_base = (char *) data; + req->rq_iov[2].iov_len = count; + req->rq_iovlen = 3; + req->rq_flags |= SMB_REQ_NORETRY; + + result = smb_request_ok(req, SMBwriteX, 6, 0); + if (result >= 0) + result = WVAL(req->rq_header, smb_vwv2); + + smb_rput(req); +out: + return result; +} + +int +smb_proc_create(struct dentry *dentry, __u16 attr, time_t ctime, __u16 *fileid) +{ + struct smb_sb_info *server = server_from_dentry(dentry); + char *p; + int result; + struct smb_request *req; + + result = -ENOMEM; + if (! (req = smb_alloc_request(server, PAGE_SIZE))) + goto out; + + p = smb_setup_header(req, SMBcreate, 3, 0); + WSET(req->rq_header, smb_vwv0, attr); + DSET(req->rq_header, smb_vwv1, utc2local(server, ctime)); + result = smb_simple_encode_path(req, &p, dentry, NULL); + if (result < 0) + goto out_free; + smb_setup_bcc(req, p); + + result = smb_request_ok(req, SMBcreate, 1, 0); + if (result < 0) + goto out_free; + + *fileid = WVAL(req->rq_header, smb_vwv0); + result = 0; + +out_free: + smb_rput(req); +out: + return result; +} + +int +smb_proc_mv(struct dentry *old_dentry, struct dentry *new_dentry) +{ + struct smb_sb_info *server = server_from_dentry(old_dentry); + char *p; + int result; + struct smb_request *req; + + result = -ENOMEM; + if (! (req = smb_alloc_request(server, PAGE_SIZE))) + goto out; + + p = smb_setup_header(req, SMBmv, 1, 0); + WSET(req->rq_header, smb_vwv0, aSYSTEM | aHIDDEN | aDIR); + result = smb_simple_encode_path(req, &p, old_dentry, NULL); + if (result < 0) + goto out_free; + result = smb_simple_encode_path(req, &p, new_dentry, NULL); + if (result < 0) + goto out_free; + smb_setup_bcc(req, p); + + if ((result = smb_request_ok(req, SMBmv, 0, 0)) < 0) + goto out_free; + result = 0; + +out_free: + smb_rput(req); +out: + return result; +} + +/* + * Code common to mkdir and rmdir. + */ +static int +smb_proc_generic_command(struct dentry *dentry, __u8 command) +{ + struct smb_sb_info *server = server_from_dentry(dentry); + char *p; + int result; + struct smb_request *req; + + result = -ENOMEM; + if (! (req = smb_alloc_request(server, PAGE_SIZE))) + goto out; + + p = smb_setup_header(req, command, 0, 0); + result = smb_simple_encode_path(req, &p, dentry, NULL); + if (result < 0) + goto out_free; + smb_setup_bcc(req, p); + + result = smb_request_ok(req, command, 0, 0); + if (result < 0) + goto out_free; + result = 0; + +out_free: + smb_rput(req); +out: + return result; +} + +int +smb_proc_mkdir(struct dentry *dentry) +{ + return smb_proc_generic_command(dentry, SMBmkdir); +} + +int +smb_proc_rmdir(struct dentry *dentry) +{ + return smb_proc_generic_command(dentry, SMBrmdir); +} + +#if SMBFS_POSIX_UNLINK +/* + * Removes readonly attribute from a file. Used by unlink to give posix + * semantics. + */ +static int +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); + smb_finish_dirent(server, &fattr); + if (result < 0) + return result; + + /* if RONLY attribute is set, remove it */ + if (fattr.attr & aRONLY) { /* read only attribute is set */ + fattr.attr &= ~aRONLY; + result = smb_proc_setattr_core(server, dentry, fattr.attr); + } + return result; +} +#endif + +int +smb_proc_unlink(struct dentry *dentry) +{ + struct smb_sb_info *server = server_from_dentry(dentry); + int flag = 0; + char *p; + int result; + struct smb_request *req; + + result = -ENOMEM; + if (! (req = smb_alloc_request(server, PAGE_SIZE))) + goto out; + + retry: + p = smb_setup_header(req, SMBunlink, 1, 0); + WSET(req->rq_header, smb_vwv0, aSYSTEM | aHIDDEN); + result = smb_simple_encode_path(req, &p, dentry, NULL); + if (result < 0) + goto out_free; + smb_setup_bcc(req, p); + + if ((result = smb_request_ok(req, SMBunlink, 0, 0)) < 0) { +#if SMBFS_POSIX_UNLINK + if (result == -EACCES && !flag) { + /* Posix semantics is for the read-only state + of a file to be ignored in unlink(). In the + SMB world a unlink() is refused on a + read-only file. To make things easier for + unix users we try to override the files + permission if the unlink fails with the + right error. + This introduces a race condition that could + lead to a file being written by someone who + shouldn't have access, but as far as I can + tell that is unavoidable */ + + /* remove RONLY attribute and try again */ + result = smb_set_rw(dentry,server); + if (result == 0) { + flag = 1; + req->rq_flags = 0; + goto retry; + } + } +#endif + goto out_free; + } + result = 0; + +out_free: + smb_rput(req); +out: + return result; +} + +int +smb_proc_flush(struct smb_sb_info *server, __u16 fileid) +{ + int result; + struct smb_request *req; + + result = -ENOMEM; + if (! (req = smb_alloc_request(server, 0))) + goto out; + + smb_setup_header(req, SMBflush, 1, 0); + WSET(req->rq_header, smb_vwv0, fileid); + req->rq_flags |= SMB_REQ_NORETRY; + result = smb_request_ok(req, SMBflush, 0, 0); + + smb_rput(req); +out: + return result; +} + +static int +smb_proc_trunc32(struct inode *inode, loff_t length) +{ + /* + * Writing 0bytes is old-SMB magic for truncating files. + * MAX_NON_LFS should prevent this from being called with a too + * large offset. + */ + return smb_proc_write(inode, length, 0, NULL); +} + +static int +smb_proc_trunc64(struct inode *inode, loff_t length) +{ + struct smb_sb_info *server = server_from_inode(inode); + int result; + char *param; + char *data; + struct smb_request *req; + + result = -ENOMEM; + if (! (req = smb_alloc_request(server, 14))) + goto out; + + param = req->rq_buffer; + data = req->rq_buffer + 6; + + /* FIXME: must we also set allocation size? winNT seems to do that */ + WSET(param, 0, SMB_I(inode)->fileid); + WSET(param, 2, SMB_SET_FILE_END_OF_FILE_INFO); + WSET(param, 4, 0); + LSET(data, 0, length); + + req->rq_trans2_command = TRANSACT2_SETFILEINFO; + req->rq_ldata = 8; + req->rq_data = data; + req->rq_lparm = 6; + req->rq_parm = param; + req->rq_flags |= SMB_REQ_NORETRY; + result = smb_add_request(req); + if (result < 0) + goto out_free; + + result = 0; + if (req->rq_rcls != 0) + result = smb_errno(req); + +out_free: + smb_rput(req); +out: + return result; +} + +static int +smb_proc_trunc95(struct inode *inode, loff_t length) +{ + struct smb_sb_info *server = server_from_inode(inode); + int result = smb_proc_trunc32(inode, length); + + /* + * win9x doesn't appear to update the size immediately. + * It will return the old file size after the truncate, + * confusing smbfs. So we force an update. + * + * FIXME: is this still necessary? + */ + smb_proc_flush(server, SMB_I(inode)->fileid); + return result; +} + +static void +smb_init_dirent(struct smb_sb_info *server, struct smb_fattr *fattr) +{ + memset(fattr, 0, sizeof(*fattr)); + + fattr->f_nlink = 1; + fattr->f_uid = server->mnt->uid; + fattr->f_gid = server->mnt->gid; + 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) { + 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); + return; +} + +void +smb_init_root_dirent(struct smb_sb_info *server, struct smb_fattr *fattr, + struct super_block *sb) +{ + smb_init_dirent(server, fattr); + fattr->attr = aDIR; + fattr->f_ino = 2; /* traditional root inode number */ + fattr->f_mtime = current_fs_time(sb); + smb_finish_dirent(server, fattr); +} + +/* + * Decode a dirent for old protocols + * + * qname is filled with the decoded, and possibly translated, name. + * fattr receives decoded attributes + * + * Bugs Noted: + * (1) Pathworks servers may pad the name with extra spaces. + */ +static char * +smb_decode_short_dirent(struct smb_sb_info *server, char *p, + struct qstr *qname, struct smb_fattr *fattr, + unsigned char *name_buf) +{ + int len; + + /* + * SMB doesn't have a concept of inode numbers ... + */ + smb_init_dirent(server, fattr); + fattr->f_ino = 0; /* FIXME: do we need this? */ + + p += SMB_STATUS_SIZE; /* reserved (search_status) */ + fattr->attr = *p; + fattr->f_mtime.tv_sec = date_dos2unix(server, WVAL(p, 3), WVAL(p, 1)); + fattr->f_mtime.tv_nsec = 0; + fattr->f_size = DVAL(p, 5); + fattr->f_ctime = fattr->f_mtime; + fattr->f_atime = fattr->f_mtime; + qname->name = p + 9; + len = strnlen(qname->name, 12); + + /* + * Trim trailing blanks for Pathworks servers + */ + while (len > 2 && qname->name[len-1] == ' ') + len--; + + smb_finish_dirent(server, fattr); + +#if 0 + /* FIXME: These only work for ascii chars, and recent smbmount doesn't + allow the flag to be set anyway. It kills const. Remove? */ + switch (server->opt.case_handling) { + case SMB_CASE_UPPER: + str_upper(entry->name, len); + break; + case SMB_CASE_LOWER: + str_lower(entry->name, len); + break; + default: + break; + } +#endif + + qname->len = 0; + len = server->ops->convert(name_buf, SMB_MAXNAMELEN, + qname->name, len, + server->remote_nls, server->local_nls); + if (len > 0) { + qname->len = len; + qname->name = name_buf; + DEBUG1("len=%d, name=%.*s\n",qname->len,qname->len,qname->name); + } + + return p + 22; +} + +/* + * This routine is used to read in directory entries from the network. + * Note that it is for short directory name seeks, i.e.: protocol < + * SMB_PROTOCOL_LANMAN2 + */ +static int +smb_proc_readdir_short(struct file *filp, void *dirent, filldir_t filldir, + struct smb_cache_control *ctl) +{ + struct dentry *dir = filp->f_path.dentry; + struct smb_sb_info *server = server_from_dentry(dir); + struct qstr qname; + struct smb_fattr fattr; + char *p; + int result; + int i, first, entries_seen, entries; + int entries_asked = (server->opt.max_xmit - 100) / SMB_DIRINFO_SIZE; + __u16 bcc; + __u16 count; + char status[SMB_STATUS_SIZE]; + static struct qstr mask = { + .name = "*.*", + .len = 3, + }; + unsigned char *last_status; + struct smb_request *req; + unsigned char *name_buf; + + VERBOSE("%s/%s\n", DENTRY_PATH(dir)); + + lock_kernel(); + + result = -ENOMEM; + if (! (name_buf = kmalloc(SMB_MAXNAMELEN, GFP_KERNEL))) + goto out; + + first = 1; + entries = 0; + entries_seen = 2; /* implicit . and .. */ + + result = -ENOMEM; + if (! (req = smb_alloc_request(server, server->opt.max_xmit))) + goto out_name; + + while (1) { + p = smb_setup_header(req, SMBsearch, 2, 0); + WSET(req->rq_header, smb_vwv0, entries_asked); + WSET(req->rq_header, smb_vwv1, aDIR); + if (first == 1) { + result = smb_simple_encode_path(req, &p, dir, &mask); + if (result < 0) + goto out_free; + if (p + 3 > (char *)req->rq_buffer + req->rq_bufsize) { + result = -ENAMETOOLONG; + goto out_free; + } + *p++ = 5; + WSET(p, 0, 0); + p += 2; + first = 0; + } else { + if (p + 5 + SMB_STATUS_SIZE > + (char *)req->rq_buffer + req->rq_bufsize) { + result = -ENAMETOOLONG; + goto out_free; + } + + *p++ = 4; + *p++ = 0; + *p++ = 5; + WSET(p, 0, SMB_STATUS_SIZE); + p += 2; + memcpy(p, status, SMB_STATUS_SIZE); + p += SMB_STATUS_SIZE; + } + + smb_setup_bcc(req, p); + + result = smb_request_ok(req, SMBsearch, 1, -1); + if (result < 0) { + if ((req->rq_rcls == ERRDOS) && + (req->rq_err == ERRnofiles)) + break; + goto out_free; + } + count = WVAL(req->rq_header, smb_vwv0); + if (count <= 0) + break; + + result = -EIO; + bcc = smb_bcc(req->rq_header); + if (bcc != count * SMB_DIRINFO_SIZE + 3) + goto out_free; + p = req->rq_buffer + 3; + + + /* Make sure the response fits in the buffer. Fixed sized + entries means we don't have to check in the decode loop. */ + + last_status = req->rq_buffer + 3 + (count-1) * SMB_DIRINFO_SIZE; + + if (last_status + SMB_DIRINFO_SIZE >= + req->rq_buffer + req->rq_bufsize) { + printk(KERN_ERR "smb_proc_readdir_short: " + "last dir entry outside buffer! " + "%d@%p %d@%p\n", SMB_DIRINFO_SIZE, last_status, + req->rq_bufsize, req->rq_buffer); + goto out_free; + } + + /* Read the last entry into the status field. */ + memcpy(status, last_status, SMB_STATUS_SIZE); + + + /* Now we are ready to parse smb directory entries. */ + + for (i = 0; i < count; i++) { + p = smb_decode_short_dirent(server, p, + &qname, &fattr, name_buf); + if (qname.len == 0) + continue; + + if (entries_seen == 2 && qname.name[0] == '.') { + if (qname.len == 1) + continue; + if (qname.name[1] == '.' && qname.len == 2) + continue; + } + if (!smb_fill_cache(filp, dirent, filldir, ctl, + &qname, &fattr)) + ; /* stop reading? */ + entries_seen++; + } + } + result = entries; + +out_free: + smb_rput(req); +out_name: + kfree(name_buf); +out: + unlock_kernel(); + return result; +} + +static void smb_decode_unix_basic(struct smb_fattr *fattr, struct smb_sb_info *server, char *p) +{ + u64 size, disk_bytes; + + /* FIXME: verify nls support. all is sent as utf8? */ + + 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 */ + + size = LVAL(p, 0); + disk_bytes = LVAL(p, 8); + + /* + * Some samba versions round up on-disk byte usage + * to 1MB boundaries, making it useless. When seeing + * that, use the size instead. + */ + if (!(disk_bytes & 0xfffff)) + disk_bytes = size+511; + + fattr->f_size = size; + fattr->f_blocks = disk_bytes >> 9; + fattr->f_ctime = smb_ntutc2unixutc(LVAL(p, 16)); + fattr->f_atime = smb_ntutc2unixutc(LVAL(p, 24)); + fattr->f_mtime = smb_ntutc2unixutc(LVAL(p, 32)); + + if (server->mnt->flags & SMB_MOUNT_UID) + fattr->f_uid = server->mnt->uid; + else + fattr->f_uid = LVAL(p, 40); + + if (server->mnt->flags & SMB_MOUNT_GID) + fattr->f_gid = server->mnt->gid; + else + 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)) { + __u64 major = LVAL(p, 60); + __u64 minor = LVAL(p, 68); + + fattr->f_rdev = MKDEV(major & 0xffffffff, minor & 0xffffffff); + if (MAJOR(fattr->f_rdev) != (major & 0xffffffff) || + MINOR(fattr->f_rdev) != (minor & 0xffffffff)) + fattr->f_rdev = 0; + } + + fattr->f_mode |= LVAL(p, 84); + + if ( (server->mnt->flags & SMB_MOUNT_DMODE) && + (S_ISDIR(fattr->f_mode)) ) + fattr->f_mode = (server->mnt->dir_mode & S_IRWXUGO) | S_IFDIR; + else if ( (server->mnt->flags & SMB_MOUNT_FMODE) && + !(S_ISDIR(fattr->f_mode)) ) + fattr->f_mode = (server->mnt->file_mode & S_IRWXUGO) | + (fattr->f_mode & S_IFMT); + +} + +/* + * Interpret a long filename structure using the specified info level: + * level 1 for anything below NT1 protocol + * level 260 for NT1 protocol + * + * qname is filled with the decoded, and possibly translated, name + * fattr receives decoded attributes. + * + * Bugs Noted: + * (1) Win NT 4.0 appends a null byte to names and counts it in the length! + */ +static char * +smb_decode_long_dirent(struct smb_sb_info *server, char *p, int level, + struct qstr *qname, struct smb_fattr *fattr, + unsigned char *name_buf) +{ + char *result; + unsigned int len = 0; + int n; + __u16 date, time; + int unicode = (server->mnt->flags & SMB_MOUNT_UNICODE); + + /* + * SMB doesn't have a concept of inode numbers ... + */ + smb_init_dirent(server, fattr); + fattr->f_ino = 0; /* FIXME: do we need this? */ + + switch (level) { + case 1: + len = *((unsigned char *) p + 22); + qname->name = p + 23; + result = p + 24 + len; + + date = WVAL(p, 0); + time = WVAL(p, 2); + fattr->f_ctime.tv_sec = date_dos2unix(server, date, time); + fattr->f_ctime.tv_nsec = 0; + + date = WVAL(p, 4); + time = WVAL(p, 6); + fattr->f_atime.tv_sec = date_dos2unix(server, date, time); + fattr->f_atime.tv_nsec = 0; + + date = WVAL(p, 8); + time = WVAL(p, 10); + fattr->f_mtime.tv_sec = date_dos2unix(server, date, time); + fattr->f_mtime.tv_nsec = 0; + fattr->f_size = DVAL(p, 12); + /* ULONG allocation size */ + fattr->attr = WVAL(p, 20); + + VERBOSE("info 1 at %p, len=%d, name=%.*s\n", + p, len, len, qname->name); + break; + case 260: + result = p + WVAL(p, 0); + len = DVAL(p, 60); + if (len > 255) len = 255; + /* NT4 null terminates, unless we are using unicode ... */ + qname->name = p + 94; + if (!unicode && len && qname->name[len-1] == '\0') + len--; + + fattr->f_ctime = smb_ntutc2unixutc(LVAL(p, 8)); + fattr->f_atime = smb_ntutc2unixutc(LVAL(p, 16)); + fattr->f_mtime = smb_ntutc2unixutc(LVAL(p, 24)); + /* change time (32) */ + fattr->f_size = LVAL(p, 40); + /* alloc size (48) */ + fattr->attr = DVAL(p, 56); + + 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, server, 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); + goto out; + } + + smb_finish_dirent(server, fattr); + +#if 0 + /* FIXME: These only work for ascii chars, and recent smbmount doesn't + allow the flag to be set anyway. Remove? */ + switch (server->opt.case_handling) { + case SMB_CASE_UPPER: + str_upper(qname->name, len); + break; + case SMB_CASE_LOWER: + str_lower(qname->name, len); + break; + default: + break; + } +#endif + + qname->len = 0; + n = server->ops->convert(name_buf, SMB_MAXNAMELEN, + qname->name, len, + server->remote_nls, server->local_nls); + if (n > 0) { + qname->len = n; + qname->name = name_buf; + } + +out: + return result; +} + +/* findfirst/findnext flags */ +#define SMB_CLOSE_AFTER_FIRST (1<<0) +#define SMB_CLOSE_IF_END (1<<1) +#define SMB_REQUIRE_RESUME_KEY (1<<2) +#define SMB_CONTINUE_BIT (1<<3) + +/* + * Note: samba-2.0.7 (at least) has a very similar routine, cli_list, in + * source/libsmb/clilist.c. When looking for smb bugs in the readdir code, + * go there for advise. + * + * Bugs Noted: + * (1) When using Info Level 1 Win NT 4.0 truncates directory listings + * for certain patterns of names and/or lengths. The breakage pattern + * is completely reproducible and can be toggled by the creation of a + * single file. (E.g. echo hi >foo breaks, rm -f foo works.) + */ +static int +smb_proc_readdir_long(struct file *filp, void *dirent, filldir_t filldir, + struct smb_cache_control *ctl) +{ + struct dentry *dir = filp->f_path.dentry; + struct smb_sb_info *server = server_from_dentry(dir); + struct qstr qname; + struct smb_fattr fattr; + + unsigned char *p, *lastname; + char *mask, *param; + __u16 command; + int first, entries_seen; + + /* Both NT and OS/2 accept info level 1 (but see note below). */ + int info_level = 260; + const int max_matches = 512; + + unsigned int ff_searchcount = 0; + unsigned int ff_eos = 0; + unsigned int ff_lastname = 0; + unsigned int ff_dir_handle = 0; + unsigned int loop_count = 0; + unsigned int mask_len, i; + int result; + struct smb_request *req; + unsigned char *name_buf; + static struct qstr star = { + .name = "*", + .len = 1, + }; + + lock_kernel(); + + /* + * We always prefer unix style. Use info level 1 for older + * servers that don't do 260. + */ + 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; + if (! (name_buf = kmalloc(SMB_MAXNAMELEN+2, GFP_KERNEL))) + goto out; + if (! (req = smb_alloc_request(server, server->opt.max_xmit))) + goto out_name; + param = req->rq_buffer; + + /* + * Encode the initial path + */ + mask = param + 12; + + result = smb_encode_path(server, mask, SMB_MAXPATHLEN+1, dir, &star); + if (result <= 0) + goto out_free; + mask_len = result - 1; /* mask_len is strlen, not #bytes */ + result = 0; + first = 1; + VERBOSE("starting mask_len=%d, mask=%s\n", mask_len, mask); + + entries_seen = 2; + ff_eos = 0; + + while (ff_eos == 0) { + loop_count += 1; + if (loop_count > 10) { + printk(KERN_WARNING "smb_proc_readdir_long: " + "Looping in FIND_NEXT??\n"); + result = -EIO; + break; + } + + if (first != 0) { + command = TRANSACT2_FINDFIRST; + WSET(param, 0, aSYSTEM | aHIDDEN | aDIR); + WSET(param, 2, max_matches); /* max count */ + WSET(param, 4, SMB_CLOSE_IF_END); + WSET(param, 6, info_level); + DSET(param, 8, 0); + } else { + command = TRANSACT2_FINDNEXT; + + VERBOSE("handle=0x%X, lastname=%d, mask=%.*s\n", + ff_dir_handle, ff_lastname, mask_len, mask); + + WSET(param, 0, ff_dir_handle); /* search handle */ + WSET(param, 2, max_matches); /* max count */ + WSET(param, 4, info_level); + DSET(param, 6, 0); + WSET(param, 10, SMB_CONTINUE_BIT|SMB_CLOSE_IF_END); + } + + req->rq_trans2_command = command; + req->rq_ldata = 0; + req->rq_data = NULL; + req->rq_lparm = 12 + mask_len + 1; + req->rq_parm = param; + req->rq_flags = 0; + result = smb_add_request(req); + if (result < 0) { + PARANOIA("error=%d, breaking\n", result); + break; + } + + if (req->rq_rcls == ERRSRV && req->rq_err == ERRerror) { + /* a damn Win95 bug - sometimes it clags if you + ask it too fast */ + schedule_timeout_interruptible(msecs_to_jiffies(200)); + continue; + } + + if (req->rq_rcls != 0) { + result = smb_errno(req); + PARANOIA("name=%s, result=%d, rcls=%d, err=%d\n", + mask, result, req->rq_rcls, req->rq_err); + break; + } + + /* parse out some important return info */ + if (first != 0) { + ff_dir_handle = WVAL(req->rq_parm, 0); + ff_searchcount = WVAL(req->rq_parm, 2); + ff_eos = WVAL(req->rq_parm, 4); + ff_lastname = WVAL(req->rq_parm, 8); + } else { + ff_searchcount = WVAL(req->rq_parm, 0); + ff_eos = WVAL(req->rq_parm, 2); + ff_lastname = WVAL(req->rq_parm, 6); + } + + if (ff_searchcount == 0) + break; + + /* Now we are ready to parse smb directory entries. */ + + /* point to the data bytes */ + p = req->rq_data; + for (i = 0; i < ff_searchcount; i++) { + /* make sure we stay within the buffer */ + if (p >= req->rq_data + req->rq_ldata) { + printk(KERN_ERR "smb_proc_readdir_long: " + "dirent pointer outside buffer! " + "%p %d@%p\n", + p, req->rq_ldata, req->rq_data); + result = -EIO; /* always a comm. error? */ + goto out_free; + } + + p = smb_decode_long_dirent(server, p, info_level, + &qname, &fattr, name_buf); + + /* ignore . and .. from the server */ + if (entries_seen == 2 && qname.name[0] == '.') { + if (qname.len == 1) + continue; + if (qname.name[1] == '.' && qname.len == 2) + continue; + } + + if (!smb_fill_cache(filp, dirent, filldir, ctl, + &qname, &fattr)) + ; /* stop reading? */ + entries_seen++; + } + + VERBOSE("received %d entries, eos=%d\n", ff_searchcount,ff_eos); + + /* + * We might need the lastname for continuations. + * + * Note that some servers (win95?) point to the filename and + * others (NT4, Samba using NT1) to the dir entry. We assume + * 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. + * 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 (info_level != SMB_FIND_FILE_UNIX && + ff_lastname > 0 && ff_lastname < req->rq_ldata) { + lastname = req->rq_data + ff_lastname; + + switch (info_level) { + case 260: + mask_len = req->rq_ldata - ff_lastname; + break; + case 1: + /* lastname points to a length byte */ + mask_len = *lastname++; + if (ff_lastname + 1 + mask_len > req->rq_ldata) + mask_len = req->rq_ldata - ff_lastname - 1; + break; + } + + /* + * Update the mask string for the next message. + */ + if (mask_len > 255) + mask_len = 255; + if (mask_len) + strncpy(mask, lastname, mask_len); + } + mask_len = strnlen(mask, mask_len); + VERBOSE("new mask, len=%d@%d of %d, mask=%.*s\n", + mask_len, ff_lastname, req->rq_ldata, mask_len, mask); + + first = 0; + loop_count = 0; + } + +out_free: + smb_rput(req); +out_name: + kfree(name_buf); +out: + unlock_kernel(); + return result; +} + +/* + * This version uses the trans2 TRANSACT2_FINDFIRST message + * to get the attribute data. + * + * Bugs Noted: + */ +static int +smb_proc_getattr_ff(struct smb_sb_info *server, struct dentry *dentry, + struct smb_fattr *fattr) +{ + char *param, *mask; + __u16 date, time; + int mask_len, result; + struct smb_request *req; + + result = -ENOMEM; + if (! (req = smb_alloc_request(server, PAGE_SIZE))) + goto out; + param = req->rq_buffer; + mask = param + 12; + + mask_len = smb_encode_path(server, mask, SMB_MAXPATHLEN+1, dentry,NULL); + if (mask_len < 0) { + result = mask_len; + goto out_free; + } + VERBOSE("name=%s, len=%d\n", mask, mask_len); + WSET(param, 0, aSYSTEM | aHIDDEN | aDIR); + WSET(param, 2, 1); /* max count */ + WSET(param, 4, 1); /* close after this call */ + WSET(param, 6, 1); /* info_level */ + DSET(param, 8, 0); + + req->rq_trans2_command = TRANSACT2_FINDFIRST; + req->rq_ldata = 0; + req->rq_data = NULL; + req->rq_lparm = 12 + mask_len; + req->rq_parm = param; + req->rq_flags = 0; + result = smb_add_request(req); + if (result < 0) + goto out_free; + if (req->rq_rcls != 0) { + result = smb_errno(req); +#ifdef SMBFS_PARANOIA + if (result != -ENOENT) + PARANOIA("error for %s, rcls=%d, err=%d\n", + mask, req->rq_rcls, req->rq_err); +#endif + goto out_free; + } + /* Make sure we got enough data ... */ + result = -EINVAL; + if (req->rq_ldata < 22 || WVAL(req->rq_parm, 2) != 1) { + PARANOIA("bad result for %s, len=%d, count=%d\n", + mask, req->rq_ldata, WVAL(req->rq_parm, 2)); + goto out_free; + } + + /* + * Decode the response into the fattr ... + */ + date = WVAL(req->rq_data, 0); + time = WVAL(req->rq_data, 2); + fattr->f_ctime.tv_sec = date_dos2unix(server, date, time); + fattr->f_ctime.tv_nsec = 0; + + date = WVAL(req->rq_data, 4); + time = WVAL(req->rq_data, 6); + fattr->f_atime.tv_sec = date_dos2unix(server, date, time); + fattr->f_atime.tv_nsec = 0; + + date = WVAL(req->rq_data, 8); + time = WVAL(req->rq_data, 10); + fattr->f_mtime.tv_sec = date_dos2unix(server, date, time); + fattr->f_mtime.tv_nsec = 0; + VERBOSE("name=%s, date=%x, time=%x, mtime=%ld\n", + mask, date, time, fattr->f_mtime.tv_sec); + fattr->f_size = DVAL(req->rq_data, 12); + /* ULONG allocation size */ + fattr->attr = WVAL(req->rq_data, 20); + result = 0; + +out_free: + smb_rput(req); +out: + return result; +} + +static int +smb_proc_getattr_core(struct smb_sb_info *server, struct dentry *dir, + struct smb_fattr *fattr) +{ + int result; + char *p; + struct smb_request *req; + + result = -ENOMEM; + if (! (req = smb_alloc_request(server, PAGE_SIZE))) + goto out; + + p = smb_setup_header(req, SMBgetatr, 0, 0); + result = smb_simple_encode_path(req, &p, dir, NULL); + if (result < 0) + goto out_free; + smb_setup_bcc(req, p); + + if ((result = smb_request_ok(req, SMBgetatr, 10, 0)) < 0) + goto out_free; + fattr->attr = WVAL(req->rq_header, smb_vwv0); + fattr->f_mtime.tv_sec = local2utc(server, DVAL(req->rq_header, smb_vwv1)); + fattr->f_mtime.tv_nsec = 0; + fattr->f_size = DVAL(req->rq_header, smb_vwv3); + fattr->f_ctime = fattr->f_mtime; + fattr->f_atime = fattr->f_mtime; +#ifdef SMBFS_DEBUG_TIMESTAMP + printk("getattr_core: %s/%s, mtime=%ld\n", + DENTRY_PATH(dir), fattr->f_mtime); +#endif + result = 0; + +out_free: + smb_rput(req); +out: + return result; +} + +/* + * Bugs Noted: + * (1) Win 95 swaps the date and time fields in the standard info level. + */ +static int +smb_proc_getattr_trans2(struct smb_sb_info *server, struct dentry *dir, + struct smb_request *req, int infolevel) +{ + char *p, *param; + int result; + + param = req->rq_buffer; + WSET(param, 0, infolevel); + DSET(param, 2, 0); + result = smb_encode_path(server, param+6, SMB_MAXPATHLEN+1, dir, NULL); + if (result < 0) + goto out; + 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; + if (req->rq_rcls != 0) { + VERBOSE("for %s: result=%d, rcls=%d, err=%d\n", + ¶m[6], result, req->rq_rcls, req->rq_err); + result = smb_errno(req); + goto out; + } + result = -ENOENT; + if (req->rq_ldata < 22) { + PARANOIA("not enough data for %s, len=%d\n", + ¶m[6], req->rq_ldata); + goto out; + } + + result = 0; +out: + return result; +} + +static int +smb_proc_getattr_trans2_std(struct smb_sb_info *server, struct dentry *dir, + struct smb_fattr *attr) +{ + u16 date, time; + int off_date = 0, off_time = 2; + int result; + struct smb_request *req; + + result = -ENOMEM; + if (! (req = smb_alloc_request(server, PAGE_SIZE))) + goto out; + + result = smb_proc_getattr_trans2(server, dir, req, SMB_INFO_STANDARD); + if (result < 0) + goto out_free; + + /* + * 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(req->rq_data, off_date); + time = WVAL(req->rq_data, off_time); + attr->f_ctime.tv_sec = date_dos2unix(server, date, time); + attr->f_ctime.tv_nsec = 0; + + date = WVAL(req->rq_data, 4 + off_date); + time = WVAL(req->rq_data, 4 + off_time); + attr->f_atime.tv_sec = date_dos2unix(server, date, time); + attr->f_atime.tv_nsec = 0; + + date = WVAL(req->rq_data, 8 + off_date); + time = WVAL(req->rq_data, 8 + off_time); + attr->f_mtime.tv_sec = date_dos2unix(server, date, time); + attr->f_mtime.tv_nsec = 0; +#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); +#endif + attr->f_size = DVAL(req->rq_data, 12); + attr->attr = WVAL(req->rq_data, 20); + +out_free: + smb_rput(req); +out: + return result; +} + +static int +smb_proc_getattr_trans2_all(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_ALL_INFO); + if (result < 0) + goto out_free; + + attr->f_ctime = smb_ntutc2unixutc(LVAL(req->rq_data, 0)); + attr->f_atime = smb_ntutc2unixutc(LVAL(req->rq_data, 8)); + attr->f_mtime = smb_ntutc2unixutc(LVAL(req->rq_data, 16)); + /* change (24) */ + attr->attr = WVAL(req->rq_data, 32); + /* pad? (34) */ + /* allocated size (40) */ + attr->f_size = LVAL(req->rq_data, 48); + +out_free: + smb_rput(req); +out: + return result; +} + +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, server, 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) +{ + struct inode *inode = dir->d_inode; + int result; + + /* FIXME: why not use the "all" version? */ + result = smb_proc_getattr_trans2_std(server, dir, attr); + if (result < 0) + goto out; + + /* + * None of the getattr versions here can make win9x return the right + * filesize if there are changes made to an open file. + * A seek-to-end does return the right size, but we only need to do + * that on files we have written. + */ + if (inode && SMB_I(inode)->flags & SMB_F_LOCALWRITE && + smb_is_open(inode)) + { + __u16 fileid = SMB_I(inode)->fileid; + attr->f_size = smb_proc_seek(server, fileid, 2, 0); + } + +out: + return result; +} + +static int +smb_proc_ops_wait(struct smb_sb_info *server) +{ + int result; + + result = wait_event_interruptible_timeout(server->conn_wq, + server->conn_complete, 30*HZ); + + if (!result || signal_pending(current)) + return -EIO; + + return 0; +} + +static int +smb_proc_getattr_null(struct smb_sb_info *server, struct dentry *dir, + struct smb_fattr *fattr) +{ + int result; + + if (smb_proc_ops_wait(server) < 0) + return -EIO; + + smb_init_dirent(server, fattr); + result = server->ops->getattr(server, dir, fattr); + smb_finish_dirent(server, fattr); + + return result; +} + +static int +smb_proc_readdir_null(struct file *filp, void *dirent, filldir_t filldir, + struct smb_cache_control *ctl) +{ + struct smb_sb_info *server = server_from_dentry(filp->f_path.dentry); + + if (smb_proc_ops_wait(server) < 0) + return -EIO; + + return server->ops->readdir(filp, dirent, filldir, ctl); +} + +int +smb_proc_getattr(struct dentry *dir, struct smb_fattr *fattr) +{ + struct smb_sb_info *server = server_from_dentry(dir); + int result; + + smb_init_dirent(server, fattr); + result = server->ops->getattr(server, dir, fattr); + smb_finish_dirent(server, fattr); + + return result; +} + + +/* + * Because of bugs in the core protocol, we use this only to set + * attributes. See smb_proc_settime() below for timestamp handling. + * + * Bugs Noted: + * (1) If mtime is non-zero, both Win 3.1 and Win 95 fail + * with an undocumented error (ERRDOS code 50). Setting + * mtime to 0 allows the attributes to be set. + * (2) The extra parameters following the name string aren't + * in the CIFS docs, but seem to be necessary for operation. + */ +static int +smb_proc_setattr_core(struct smb_sb_info *server, struct dentry *dentry, + __u16 attr) +{ + char *p; + int result; + struct smb_request *req; + + result = -ENOMEM; + if (! (req = smb_alloc_request(server, PAGE_SIZE))) + goto out; + + p = smb_setup_header(req, SMBsetatr, 8, 0); + WSET(req->rq_header, smb_vwv0, attr); + DSET(req->rq_header, smb_vwv1, 0); /* mtime */ + WSET(req->rq_header, smb_vwv3, 0); /* reserved values */ + WSET(req->rq_header, smb_vwv4, 0); + WSET(req->rq_header, smb_vwv5, 0); + WSET(req->rq_header, smb_vwv6, 0); + WSET(req->rq_header, smb_vwv7, 0); + result = smb_simple_encode_path(req, &p, dentry, NULL); + if (result < 0) + goto out_free; + if (p + 2 > (char *)req->rq_buffer + req->rq_bufsize) { + result = -ENAMETOOLONG; + goto out_free; + } + *p++ = 4; + *p++ = 0; + smb_setup_bcc(req, p); + + result = smb_request_ok(req, SMBsetatr, 0, 0); + if (result < 0) + goto out_free; + result = 0; + +out_free: + smb_rput(req); +out: + return result; +} + +/* + * 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) +{ + struct smb_sb_info *server = server_from_dentry(dir); + int result; + + VERBOSE("setting %s/%s, open=%d\n", + DENTRY_PATH(dir), smb_is_open(dir->d_inode)); + result = smb_proc_setattr_core(server, dir, fattr->attr); + return result; +} + +/* + * Sets the timestamps for an file open with write permissions. + */ +static int +smb_proc_setattr_ext(struct smb_sb_info *server, + struct inode *inode, struct smb_fattr *fattr) +{ + __u16 date, time; + int result; + struct smb_request *req; + + result = -ENOMEM; + if (! (req = smb_alloc_request(server, 0))) + goto out; + + smb_setup_header(req, SMBsetattrE, 7, 0); + WSET(req->rq_header, smb_vwv0, SMB_I(inode)->fileid); + /* We don't change the creation time */ + WSET(req->rq_header, smb_vwv1, 0); + WSET(req->rq_header, smb_vwv2, 0); + date_unix2dos(server, fattr->f_atime.tv_sec, &date, &time); + WSET(req->rq_header, smb_vwv3, date); + WSET(req->rq_header, smb_vwv4, time); + date_unix2dos(server, fattr->f_mtime.tv_sec, &date, &time); + WSET(req->rq_header, smb_vwv5, date); + WSET(req->rq_header, smb_vwv6, time); +#ifdef SMBFS_DEBUG_TIMESTAMP + printk(KERN_DEBUG "smb_proc_setattr_ext: date=%d, time=%d, mtime=%ld\n", + date, time, fattr->f_mtime); +#endif + + req->rq_flags |= SMB_REQ_NORETRY; + result = smb_request_ok(req, SMBsetattrE, 0, 0); + if (result < 0) + goto out_free; + result = 0; +out_free: + smb_rput(req); +out: + return result; +} + +/* + * Bugs Noted: + * (1) The TRANSACT2_SETPATHINFO message under Win NT 4.0 doesn't + * set the file's attribute flags. + */ +static int +smb_proc_setattr_trans2(struct smb_sb_info *server, + struct dentry *dir, struct smb_fattr *fattr) +{ + __u16 date, time; + char *p, *param; + int result; + char data[26]; + struct smb_request *req; + + result = -ENOMEM; + if (! (req = smb_alloc_request(server, PAGE_SIZE))) + goto out; + param = req->rq_buffer; + + WSET(param, 0, 1); /* Info level SMB_INFO_STANDARD */ + DSET(param, 2, 0); + result = smb_encode_path(server, param+6, SMB_MAXPATHLEN+1, dir, NULL); + if (result < 0) + goto out_free; + p = param + 6 + result; + + WSET(data, 0, 0); /* creation time */ + WSET(data, 2, 0); + date_unix2dos(server, fattr->f_atime.tv_sec, &date, &time); + WSET(data, 4, date); + WSET(data, 6, time); + date_unix2dos(server, fattr->f_mtime.tv_sec, &date, &time); + WSET(data, 8, date); + WSET(data, 10, time); +#ifdef SMBFS_DEBUG_TIMESTAMP + printk(KERN_DEBUG "setattr_trans2: %s/%s, date=%x, time=%x, mtime=%ld\n", + DENTRY_PATH(dir), date, time, fattr->f_mtime); +#endif + DSET(data, 12, 0); /* size */ + DSET(data, 16, 0); /* blksize */ + WSET(data, 20, 0); /* attr */ + DSET(data, 22, 0); /* ULONG EA size */ + + req->rq_trans2_command = TRANSACT2_SETPATHINFO; + req->rq_ldata = 26; + req->rq_data = data; + req->rq_lparm = p - param; + req->rq_parm = param; + req->rq_flags = 0; + result = smb_add_request(req); + if (result < 0) + goto out_free; + result = 0; + if (req->rq_rcls != 0) + result = smb_errno(req); + +out_free: + smb_rput(req); +out: + return result; +} + +/* + * 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, + unsigned int major, unsigned 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); + DSET(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 + * setting both attributes and timestamps at once. + * + * Bugs Noted: + * (1) Win 95 doesn't support the TRANSACT2_SETFILEINFO message + * with info level 1 (INFO_STANDARD). + * (2) Win 95 seems not to support setting directory timestamps. + * (3) Under the core protocol apparently the only way to set the + * timestamp is to open and close the file. + */ +int +smb_proc_settime(struct dentry *dentry, struct smb_fattr *fattr) +{ + struct smb_sb_info *server = server_from_dentry(dentry); + struct inode *inode = dentry->d_inode; + int result; + + VERBOSE("setting %s/%s, open=%d\n", + DENTRY_PATH(dentry), smb_is_open(inode)); + + /* setting the time on a Win95 server fails (tridge) */ + if (server->opt.protocol >= SMB_PROTOCOL_LANMAN2 && + !(server->mnt->flags & SMB_MOUNT_WIN95)) { + if (smb_is_open(inode) && SMB_I(inode)->access != SMB_O_RDONLY) + result = smb_proc_setattr_ext(server, inode, fattr); + else + result = smb_proc_setattr_trans2(server, dentry, fattr); + } else { + /* + * Fail silently on directories ... timestamp can't be set? + */ + result = 0; + if (S_ISREG(inode->i_mode)) { + /* + * Set the mtime by opening and closing the file. + * Note that the file is opened read-only, but this + * still allows us to set the date (tridge) + */ + result = -EACCES; + if (!smb_is_open(inode)) + smb_proc_open(server, dentry, SMB_O_RDONLY); + if (smb_is_open(inode)) { + inode->i_mtime = fattr->f_mtime; + result = smb_proc_close_inode(server, inode); + } + } + } + + return result; +} + +int +smb_proc_dskattr(struct dentry *dentry, struct kstatfs *attr) +{ + struct smb_sb_info *server = SMB_SB(dentry->d_sb); + int result; + char *p; + long unit; + struct smb_request *req; + + result = -ENOMEM; + if (! (req = smb_alloc_request(server, 0))) + goto out; + + smb_setup_header(req, SMBdskattr, 0, 0); + if ((result = smb_request_ok(req, SMBdskattr, 5, 0)) < 0) + goto out_free; + p = SMB_VWV(req->rq_header); + unit = (WVAL(p, 2) * WVAL(p, 4)) >> SMB_ST_BLKSHIFT; + attr->f_blocks = WVAL(p, 0) * unit; + attr->f_bsize = SMB_ST_BLKSIZE; + attr->f_bavail = attr->f_bfree = WVAL(p, 6) * unit; + result = 0; + +out_free: + smb_rput(req); +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, req->rq_rcls, req->rq_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, req->rq_rcls, req->rq_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, req->rq_rcls, req->rq_err); + result = 0; + +out_free: + smb_rput(req); +out: + return result; +} + +static 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) +{ + memcpy(dst, src, sizeof(void *) * SMB_OPS_NUM_STATIC); +} + +/* < LANMAN2 */ +static struct smb_ops smb_ops_core = +{ + .read = smb_proc_read, + .write = smb_proc_write, + .readdir = smb_proc_readdir_short, + .getattr = smb_proc_getattr_core, + .truncate = smb_proc_trunc32, +}; + +/* LANMAN2, OS/2, others? */ +static struct smb_ops smb_ops_os2 = +{ + .read = smb_proc_read, + .write = smb_proc_write, + .readdir = smb_proc_readdir_long, + .getattr = smb_proc_getattr_trans2_std, + .truncate = smb_proc_trunc32, +}; + +/* Win95, and possibly some NetApp versions too */ +static struct smb_ops smb_ops_win95 = +{ + .read = smb_proc_read, /* does not support 12word readX */ + .write = smb_proc_write, + .readdir = smb_proc_readdir_long, + .getattr = smb_proc_getattr_95, + .truncate = smb_proc_trunc95, +}; + +/* Samba, NT4 and NT5 */ +static struct smb_ops smb_ops_winNT = +{ + .read = smb_proc_readX, + .write = smb_proc_writeX, + .readdir = smb_proc_readdir_long, + .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, +}; + +/* Place holder until real ops are in place */ +static struct smb_ops smb_ops_null = +{ + .readdir = smb_proc_readdir_null, + .getattr = smb_proc_getattr_null, +}; + +void smb_install_null_ops(struct smb_ops *ops) +{ + install_ops(ops, &smb_ops_null); +} diff --git a/drivers/staging/smbfs/proto.h b/drivers/staging/smbfs/proto.h new file mode 100644 index 000000000000..05939a6f43e6 --- /dev/null +++ b/drivers/staging/smbfs/proto.h @@ -0,0 +1,87 @@ +/* + * Autogenerated with cproto on: Sat Sep 13 17:18:51 CEST 2003 + */ + +struct smb_request; +struct sock; +struct statfs; + +/* proc.c */ +extern int smb_setcodepage(struct smb_sb_info *server, struct smb_nls_codepage *cp); +extern __u32 smb_len(__u8 *p); +extern int smb_get_rsize(struct smb_sb_info *server); +extern int smb_get_wsize(struct smb_sb_info *server); +extern int smb_errno(struct smb_request *req); +extern int smb_newconn(struct smb_sb_info *server, struct smb_conn_opt *opt); +extern __u8 *smb_setup_header(struct smb_request *req, __u8 command, __u16 wct, __u16 bcc); +extern int smb_open(struct dentry *dentry, int wish); +extern int smb_close(struct inode *ino); +extern int smb_close_fileid(struct dentry *dentry, __u16 fileid); +extern int smb_proc_create(struct dentry *dentry, __u16 attr, time_t ctime, __u16 *fileid); +extern int smb_proc_mv(struct dentry *old_dentry, struct dentry *new_dentry); +extern int smb_proc_mkdir(struct dentry *dentry); +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, + struct super_block *sb); +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, unsigned int major, unsigned int minor); +extern int smb_proc_settime(struct dentry *dentry, struct smb_fattr *fattr); +extern int smb_proc_dskattr(struct dentry *dentry, struct kstatfs *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 void smb_install_null_ops(struct smb_ops *ops); +/* dir.c */ +extern const struct file_operations smb_dir_operations; +extern const struct inode_operations smb_dir_inode_operations; +extern const 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 */ +extern void smb_invalid_dir_cache(struct inode *dir); +extern void smb_invalidate_dircache_entries(struct dentry *parent); +extern struct dentry *smb_dget_fpos(struct dentry *dentry, struct dentry *parent, unsigned long fpos); +extern int smb_fill_cache(struct file *filp, void *dirent, filldir_t filldir, struct smb_cache_control *ctrl, struct qstr *qname, struct smb_fattr *entry); +/* sock.c */ +extern void smb_data_ready(struct sock *sk, int len); +extern int smb_valid_socket(struct inode *inode); +extern void smb_close_socket(struct smb_sb_info *server); +extern int smb_recv_available(struct smb_sb_info *server); +extern int smb_receive_header(struct smb_sb_info *server); +extern int smb_receive_drop(struct smb_sb_info *server); +extern int smb_receive(struct smb_sb_info *server, struct smb_request *req); +extern int smb_send_request(struct smb_request *req); +/* inode.c */ +extern struct inode *smb_iget(struct super_block *sb, struct smb_fattr *fattr); +extern void smb_get_inode_attr(struct inode *inode, struct smb_fattr *fattr); +extern void smb_set_inode_attr(struct inode *inode, struct smb_fattr *fattr); +extern void smb_invalidate_inodes(struct smb_sb_info *server); +extern int smb_revalidate_inode(struct dentry *dentry); +extern int smb_getattr(struct vfsmount *mnt, struct dentry *dentry, struct kstat *stat); +extern int smb_notify_change(struct dentry *dentry, struct iattr *attr); +/* file.c */ +extern const struct address_space_operations smb_file_aops; +extern const struct file_operations smb_file_operations; +extern const struct inode_operations smb_file_inode_operations; +/* ioctl.c */ +extern long smb_ioctl(struct file *filp, unsigned int cmd, unsigned long arg); +/* smbiod.c */ +extern void smbiod_wake_up(void); +extern int smbiod_register_server(struct smb_sb_info *server); +extern void smbiod_unregister_server(struct smb_sb_info *server); +extern void smbiod_flush(struct smb_sb_info *server); +extern int smbiod_retry(struct smb_sb_info *server); +/* request.c */ +extern int smb_init_request_cache(void); +extern void smb_destroy_request_cache(void); +extern struct smb_request *smb_alloc_request(struct smb_sb_info *server, int bufsize); +extern void smb_rput(struct smb_request *req); +extern int smb_add_request(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_symlink(struct inode *inode, struct dentry *dentry, const char *oldname); +extern const struct inode_operations smb_link_inode_operations; diff --git a/drivers/staging/smbfs/request.c b/drivers/staging/smbfs/request.c new file mode 100644 index 000000000000..3e7716864306 --- /dev/null +++ b/drivers/staging/smbfs/request.c @@ -0,0 +1,817 @@ +/* + * request.c + * + * Copyright (C) 2001 by Urban Widmark + * + * Please add a note about your changes to smbfs in the ChangeLog file. + */ + +#include +#include +#include +#include +#include +#include + +#include "smb_fs.h" +#include "smbno.h" +#include "smb_mount.h" +#include "smb_debug.h" +#include "request.h" +#include "proto.h" + +/* #define SMB_SLAB_DEBUG (SLAB_RED_ZONE | SLAB_POISON) */ +#define SMB_SLAB_DEBUG 0 + +/* cache for request structures */ +static struct kmem_cache *req_cachep; + +static int smb_request_send_req(struct smb_request *req); + +/* + /proc/slabinfo: + name, active, num, objsize, active_slabs, num_slaps, #pages +*/ + + +int smb_init_request_cache(void) +{ + req_cachep = kmem_cache_create("smb_request", + sizeof(struct smb_request), 0, + SMB_SLAB_DEBUG | SLAB_HWCACHE_ALIGN, + NULL); + if (req_cachep == NULL) + return -ENOMEM; + + return 0; +} + +void smb_destroy_request_cache(void) +{ + kmem_cache_destroy(req_cachep); +} + +/* + * Allocate and initialise a request structure + */ +static struct smb_request *smb_do_alloc_request(struct smb_sb_info *server, + int bufsize) +{ + struct smb_request *req; + unsigned char *buf = NULL; + + req = kmem_cache_zalloc(req_cachep, GFP_KERNEL); + VERBOSE("allocating request: %p\n", req); + if (!req) + goto out; + + if (bufsize > 0) { + buf = kmalloc(bufsize, GFP_NOFS); + if (!buf) { + kmem_cache_free(req_cachep, req); + return NULL; + } + } + + req->rq_buffer = buf; + req->rq_bufsize = bufsize; + req->rq_server = server; + init_waitqueue_head(&req->rq_wait); + INIT_LIST_HEAD(&req->rq_queue); + atomic_set(&req->rq_count, 1); + +out: + return req; +} + +struct smb_request *smb_alloc_request(struct smb_sb_info *server, int bufsize) +{ + struct smb_request *req = NULL; + + for (;;) { + atomic_inc(&server->nr_requests); + if (atomic_read(&server->nr_requests) <= MAX_REQUEST_HARD) { + req = smb_do_alloc_request(server, bufsize); + if (req != NULL) + break; + } + +#if 0 + /* + * Try to free up at least one request in order to stay + * below the hard limit + */ + if (nfs_try_to_free_pages(server)) + continue; + + if (fatal_signal_pending(current)) + return ERR_PTR(-ERESTARTSYS); + current->policy = SCHED_YIELD; + schedule(); +#else + /* FIXME: we want something like nfs does above, but that + requires changes to all callers and can wait. */ + break; +#endif + } + return req; +} + +static void smb_free_request(struct smb_request *req) +{ + atomic_dec(&req->rq_server->nr_requests); + if (req->rq_buffer && !(req->rq_flags & SMB_REQ_STATIC)) + kfree(req->rq_buffer); + kfree(req->rq_trans2buffer); + kmem_cache_free(req_cachep, req); +} + +/* + * What prevents a rget to race with a rput? The count must never drop to zero + * while it is in use. Only rput if it is ok that it is free'd. + */ +static void smb_rget(struct smb_request *req) +{ + atomic_inc(&req->rq_count); +} +void smb_rput(struct smb_request *req) +{ + if (atomic_dec_and_test(&req->rq_count)) { + list_del_init(&req->rq_queue); + smb_free_request(req); + } +} + +/* setup to receive the data part of the SMB */ +static int smb_setup_bcc(struct smb_request *req) +{ + int result = 0; + req->rq_rlen = smb_len(req->rq_header) + 4 - req->rq_bytes_recvd; + + if (req->rq_rlen > req->rq_bufsize) { + PARANOIA("Packet too large %d > %d\n", + req->rq_rlen, req->rq_bufsize); + return -ENOBUFS; + } + + req->rq_iov[0].iov_base = req->rq_buffer; + req->rq_iov[0].iov_len = req->rq_rlen; + req->rq_iovlen = 1; + + return result; +} + +/* + * Prepare a "normal" request structure. + */ +static int smb_setup_request(struct smb_request *req) +{ + int len = smb_len(req->rq_header) + 4; + req->rq_slen = len; + + /* if we expect a data part in the reply we set the iov's to read it */ + if (req->rq_resp_bcc) + req->rq_setup_read = smb_setup_bcc; + + /* This tries to support re-using the same request */ + req->rq_bytes_sent = 0; + req->rq_rcls = 0; + req->rq_err = 0; + req->rq_errno = 0; + req->rq_fragment = 0; + kfree(req->rq_trans2buffer); + req->rq_trans2buffer = NULL; + + return 0; +} + +/* + * Prepare a transaction2 request structure + */ +static int smb_setup_trans2request(struct smb_request *req) +{ + struct smb_sb_info *server = req->rq_server; + int mparam, mdata; + static unsigned char padding[4]; + + /* I know the following is very ugly, but I want to build the + smb packet as efficiently as possible. */ + + const int smb_parameters = 15; + const int header = SMB_HEADER_LEN + 2 * smb_parameters + 2; + const int oparam = ALIGN(header + 3, sizeof(u32)); + const int odata = ALIGN(oparam + req->rq_lparm, sizeof(u32)); + const int bcc = (req->rq_data ? odata + req->rq_ldata : + oparam + req->rq_lparm) - header; + + if ((bcc + oparam) > server->opt.max_xmit) + return -ENOMEM; + smb_setup_header(req, SMBtrans2, smb_parameters, bcc); + + /* + * max parameters + max data + max setup == bufsize to make NT4 happy + * and not abort the transfer or split into multiple responses. It also + * makes smbfs happy as handling packets larger than the buffer size + * is extra work. + * + * OS/2 is probably going to hate me for this ... + */ + mparam = SMB_TRANS2_MAX_PARAM; + mdata = req->rq_bufsize - mparam; + + mdata = server->opt.max_xmit - mparam - 100; + if (mdata < 1024) { + mdata = 1024; + mparam = 20; + } + +#if 0 + /* NT/win2k has ~4k max_xmit, so with this we request more than it wants + to return as one SMB. Useful for testing the fragmented trans2 + handling. */ + mdata = 8192; +#endif + + WSET(req->rq_header, smb_tpscnt, req->rq_lparm); + WSET(req->rq_header, smb_tdscnt, req->rq_ldata); + WSET(req->rq_header, smb_mprcnt, mparam); + WSET(req->rq_header, smb_mdrcnt, mdata); + WSET(req->rq_header, smb_msrcnt, 0); /* max setup always 0 ? */ + WSET(req->rq_header, smb_flags, 0); + DSET(req->rq_header, smb_timeout, 0); + WSET(req->rq_header, smb_pscnt, req->rq_lparm); + WSET(req->rq_header, smb_psoff, oparam - 4); + WSET(req->rq_header, smb_dscnt, req->rq_ldata); + WSET(req->rq_header, smb_dsoff, req->rq_data ? odata - 4 : 0); + *(req->rq_header + smb_suwcnt) = 0x01; /* setup count */ + *(req->rq_header + smb_suwcnt + 1) = 0x00; /* reserved */ + WSET(req->rq_header, smb_setup0, req->rq_trans2_command); + + req->rq_iovlen = 2; + req->rq_iov[0].iov_base = (void *) req->rq_header; + req->rq_iov[0].iov_len = oparam; + req->rq_iov[1].iov_base = (req->rq_parm==NULL) ? padding : req->rq_parm; + req->rq_iov[1].iov_len = req->rq_lparm; + req->rq_slen = oparam + req->rq_lparm; + + if (req->rq_data) { + req->rq_iovlen += 2; + req->rq_iov[2].iov_base = padding; + req->rq_iov[2].iov_len = odata - oparam - req->rq_lparm; + req->rq_iov[3].iov_base = req->rq_data; + req->rq_iov[3].iov_len = req->rq_ldata; + req->rq_slen = odata + req->rq_ldata; + } + + /* always a data part for trans2 replies */ + req->rq_setup_read = smb_setup_bcc; + + return 0; +} + +/* + * Add a request and tell smbiod to process it + */ +int smb_add_request(struct smb_request *req) +{ + long timeleft; + struct smb_sb_info *server = req->rq_server; + int result = 0; + + smb_setup_request(req); + if (req->rq_trans2_command) { + if (req->rq_buffer == NULL) { + PARANOIA("trans2 attempted without response buffer!\n"); + return -EIO; + } + result = smb_setup_trans2request(req); + } + if (result < 0) + return result; + +#ifdef SMB_DEBUG_PACKET_SIZE + add_xmit_stats(req); +#endif + + /* add 'req' to the queue of requests */ + if (smb_lock_server_interruptible(server)) + return -EINTR; + + /* + * Try to send the request as the process. If that fails we queue the + * request and let smbiod send it later. + */ + + /* FIXME: each server has a number on the maximum number of parallel + requests. 10, 50 or so. We should not allow more requests to be + active. */ + if (server->mid > 0xf000) + server->mid = 0; + req->rq_mid = server->mid++; + WSET(req->rq_header, smb_mid, req->rq_mid); + + result = 0; + if (server->state == CONN_VALID) { + if (list_empty(&server->xmitq)) + result = smb_request_send_req(req); + if (result < 0) { + /* Connection lost? */ + server->conn_error = result; + server->state = CONN_INVALID; + } + } + if (result != 1) + list_add_tail(&req->rq_queue, &server->xmitq); + smb_rget(req); + + if (server->state != CONN_VALID) + smbiod_retry(server); + + smb_unlock_server(server); + + smbiod_wake_up(); + + timeleft = wait_event_interruptible_timeout(req->rq_wait, + req->rq_flags & SMB_REQ_RECEIVED, 30*HZ); + if (!timeleft || signal_pending(current)) { + /* + * On timeout or on interrupt we want to try and remove the + * request from the recvq/xmitq. + * First check if the request is still part of a queue. (May + * have been removed by some error condition) + */ + smb_lock_server(server); + if (!list_empty(&req->rq_queue)) { + list_del_init(&req->rq_queue); + smb_rput(req); + } + smb_unlock_server(server); + } + + if (!timeleft) { + PARANOIA("request [%p, mid=%d] timed out!\n", + req, req->rq_mid); + VERBOSE("smb_com: %02x\n", *(req->rq_header + smb_com)); + VERBOSE("smb_rcls: %02x\n", *(req->rq_header + smb_rcls)); + VERBOSE("smb_flg: %02x\n", *(req->rq_header + smb_flg)); + VERBOSE("smb_tid: %04x\n", WVAL(req->rq_header, smb_tid)); + VERBOSE("smb_pid: %04x\n", WVAL(req->rq_header, smb_pid)); + VERBOSE("smb_uid: %04x\n", WVAL(req->rq_header, smb_uid)); + VERBOSE("smb_mid: %04x\n", WVAL(req->rq_header, smb_mid)); + VERBOSE("smb_wct: %02x\n", *(req->rq_header + smb_wct)); + + req->rq_rcls = ERRSRV; + req->rq_err = ERRtimeout; + + /* Just in case it was "stuck" */ + smbiod_wake_up(); + } + VERBOSE("woke up, rcls=%d\n", req->rq_rcls); + + if (req->rq_rcls != 0) + req->rq_errno = smb_errno(req); + if (signal_pending(current)) + req->rq_errno = -ERESTARTSYS; + return req->rq_errno; +} + +/* + * Send a request and place it on the recvq if successfully sent. + * Must be called with the server lock held. + */ +static int smb_request_send_req(struct smb_request *req) +{ + struct smb_sb_info *server = req->rq_server; + int result; + + if (req->rq_bytes_sent == 0) { + WSET(req->rq_header, smb_tid, server->opt.tid); + WSET(req->rq_header, smb_pid, 1); + WSET(req->rq_header, smb_uid, server->opt.server_uid); + } + + result = smb_send_request(req); + if (result < 0 && result != -EAGAIN) + goto out; + + result = 0; + if (!(req->rq_flags & SMB_REQ_TRANSMITTED)) + goto out; + + list_move_tail(&req->rq_queue, &server->recvq); + result = 1; +out: + return result; +} + +/* + * Sends one request for this server. (smbiod) + * Must be called with the server lock held. + * Returns: <0 on error + * 0 if no request could be completely sent + * 1 if all data for one request was sent + */ +int smb_request_send_server(struct smb_sb_info *server) +{ + struct list_head *head; + struct smb_request *req; + int result; + + if (server->state != CONN_VALID) + return 0; + + /* dequeue first request, if any */ + req = NULL; + head = server->xmitq.next; + if (head != &server->xmitq) { + req = list_entry(head, struct smb_request, rq_queue); + } + if (!req) + return 0; + + result = smb_request_send_req(req); + if (result < 0) { + server->conn_error = result; + list_move(&req->rq_queue, &server->xmitq); + result = -EIO; + goto out; + } + +out: + return result; +} + +/* + * Try to find a request matching this "mid". Typically the first entry will + * be the matching one. + */ +static struct smb_request *find_request(struct smb_sb_info *server, int mid) +{ + struct list_head *tmp; + struct smb_request *req = NULL; + + list_for_each(tmp, &server->recvq) { + req = list_entry(tmp, struct smb_request, rq_queue); + if (req->rq_mid == mid) { + break; + } + req = NULL; + } + + if (!req) { + VERBOSE("received reply with mid %d but no request!\n", + WVAL(server->header, smb_mid)); + server->rstate = SMB_RECV_DROP; + } + + return req; +} + +/* + * Called when we have read the smb header and believe this is a response. + */ +static int smb_init_request(struct smb_sb_info *server, struct smb_request *req) +{ + int hdrlen, wct; + + memcpy(req->rq_header, server->header, SMB_HEADER_LEN); + + wct = *(req->rq_header + smb_wct); + if (wct > 20) { + PARANOIA("wct too large, %d > 20\n", wct); + server->rstate = SMB_RECV_DROP; + return 0; + } + + req->rq_resp_wct = wct; + hdrlen = SMB_HEADER_LEN + wct*2 + 2; + VERBOSE("header length: %d smb_wct: %2d\n", hdrlen, wct); + + req->rq_bytes_recvd = SMB_HEADER_LEN; + req->rq_rlen = hdrlen; + req->rq_iov[0].iov_base = req->rq_header; + req->rq_iov[0].iov_len = hdrlen; + req->rq_iovlen = 1; + server->rstate = SMB_RECV_PARAM; + +#ifdef SMB_DEBUG_PACKET_SIZE + add_recv_stats(smb_len(server->header)); +#endif + return 0; +} + +/* + * Reads the SMB parameters + */ +static int smb_recv_param(struct smb_sb_info *server, struct smb_request *req) +{ + int result; + + result = smb_receive(server, req); + if (result < 0) + return result; + if (req->rq_bytes_recvd < req->rq_rlen) + return 0; + + VERBOSE("result: %d smb_bcc: %04x\n", result, + WVAL(req->rq_header, SMB_HEADER_LEN + + (*(req->rq_header + smb_wct) * 2))); + + result = 0; + req->rq_iov[0].iov_base = NULL; + req->rq_rlen = 0; + if (req->rq_callback) + req->rq_callback(req); + else if (req->rq_setup_read) + result = req->rq_setup_read(req); + if (result < 0) { + server->rstate = SMB_RECV_DROP; + return result; + } + + server->rstate = req->rq_rlen > 0 ? SMB_RECV_DATA : SMB_RECV_END; + + req->rq_bytes_recvd = 0; // recvd out of the iov + + VERBOSE("rlen: %d\n", req->rq_rlen); + if (req->rq_rlen < 0) { + PARANOIA("Parameters read beyond end of packet!\n"); + server->rstate = SMB_RECV_END; + return -EIO; + } + return 0; +} + +/* + * Reads the SMB data + */ +static int smb_recv_data(struct smb_sb_info *server, struct smb_request *req) +{ + int result; + + result = smb_receive(server, req); + if (result < 0) + goto out; + if (req->rq_bytes_recvd < req->rq_rlen) + goto out; + server->rstate = SMB_RECV_END; +out: + VERBOSE("result: %d\n", result); + return result; +} + +/* + * Receive a transaction2 response + * Return: 0 if the response has been fully read + * 1 if there are further "fragments" to read + * <0 if there is an error + */ +static int smb_recv_trans2(struct smb_sb_info *server, struct smb_request *req) +{ + unsigned char *inbuf; + unsigned int parm_disp, parm_offset, parm_count, parm_tot; + unsigned int data_disp, data_offset, data_count, data_tot; + int hdrlen = SMB_HEADER_LEN + req->rq_resp_wct*2 - 2; + + VERBOSE("handling trans2\n"); + + inbuf = req->rq_header; + data_tot = WVAL(inbuf, smb_tdrcnt); + parm_tot = WVAL(inbuf, smb_tprcnt); + parm_disp = WVAL(inbuf, smb_prdisp); + parm_offset = WVAL(inbuf, smb_proff); + parm_count = WVAL(inbuf, smb_prcnt); + data_disp = WVAL(inbuf, smb_drdisp); + data_offset = WVAL(inbuf, smb_droff); + data_count = WVAL(inbuf, smb_drcnt); + + /* Modify offset for the split header/buffer we use */ + if (data_count || data_offset) { + if (unlikely(data_offset < hdrlen)) + goto out_bad_data; + else + data_offset -= hdrlen; + } + if (parm_count || parm_offset) { + if (unlikely(parm_offset < hdrlen)) + goto out_bad_parm; + else + parm_offset -= hdrlen; + } + + if (parm_count == parm_tot && data_count == data_tot) { + /* + * This packet has all the trans2 data. + * + * We setup the request so that this will be the common + * case. It may be a server error to not return a + * response that fits. + */ + VERBOSE("single trans2 response " + "dcnt=%u, pcnt=%u, doff=%u, poff=%u\n", + data_count, parm_count, + data_offset, parm_offset); + req->rq_ldata = data_count; + req->rq_lparm = parm_count; + req->rq_data = req->rq_buffer + data_offset; + req->rq_parm = req->rq_buffer + parm_offset; + if (unlikely(parm_offset + parm_count > req->rq_rlen)) + goto out_bad_parm; + if (unlikely(data_offset + data_count > req->rq_rlen)) + goto out_bad_data; + return 0; + } + + VERBOSE("multi trans2 response " + "frag=%d, dcnt=%u, pcnt=%u, doff=%u, poff=%u\n", + req->rq_fragment, + data_count, parm_count, + data_offset, parm_offset); + + if (!req->rq_fragment) { + int buf_len; + + /* We got the first trans2 fragment */ + req->rq_fragment = 1; + req->rq_total_data = data_tot; + req->rq_total_parm = parm_tot; + req->rq_ldata = 0; + req->rq_lparm = 0; + + buf_len = data_tot + parm_tot; + if (buf_len > SMB_MAX_PACKET_SIZE) + goto out_too_long; + + req->rq_trans2bufsize = buf_len; + req->rq_trans2buffer = kzalloc(buf_len, GFP_NOFS); + if (!req->rq_trans2buffer) + goto out_no_mem; + + req->rq_parm = req->rq_trans2buffer; + req->rq_data = req->rq_trans2buffer + parm_tot; + } else if (unlikely(req->rq_total_data < data_tot || + req->rq_total_parm < parm_tot)) + goto out_data_grew; + + if (unlikely(parm_disp + parm_count > req->rq_total_parm || + parm_offset + parm_count > req->rq_rlen)) + goto out_bad_parm; + if (unlikely(data_disp + data_count > req->rq_total_data || + data_offset + data_count > req->rq_rlen)) + goto out_bad_data; + + inbuf = req->rq_buffer; + memcpy(req->rq_parm + parm_disp, inbuf + parm_offset, parm_count); + memcpy(req->rq_data + data_disp, inbuf + data_offset, data_count); + + req->rq_ldata += data_count; + req->rq_lparm += parm_count; + + /* + * Check whether we've received all of the data. Note that + * we use the packet totals -- total lengths might shrink! + */ + if (req->rq_ldata >= data_tot && req->rq_lparm >= parm_tot) { + req->rq_ldata = data_tot; + req->rq_lparm = parm_tot; + return 0; + } + return 1; + +out_too_long: + printk(KERN_ERR "smb_trans2: data/param too long, data=%u, parm=%u\n", + data_tot, parm_tot); + goto out_EIO; +out_no_mem: + printk(KERN_ERR "smb_trans2: couldn't allocate data area of %d bytes\n", + req->rq_trans2bufsize); + req->rq_errno = -ENOMEM; + goto out; +out_data_grew: + printk(KERN_ERR "smb_trans2: data/params grew!\n"); + goto out_EIO; +out_bad_parm: + printk(KERN_ERR "smb_trans2: invalid parms, disp=%u, cnt=%u, tot=%u, ofs=%u\n", + parm_disp, parm_count, parm_tot, parm_offset); + goto out_EIO; +out_bad_data: + printk(KERN_ERR "smb_trans2: invalid data, disp=%u, cnt=%u, tot=%u, ofs=%u\n", + data_disp, data_count, data_tot, data_offset); +out_EIO: + req->rq_errno = -EIO; +out: + return req->rq_errno; +} + +/* + * State machine for receiving responses. We handle the fact that we can't + * read the full response in one try by having states telling us how much we + * have read. + * + * Must be called with the server lock held (only called from smbiod). + * + * Return: <0 on error + */ +int smb_request_recv(struct smb_sb_info *server) +{ + struct smb_request *req = NULL; + int result = 0; + + if (smb_recv_available(server) <= 0) + return 0; + + VERBOSE("state: %d\n", server->rstate); + switch (server->rstate) { + case SMB_RECV_DROP: + result = smb_receive_drop(server); + if (result < 0) + break; + if (server->rstate == SMB_RECV_DROP) + break; + server->rstate = SMB_RECV_START; + /* fallthrough */ + case SMB_RECV_START: + server->smb_read = 0; + server->rstate = SMB_RECV_HEADER; + /* fallthrough */ + case SMB_RECV_HEADER: + result = smb_receive_header(server); + if (result < 0) + break; + if (server->rstate == SMB_RECV_HEADER) + break; + if (! (*(server->header + smb_flg) & SMB_FLAGS_REPLY) ) { + server->rstate = SMB_RECV_REQUEST; + break; + } + if (server->rstate != SMB_RECV_HCOMPLETE) + break; + /* fallthrough */ + case SMB_RECV_HCOMPLETE: + req = find_request(server, WVAL(server->header, smb_mid)); + if (!req) + break; + smb_init_request(server, req); + req->rq_rcls = *(req->rq_header + smb_rcls); + req->rq_err = WVAL(req->rq_header, smb_err); + if (server->rstate != SMB_RECV_PARAM) + break; + /* fallthrough */ + case SMB_RECV_PARAM: + if (!req) + req = find_request(server,WVAL(server->header,smb_mid)); + if (!req) + break; + result = smb_recv_param(server, req); + if (result < 0) + break; + if (server->rstate != SMB_RECV_DATA) + break; + /* fallthrough */ + case SMB_RECV_DATA: + if (!req) + req = find_request(server,WVAL(server->header,smb_mid)); + if (!req) + break; + result = smb_recv_data(server, req); + if (result < 0) + break; + break; + + /* We should never be called with any of these states */ + case SMB_RECV_END: + case SMB_RECV_REQUEST: + BUG(); + } + + if (result < 0) { + /* We saw an error */ + return result; + } + + if (server->rstate != SMB_RECV_END) + return 0; + + result = 0; + if (req->rq_trans2_command && req->rq_rcls == SUCCESS) + result = smb_recv_trans2(server, req); + + /* + * Response completely read. Drop any extra bytes sent by the server. + * (Yes, servers sometimes add extra bytes to responses) + */ + VERBOSE("smb_len: %d smb_read: %d\n", + server->smb_len, server->smb_read); + if (server->smb_read < server->smb_len) + smb_receive_drop(server); + + server->rstate = SMB_RECV_START; + + if (!result) { + list_del_init(&req->rq_queue); + req->rq_flags |= SMB_REQ_RECEIVED; + smb_rput(req); + wake_up_interruptible(&req->rq_wait); + } + return 0; +} diff --git a/drivers/staging/smbfs/request.h b/drivers/staging/smbfs/request.h new file mode 100644 index 000000000000..efb21451e7c9 --- /dev/null +++ b/drivers/staging/smbfs/request.h @@ -0,0 +1,70 @@ +#include +#include +#include +#include + +struct smb_request { + struct list_head rq_queue; /* recvq or xmitq for the server */ + + atomic_t rq_count; + + wait_queue_head_t rq_wait; + int rq_flags; + int rq_mid; /* multiplex ID, set by request.c */ + + struct smb_sb_info *rq_server; + + /* header + word count + parameter words + byte count */ + unsigned char rq_header[SMB_HEADER_LEN + 20*2 + 2]; + + int rq_bufsize; + unsigned char *rq_buffer; + + /* FIXME: this is not good enough for merging IO requests. */ + unsigned char *rq_page; + int rq_rsize; + + int rq_resp_wct; + int rq_resp_bcc; + + int rq_rlen; + int rq_bytes_recvd; + + int rq_slen; + int rq_bytes_sent; + + int rq_iovlen; + struct kvec rq_iov[4]; + + int (*rq_setup_read) (struct smb_request *); + void (*rq_callback) (struct smb_request *); + + /* ------ trans2 stuff ------ */ + + u16 rq_trans2_command; /* 0 if not a trans2 request */ + unsigned int rq_ldata; + unsigned char *rq_data; + unsigned int rq_lparm; + unsigned char *rq_parm; + + int rq_fragment; + u32 rq_total_data; + u32 rq_total_parm; + int rq_trans2bufsize; + unsigned char *rq_trans2buffer; + + /* ------ response ------ */ + + unsigned short rq_rcls; + unsigned short rq_err; + int rq_errno; +}; + +#define SMB_REQ_STATIC 0x0001 /* rq_buffer is static */ +#define SMB_REQ_NORETRY 0x0002 /* request is invalid after retry */ + +#define SMB_REQ_TRANSMITTED 0x4000 /* all data has been sent */ +#define SMB_REQ_RECEIVED 0x8000 /* reply received, smbiod is done */ + +#define xSMB_REQ_NOREPLY 0x0004 /* we don't want the reply (if any) */ +#define xSMB_REQ_NORECEIVER 0x0008 /* caller doesn't wait for response */ diff --git a/drivers/staging/smbfs/smb.h b/drivers/staging/smbfs/smb.h new file mode 100644 index 000000000000..82fefddc5987 --- /dev/null +++ b/drivers/staging/smbfs/smb.h @@ -0,0 +1,118 @@ +/* + * smb.h + * + * Copyright (C) 1995, 1996 by Paal-Kr. Engstad and Volker Lendecke + * Copyright (C) 1997 by Volker Lendecke + * + */ + +#ifndef _LINUX_SMB_H +#define _LINUX_SMB_H + +#include +#include +#ifdef __KERNEL__ +#include +#endif + +enum smb_protocol { + SMB_PROTOCOL_NONE, + SMB_PROTOCOL_CORE, + SMB_PROTOCOL_COREPLUS, + SMB_PROTOCOL_LANMAN1, + SMB_PROTOCOL_LANMAN2, + SMB_PROTOCOL_NT1 +}; + +enum smb_case_hndl { + SMB_CASE_DEFAULT, + SMB_CASE_LOWER, + SMB_CASE_UPPER +}; + +struct smb_dskattr { + __u16 total; + __u16 allocblocks; + __u16 blocksize; + __u16 free; +}; + +struct smb_conn_opt { + + /* The socket */ + unsigned int fd; + + enum smb_protocol protocol; + enum smb_case_hndl case_handling; + + /* Connection-Options */ + + __u32 max_xmit; + __u16 server_uid; + __u16 tid; + + /* The following are LANMAN 1.0 options */ + __u16 secmode; + __u16 maxmux; + __u16 maxvcs; + __u16 rawmode; + __u32 sesskey; + + /* The following are NT LM 0.12 options */ + __u32 maxraw; + __u32 capabilities; + __s16 serverzone; +}; + +#ifdef __KERNEL__ + +#define SMB_NLS_MAXNAMELEN 20 +struct smb_nls_codepage { + char local_name[SMB_NLS_MAXNAMELEN]; + char remote_name[SMB_NLS_MAXNAMELEN]; +}; + + +#define SMB_MAXNAMELEN 255 +#define SMB_MAXPATHLEN 1024 + +/* + * Contains all relevant data on a SMB networked file. + */ +struct smb_fattr { + __u16 attr; + + unsigned long f_ino; + umode_t f_mode; + nlink_t f_nlink; + uid_t f_uid; + gid_t f_gid; + dev_t f_rdev; + loff_t f_size; + struct timespec f_atime; + struct timespec f_mtime; + struct timespec f_ctime; + unsigned long f_blocks; + int f_unix; +}; + +enum smb_conn_state { + CONN_VALID, /* everything's fine */ + CONN_INVALID, /* Something went wrong, but did not + try to reconnect yet. */ + CONN_RETRIED, /* Tried a reconnection, but was refused */ + CONN_RETRYING /* Currently trying to reconnect */ +}; + +#define SMB_HEADER_LEN 37 /* includes everything up to, but not + * including smb_bcc */ + +#define SMB_INITIAL_PACKET_SIZE 4000 +#define SMB_MAX_PACKET_SIZE 32768 + +/* reserve this much space for trans2 parameters. Shouldn't have to be more + than 10 or so, but OS/2 seems happier like this. */ +#define SMB_TRANS2_MAX_PARAM 64 + +#endif +#endif diff --git a/drivers/staging/smbfs/smb_debug.h b/drivers/staging/smbfs/smb_debug.h new file mode 100644 index 000000000000..fc4b1a5dd755 --- /dev/null +++ b/drivers/staging/smbfs/smb_debug.h @@ -0,0 +1,34 @@ +/* + * Defines some debug macros for smbfs. + */ + +/* This makes a dentry parent/child name pair. Useful for debugging printk's */ +#define DENTRY_PATH(dentry) \ + (dentry)->d_parent->d_name.name,(dentry)->d_name.name + +/* + * safety checks that should never happen ??? + * these are normally enabled. + */ +#ifdef SMBFS_PARANOIA +# define PARANOIA(f, a...) printk(KERN_NOTICE "%s: " f, __func__ , ## a) +#else +# define PARANOIA(f, a...) do { ; } while(0) +#endif + +/* lots of debug messages */ +#ifdef SMBFS_DEBUG_VERBOSE +# define VERBOSE(f, a...) printk(KERN_DEBUG "%s: " f, __func__ , ## a) +#else +# define VERBOSE(f, a...) do { ; } while(0) +#endif + +/* + * "normal" debug messages, but not with a normal DEBUG define ... way + * too common name. + */ +#ifdef SMBFS_DEBUG +#define DEBUG1(f, a...) printk(KERN_DEBUG "%s: " f, __func__ , ## a) +#else +#define DEBUG1(f, a...) do { ; } while(0) +#endif diff --git a/drivers/staging/smbfs/smb_fs.h b/drivers/staging/smbfs/smb_fs.h new file mode 100644 index 000000000000..20a05c188eb9 --- /dev/null +++ b/drivers/staging/smbfs/smb_fs.h @@ -0,0 +1,153 @@ +/* + * smb_fs.h + * + * Copyright (C) 1995 by Paal-Kr. Engstad and Volker Lendecke + * Copyright (C) 1997 by Volker Lendecke + * + */ + +#ifndef _LINUX_SMB_FS_H +#define _LINUX_SMB_FS_H + +#include "smb.h" + +/* + * ioctl commands + */ +#define SMB_IOC_GETMOUNTUID _IOR('u', 1, __kernel_old_uid_t) +#define SMB_IOC_NEWCONN _IOW('u', 2, struct smb_conn_opt) + +/* __kernel_uid_t can never change, so we have to use __kernel_uid32_t */ +#define SMB_IOC_GETMOUNTUID32 _IOR('u', 3, __kernel_uid32_t) + + +#ifdef __KERNEL__ +#include "smb_fs_i.h" +#include "smb_fs_sb.h" +#include "smb_mount.h" + +#include +#include +#include +#include +#include + +static inline struct smb_sb_info *SMB_SB(struct super_block *sb) +{ + return sb->s_fs_info; +} + +static inline struct smb_inode_info *SMB_I(struct inode *inode) +{ + return container_of(inode, struct smb_inode_info, vfs_inode); +} + +/* macro names are short for word, double-word, long value (?) */ +#define WVAL(buf, pos) (get_unaligned_le16((u8 *)(buf) + (pos))) +#define DVAL(buf, pos) (get_unaligned_le32((u8 *)(buf) + (pos))) +#define LVAL(buf, pos) (get_unaligned_le64((u8 *)(buf) + (pos))) + +#define WSET(buf, pos, val) put_unaligned_le16((val), (u8 *)(buf) + (pos)) +#define DSET(buf, pos, val) put_unaligned_le32((val), (u8 *)(buf) + (pos)) +#define LSET(buf, pos, val) put_unaligned_le64((val), (u8 *)(buf) + (pos)) + +/* where to find the base of the SMB packet proper */ +#define smb_base(buf) ((u8 *)(((u8 *)(buf))+4)) + +/* + * Flags for the in-memory inode + */ +#define SMB_F_LOCALWRITE 0x02 /* file modified locally */ + + +/* NT1 protocol capability bits */ +#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 ... */ + + +/* + * This is the time we allow an inode, dentry or dir cache to live. It is bad + * for performance to have shorter ttl on an inode than on the cache. It can + * cause refresh on each inode for a dir listing ... one-by-one + */ +#define SMB_MAX_AGE(server) (((server)->mnt->ttl * HZ) / 1000) + +static inline void +smb_age_dentry(struct smb_sb_info *server, struct dentry *dentry) +{ + dentry->d_time = jiffies - SMB_MAX_AGE(server); +} + +struct smb_cache_head { + time_t mtime; /* unused */ + unsigned long time; /* cache age */ + unsigned long end; /* last valid fpos in cache */ + int eof; +}; + +#define SMB_DIRCACHE_SIZE ((int)(PAGE_CACHE_SIZE/sizeof(struct dentry *))) +union smb_dir_cache { + struct smb_cache_head head; + struct dentry *dentry[SMB_DIRCACHE_SIZE]; +}; + +#define SMB_FIRSTCACHE_SIZE ((int)((SMB_DIRCACHE_SIZE * \ + sizeof(struct dentry *) - sizeof(struct smb_cache_head)) / \ + sizeof(struct dentry *))) + +#define SMB_DIRCACHE_START (SMB_DIRCACHE_SIZE - SMB_FIRSTCACHE_SIZE) + +struct smb_cache_control { + struct smb_cache_head head; + struct page *page; + union smb_dir_cache *cache; + unsigned long fpos, ofs; + int filled, valid, idx; +}; + +#define SMB_OPS_NUM_STATIC 5 +struct smb_ops { + int (*read)(struct inode *inode, loff_t offset, int count, + char *data); + int (*write)(struct inode *inode, loff_t offset, int count, const + char *data); + int (*readdir)(struct file *filp, void *dirent, filldir_t filldir, + struct smb_cache_control *ctl); + + int (*getattr)(struct smb_sb_info *server, struct dentry *dir, + struct smb_fattr *fattr); + /* int (*setattr)(...); */ /* setattr is really icky! */ + + int (*truncate)(struct inode *inode, loff_t length); + + + /* --- --- --- end of "static" entries --- --- --- */ + + int (*convert)(unsigned char *output, int olen, + const unsigned char *input, int ilen, + struct nls_table *nls_from, + struct nls_table *nls_to); +}; + +static inline int +smb_is_open(struct inode *i) +{ + return (SMB_I(i)->open == server_from_inode(i)->generation); +} + +extern void smb_install_null_ops(struct smb_ops *); +#endif /* __KERNEL__ */ + +#endif /* _LINUX_SMB_FS_H */ diff --git a/drivers/staging/smbfs/smb_fs_i.h b/drivers/staging/smbfs/smb_fs_i.h new file mode 100644 index 000000000000..8ccf4eca2c3d --- /dev/null +++ b/drivers/staging/smbfs/smb_fs_i.h @@ -0,0 +1,37 @@ +/* + * smb_fs_i.h + * + * Copyright (C) 1995 by Paal-Kr. Engstad and Volker Lendecke + * Copyright (C) 1997 by Volker Lendecke + * + */ + +#ifndef _LINUX_SMB_FS_I +#define _LINUX_SMB_FS_I + +#include +#include + +/* + * smb fs inode data (in memory only) + */ +struct smb_inode_info { + + /* + * file handles are local to a connection. A file is open if + * (open == generation). + */ + unsigned int open; /* open generation */ + __u16 fileid; /* What id to handle a file with? */ + __u16 attr; /* Attribute fields, DOS value */ + + __u16 access; /* Access mode */ + __u16 flags; + unsigned long oldmtime; /* last time refreshed */ + unsigned long closed; /* timestamp when closed */ + unsigned openers; /* number of fileid users */ + + struct inode vfs_inode; /* must be at the end */ +}; + +#endif diff --git a/drivers/staging/smbfs/smb_fs_sb.h b/drivers/staging/smbfs/smb_fs_sb.h new file mode 100644 index 000000000000..ca058afda900 --- /dev/null +++ b/drivers/staging/smbfs/smb_fs_sb.h @@ -0,0 +1,100 @@ +/* + * smb_fs_sb.h + * + * Copyright (C) 1995 by Paal-Kr. Engstad and Volker Lendecke + * Copyright (C) 1997 by Volker Lendecke + * + */ + +#ifndef _SMB_FS_SB +#define _SMB_FS_SB + +#include +#include +#include "smb.h" + +/* + * Upper limit on the total number of active smb_request structs. + */ +#define MAX_REQUEST_HARD 256 + +enum smb_receive_state { + SMB_RECV_START, /* No data read, looking for length + sig */ + SMB_RECV_HEADER, /* Reading the header data */ + SMB_RECV_HCOMPLETE, /* Done with the header */ + SMB_RECV_PARAM, /* Reading parameter words */ + SMB_RECV_DATA, /* Reading data bytes */ + SMB_RECV_END, /* End of request */ + SMB_RECV_DROP, /* Dropping this SMB */ + SMB_RECV_REQUEST, /* Received a request and not a reply */ +}; + +/* structure access macros */ +#define server_from_inode(inode) SMB_SB((inode)->i_sb) +#define server_from_dentry(dentry) SMB_SB((dentry)->d_sb) +#define SB_of(server) ((server)->super_block) + +struct smb_sb_info { + /* List of all smbfs superblocks */ + struct list_head entry; + + enum smb_conn_state state; + struct file * sock_file; + int conn_error; + enum smb_receive_state rstate; + + atomic_t nr_requests; + struct list_head xmitq; + struct list_head recvq; + u16 mid; + + struct smb_mount_data_kernel *mnt; + + /* Connections are counted. Each time a new socket arrives, + * generation is incremented. + */ + unsigned int generation; + struct pid *conn_pid; + struct smb_conn_opt opt; + wait_queue_head_t conn_wq; + int conn_complete; + struct semaphore sem; + + unsigned char header[SMB_HEADER_LEN + 20*2 + 2]; + u32 header_len; + u32 smb_len; + u32 smb_read; + + /* We use our own data_ready callback, but need the original one */ + void *data_ready; + + /* nls pointers for codepage conversions */ + struct nls_table *remote_nls; + struct nls_table *local_nls; + + struct smb_ops *ops; + + struct super_block *super_block; + + struct backing_dev_info bdi; +}; + +static inline int +smb_lock_server_interruptible(struct smb_sb_info *server) +{ + return down_interruptible(&(server->sem)); +} + +static inline void +smb_lock_server(struct smb_sb_info *server) +{ + down(&(server->sem)); +} + +static inline void +smb_unlock_server(struct smb_sb_info *server) +{ + up(&(server->sem)); +} + +#endif diff --git a/drivers/staging/smbfs/smb_mount.h b/drivers/staging/smbfs/smb_mount.h new file mode 100644 index 000000000000..d10f00cb5703 --- /dev/null +++ b/drivers/staging/smbfs/smb_mount.h @@ -0,0 +1,65 @@ +/* + * smb_mount.h + * + * Copyright (C) 1995, 1996 by Paal-Kr. Engstad and Volker Lendecke + * Copyright (C) 1997 by Volker Lendecke + * + */ + +#ifndef _LINUX_SMB_MOUNT_H +#define _LINUX_SMB_MOUNT_H + +#include + +#define SMB_MOUNT_VERSION 6 + +struct smb_mount_data { + int version; + __kernel_uid_t mounted_uid; /* Who may umount() this filesystem? */ + __kernel_uid_t uid; + __kernel_gid_t gid; + __kernel_mode_t file_mode; + __kernel_mode_t dir_mode; +}; + + +#ifdef __KERNEL__ + +/* "vers" in big-endian */ +#define SMB_MOUNT_ASCII 0x76657273 + +#define SMB_MOUNT_OLDVERSION 6 +#undef SMB_MOUNT_VERSION +#define SMB_MOUNT_VERSION 7 + +/* flags */ +#define SMB_MOUNT_WIN95 0x0001 /* Win 95 server */ +#define SMB_MOUNT_OLDATTR 0x0002 /* Use core getattr (Win 95 speedup) */ +#define SMB_MOUNT_DIRATTR 0x0004 /* Use find_first for getattr */ +#define SMB_MOUNT_CASE 0x0008 /* Be case sensitive */ +#define SMB_MOUNT_UNICODE 0x0010 /* Server talks unicode */ +#define SMB_MOUNT_UID 0x0020 /* Use user specified uid */ +#define SMB_MOUNT_GID 0x0040 /* Use user specified gid */ +#define SMB_MOUNT_FMODE 0x0080 /* Use user specified file mode */ +#define SMB_MOUNT_DMODE 0x0100 /* Use user specified dir mode */ + +struct smb_mount_data_kernel { + int version; + + uid_t mounted_uid; /* Who may umount() this filesystem? */ + uid_t uid; + gid_t gid; + mode_t file_mode; + mode_t dir_mode; + + u32 flags; + + /* maximum age in jiffies (inode, dentry and dircache) */ + int ttl; + + struct smb_nls_codepage codepage; +}; + +#endif + +#endif diff --git a/drivers/staging/smbfs/smbfs.txt b/drivers/staging/smbfs/smbfs.txt new file mode 100644 index 000000000000..194fb0decd2c --- /dev/null +++ b/drivers/staging/smbfs/smbfs.txt @@ -0,0 +1,8 @@ +Smbfs is a filesystem that implements the SMB protocol, which is the +protocol used by Windows for Workgroups, Windows 95 and Windows NT. +Smbfs was inspired by Samba, the program written by Andrew Tridgell +that turns any Unix host into a file server for DOS or Windows clients. + +Smbfs is a SMB client, but uses parts of samba for its operation. For +more info on samba, including documentation, please go to +http://www.samba.org/ and then on to your nearest mirror. diff --git a/drivers/staging/smbfs/smbiod.c b/drivers/staging/smbfs/smbiod.c new file mode 100644 index 000000000000..ec998920f8d9 --- /dev/null +++ b/drivers/staging/smbfs/smbiod.c @@ -0,0 +1,343 @@ +/* + * smbiod.c + * + * Copyright (C) 2000, Charles Loep / Corel Corp. + * Copyright (C) 2001, Urban Widmark + */ + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "smb_fs.h" +#include "smbno.h" +#include "smb_mount.h" +#include "smb_debug.h" +#include "request.h" +#include "proto.h" + +enum smbiod_state { + SMBIOD_DEAD, + SMBIOD_STARTING, + SMBIOD_RUNNING, +}; + +static enum smbiod_state smbiod_state = SMBIOD_DEAD; +static struct task_struct *smbiod_thread; +static DECLARE_WAIT_QUEUE_HEAD(smbiod_wait); +static LIST_HEAD(smb_servers); +static DEFINE_SPINLOCK(servers_lock); + +#define SMBIOD_DATA_READY (1<<0) +static unsigned long smbiod_flags; + +static int smbiod(void *); +static int smbiod_start(void); + +/* + * called when there's work for us to do + */ +void smbiod_wake_up(void) +{ + if (smbiod_state == SMBIOD_DEAD) + return; + set_bit(SMBIOD_DATA_READY, &smbiod_flags); + wake_up_interruptible(&smbiod_wait); +} + +/* + * start smbiod if none is running + */ +static int smbiod_start(void) +{ + struct task_struct *tsk; + int err = 0; + + if (smbiod_state != SMBIOD_DEAD) + return 0; + smbiod_state = SMBIOD_STARTING; + __module_get(THIS_MODULE); + spin_unlock(&servers_lock); + tsk = kthread_run(smbiod, NULL, "smbiod"); + if (IS_ERR(tsk)) { + err = PTR_ERR(tsk); + module_put(THIS_MODULE); + } + + spin_lock(&servers_lock); + if (err < 0) { + smbiod_state = SMBIOD_DEAD; + smbiod_thread = NULL; + } else { + smbiod_state = SMBIOD_RUNNING; + smbiod_thread = tsk; + } + return err; +} + +/* + * register a server & start smbiod if necessary + */ +int smbiod_register_server(struct smb_sb_info *server) +{ + int ret; + spin_lock(&servers_lock); + list_add(&server->entry, &smb_servers); + VERBOSE("%p\n", server); + ret = smbiod_start(); + spin_unlock(&servers_lock); + return ret; +} + +/* + * Unregister a server + * Must be called with the server lock held. + */ +void smbiod_unregister_server(struct smb_sb_info *server) +{ + spin_lock(&servers_lock); + list_del_init(&server->entry); + VERBOSE("%p\n", server); + spin_unlock(&servers_lock); + + smbiod_wake_up(); + smbiod_flush(server); +} + +void smbiod_flush(struct smb_sb_info *server) +{ + struct list_head *tmp, *n; + struct smb_request *req; + + list_for_each_safe(tmp, n, &server->xmitq) { + req = list_entry(tmp, struct smb_request, rq_queue); + req->rq_errno = -EIO; + list_del_init(&req->rq_queue); + smb_rput(req); + wake_up_interruptible(&req->rq_wait); + } + list_for_each_safe(tmp, n, &server->recvq) { + req = list_entry(tmp, struct smb_request, rq_queue); + req->rq_errno = -EIO; + list_del_init(&req->rq_queue); + smb_rput(req); + wake_up_interruptible(&req->rq_wait); + } +} + +/* + * Wake up smbmount and make it reconnect to the server. + * This must be called with the server locked. + * + * FIXME: add smbconnect version to this + */ +int smbiod_retry(struct smb_sb_info *server) +{ + struct list_head *head; + struct smb_request *req; + struct pid *pid = get_pid(server->conn_pid); + int result = 0; + + VERBOSE("state: %d\n", server->state); + if (server->state == CONN_VALID || server->state == CONN_RETRYING) + goto out; + + smb_invalidate_inodes(server); + + /* + * Some requests are meaningless after a retry, so we abort them. + * One example are all requests using 'fileid' since the files are + * closed on retry. + */ + head = server->xmitq.next; + while (head != &server->xmitq) { + req = list_entry(head, struct smb_request, rq_queue); + head = head->next; + + req->rq_bytes_sent = 0; + if (req->rq_flags & SMB_REQ_NORETRY) { + VERBOSE("aborting request %p on xmitq\n", req); + req->rq_errno = -EIO; + list_del_init(&req->rq_queue); + smb_rput(req); + wake_up_interruptible(&req->rq_wait); + } + } + + /* + * FIXME: test the code for retrying request we already sent + */ + head = server->recvq.next; + while (head != &server->recvq) { + req = list_entry(head, struct smb_request, rq_queue); + head = head->next; +#if 0 + if (req->rq_flags & SMB_REQ_RETRY) { + /* must move the request to the xmitq */ + VERBOSE("retrying request %p on recvq\n", req); + list_move(&req->rq_queue, &server->xmitq); + continue; + } +#endif + + VERBOSE("aborting request %p on recvq\n", req); + /* req->rq_rcls = ???; */ /* FIXME: set smb error code too? */ + req->rq_errno = -EIO; + list_del_init(&req->rq_queue); + smb_rput(req); + wake_up_interruptible(&req->rq_wait); + } + + smb_close_socket(server); + + if (!pid) { + /* FIXME: this is fatal, umount? */ + printk(KERN_ERR "smb_retry: no connection process\n"); + server->state = CONN_RETRIED; + goto out; + } + + /* + * Change state so that only one retry per server will be started. + */ + server->state = CONN_RETRYING; + + /* + * Note: use the "priv" flag, as a user process may need to reconnect. + */ + result = kill_pid(pid, SIGUSR1, 1); + if (result) { + /* FIXME: this is most likely fatal, umount? */ + printk(KERN_ERR "smb_retry: signal failed [%d]\n", result); + goto out; + } + VERBOSE("signalled pid %d\n", pid_nr(pid)); + + /* FIXME: The retried requests should perhaps get a "time boost". */ + +out: + put_pid(pid); + return result; +} + +/* + * Currently handles lockingX packets. + */ +static void smbiod_handle_request(struct smb_sb_info *server) +{ + PARANOIA("smbiod got a request ... and we don't implement oplocks!\n"); + server->rstate = SMB_RECV_DROP; +} + +/* + * Do some IO for one server. + */ +static void smbiod_doio(struct smb_sb_info *server) +{ + int result; + int maxwork = 7; + + if (server->state != CONN_VALID) + goto out; + + do { + result = smb_request_recv(server); + if (result < 0) { + server->state = CONN_INVALID; + smbiod_retry(server); + goto out; /* reconnecting is slow */ + } else if (server->rstate == SMB_RECV_REQUEST) + smbiod_handle_request(server); + } while (result > 0 && maxwork-- > 0); + + /* + * If there is more to read then we want to be sure to wake up again. + */ + if (server->state != CONN_VALID) + goto out; + if (smb_recv_available(server) > 0) + set_bit(SMBIOD_DATA_READY, &smbiod_flags); + + do { + result = smb_request_send_server(server); + if (result < 0) { + server->state = CONN_INVALID; + smbiod_retry(server); + goto out; /* reconnecting is slow */ + } + } while (result > 0); + + /* + * If the last request was not sent out we want to wake up again. + */ + if (!list_empty(&server->xmitq)) + set_bit(SMBIOD_DATA_READY, &smbiod_flags); + +out: + return; +} + +/* + * smbiod kernel thread + */ +static int smbiod(void *unused) +{ + VERBOSE("SMB Kernel thread starting (%d) ...\n", current->pid); + + for (;;) { + struct smb_sb_info *server; + struct list_head *pos, *n; + + /* FIXME: Use poll? */ + wait_event_interruptible(smbiod_wait, + test_bit(SMBIOD_DATA_READY, &smbiod_flags)); + if (signal_pending(current)) { + spin_lock(&servers_lock); + smbiod_state = SMBIOD_DEAD; + spin_unlock(&servers_lock); + break; + } + + clear_bit(SMBIOD_DATA_READY, &smbiod_flags); + + spin_lock(&servers_lock); + if (list_empty(&smb_servers)) { + smbiod_state = SMBIOD_DEAD; + spin_unlock(&servers_lock); + break; + } + + list_for_each_safe(pos, n, &smb_servers) { + server = list_entry(pos, struct smb_sb_info, entry); + VERBOSE("checking server %p\n", server); + + if (server->state == CONN_VALID) { + spin_unlock(&servers_lock); + + smb_lock_server(server); + smbiod_doio(server); + smb_unlock_server(server); + + spin_lock(&servers_lock); + } + } + spin_unlock(&servers_lock); + } + + VERBOSE("SMB Kernel thread exiting (%d) ...\n", current->pid); + module_put_and_exit(0); +} diff --git a/drivers/staging/smbfs/smbno.h b/drivers/staging/smbfs/smbno.h new file mode 100644 index 000000000000..f99e02d9ffe2 --- /dev/null +++ b/drivers/staging/smbfs/smbno.h @@ -0,0 +1,363 @@ +#ifndef _SMBNO_H_ +#define _SMBNO_H_ + +/* these define the attribute byte as seen by DOS */ +#define aRONLY (1L<<0) +#define aHIDDEN (1L<<1) +#define aSYSTEM (1L<<2) +#define aVOLID (1L<<3) +#define aDIR (1L<<4) +#define aARCH (1L<<5) + +/* error classes */ +#define SUCCESS 0 /* The request was successful. */ +#define ERRDOS 0x01 /* Error is from the core DOS operating system set. */ +#define ERRSRV 0x02 /* Error is generated by the server network file manager.*/ +#define ERRHRD 0x03 /* Error is an hardware error. */ +#define ERRCMD 0xFF /* Command was not in the "SMB" format. */ + +/* SMB X/Open error codes for the ERRdos error class */ + +#define ERRbadfunc 1 /* Invalid function (or system call) */ +#define ERRbadfile 2 /* File not found (pathname error) */ +#define ERRbadpath 3 /* Directory not found */ +#define ERRnofids 4 /* Too many open files */ +#define ERRnoaccess 5 /* Access denied */ +#define ERRbadfid 6 /* Invalid fid */ +#define ERRbadmcb 7 /* Memory control blocks destroyed */ +#define ERRnomem 8 /* Out of memory */ +#define ERRbadmem 9 /* Invalid memory block address */ +#define ERRbadenv 10 /* Invalid environment */ +#define ERRbadformat 11 /* Invalid format */ +#define ERRbadaccess 12 /* Invalid open mode */ +#define ERRbaddata 13 /* Invalid data (only from ioctl call) */ +#define ERRres 14 /* reserved */ +#define ERRbaddrive 15 /* Invalid drive */ +#define ERRremcd 16 /* Attempt to delete current directory */ +#define ERRdiffdevice 17 /* rename/move across different filesystems */ +#define ERRnofiles 18 /* no more files found in file search */ +#define ERRbadshare 32 /* Share mode on file conflict with open mode */ +#define ERRlock 33 /* Lock request conflicts with existing lock */ +#define ERRfilexists 80 /* File in operation already exists */ +#define ERRbadpipe 230 /* Named pipe invalid */ +#define ERRpipebusy 231 /* All instances of pipe are busy */ +#define ERRpipeclosing 232 /* named pipe close in progress */ +#define ERRnotconnected 233 /* No process on other end of named pipe */ +#define ERRmoredata 234 /* More data to be returned */ + +#define ERROR_INVALID_PARAMETER 87 +#define ERROR_DISK_FULL 112 +#define ERROR_INVALID_NAME 123 +#define ERROR_DIR_NOT_EMPTY 145 +#define ERROR_NOT_LOCKED 158 +#define ERROR_ALREADY_EXISTS 183 /* see also 80 ? */ +#define ERROR_EAS_DIDNT_FIT 275 /* Extended attributes didn't fit */ +#define ERROR_EAS_NOT_SUPPORTED 282 /* Extended attributes not supported */ + +/* Error codes for the ERRSRV class */ + +#define ERRerror 1 /* Non specific error code */ +#define ERRbadpw 2 /* Bad password */ +#define ERRbadtype 3 /* reserved */ +#define ERRaccess 4 /* No permissions to do the requested operation */ +#define ERRinvnid 5 /* tid invalid */ +#define ERRinvnetname 6 /* Invalid servername */ +#define ERRinvdevice 7 /* Invalid device */ +#define ERRqfull 49 /* Print queue full */ +#define ERRqtoobig 50 /* Queued item too big */ +#define ERRinvpfid 52 /* Invalid print file in smb_fid */ +#define ERRsmbcmd 64 /* Unrecognised command */ +#define ERRsrverror 65 /* smb server internal error */ +#define ERRfilespecs 67 /* fid and pathname invalid combination */ +#define ERRbadlink 68 /* reserved */ +#define ERRbadpermits 69 /* Access specified for a file is not valid */ +#define ERRbadpid 70 /* reserved */ +#define ERRsetattrmode 71 /* attribute mode invalid */ +#define ERRpaused 81 /* Message server paused */ +#define ERRmsgoff 82 /* Not receiving messages */ +#define ERRnoroom 83 /* No room for message */ +#define ERRrmuns 87 /* too many remote usernames */ +#define ERRtimeout 88 /* operation timed out */ +#define ERRnoresource 89 /* No resources currently available for request. */ +#define ERRtoomanyuids 90 /* too many userids */ +#define ERRbaduid 91 /* bad userid */ +#define ERRuseMPX 250 /* temporarily unable to use raw mode, use MPX mode */ +#define ERRuseSTD 251 /* temporarily unable to use raw mode, use std.mode */ +#define ERRcontMPX 252 /* resume MPX mode */ +#define ERRbadPW /* reserved */ +#define ERRnosupport 0xFFFF + +/* Error codes for the ERRHRD class */ + +#define ERRnowrite 19 /* read only media */ +#define ERRbadunit 20 /* Unknown device */ +#define ERRnotready 21 /* Drive not ready */ +#define ERRbadcmd 22 /* Unknown command */ +#define ERRdata 23 /* Data (CRC) error */ +#define ERRbadreq 24 /* Bad request structure length */ +#define ERRseek 25 +#define ERRbadmedia 26 +#define ERRbadsector 27 +#define ERRnopaper 28 +#define ERRwrite 29 /* write fault */ +#define ERRread 30 /* read fault */ +#define ERRgeneral 31 /* General hardware failure */ +#define ERRwrongdisk 34 +#define ERRFCBunavail 35 +#define ERRsharebufexc 36 /* share buffer exceeded */ +#define ERRdiskfull 39 + +/* + * Access modes when opening a file + */ +#define SMB_ACCMASK 0x0003 +#define SMB_O_RDONLY 0x0000 +#define SMB_O_WRONLY 0x0001 +#define SMB_O_RDWR 0x0002 + +/* offsets into message for common items */ +#define smb_com 8 +#define smb_rcls 9 +#define smb_reh 10 +#define smb_err 11 +#define smb_flg 13 +#define smb_flg2 14 +#define smb_reb 13 +#define smb_tid 28 +#define smb_pid 30 +#define smb_uid 32 +#define smb_mid 34 +#define smb_wct 36 +#define smb_vwv 37 +#define smb_vwv0 37 +#define smb_vwv1 39 +#define smb_vwv2 41 +#define smb_vwv3 43 +#define smb_vwv4 45 +#define smb_vwv5 47 +#define smb_vwv6 49 +#define smb_vwv7 51 +#define smb_vwv8 53 +#define smb_vwv9 55 +#define smb_vwv10 57 +#define smb_vwv11 59 +#define smb_vwv12 61 +#define smb_vwv13 63 +#define smb_vwv14 65 + +/* these are the trans2 sub fields for primary requests */ +#define smb_tpscnt smb_vwv0 +#define smb_tdscnt smb_vwv1 +#define smb_mprcnt smb_vwv2 +#define smb_mdrcnt smb_vwv3 +#define smb_msrcnt smb_vwv4 +#define smb_flags smb_vwv5 +#define smb_timeout smb_vwv6 +#define smb_pscnt smb_vwv9 +#define smb_psoff smb_vwv10 +#define smb_dscnt smb_vwv11 +#define smb_dsoff smb_vwv12 +#define smb_suwcnt smb_vwv13 +#define smb_setup smb_vwv14 +#define smb_setup0 smb_setup +#define smb_setup1 (smb_setup+2) +#define smb_setup2 (smb_setup+4) + +/* these are for the secondary requests */ +#define smb_spscnt smb_vwv2 +#define smb_spsoff smb_vwv3 +#define smb_spsdisp smb_vwv4 +#define smb_sdscnt smb_vwv5 +#define smb_sdsoff smb_vwv6 +#define smb_sdsdisp smb_vwv7 +#define smb_sfid smb_vwv8 + +/* and these for responses */ +#define smb_tprcnt smb_vwv0 +#define smb_tdrcnt smb_vwv1 +#define smb_prcnt smb_vwv3 +#define smb_proff smb_vwv4 +#define smb_prdisp smb_vwv5 +#define smb_drcnt smb_vwv6 +#define smb_droff smb_vwv7 +#define smb_drdisp smb_vwv8 + +/* the complete */ +#define SMBmkdir 0x00 /* create directory */ +#define SMBrmdir 0x01 /* delete directory */ +#define SMBopen 0x02 /* open file */ +#define SMBcreate 0x03 /* create file */ +#define SMBclose 0x04 /* close file */ +#define SMBflush 0x05 /* flush file */ +#define SMBunlink 0x06 /* delete file */ +#define SMBmv 0x07 /* rename file */ +#define SMBgetatr 0x08 /* get file attributes */ +#define SMBsetatr 0x09 /* set file attributes */ +#define SMBread 0x0A /* read from file */ +#define SMBwrite 0x0B /* write to file */ +#define SMBlock 0x0C /* lock byte range */ +#define SMBunlock 0x0D /* unlock byte range */ +#define SMBctemp 0x0E /* create temporary file */ +#define SMBmknew 0x0F /* make new file */ +#define SMBchkpth 0x10 /* check directory path */ +#define SMBexit 0x11 /* process exit */ +#define SMBlseek 0x12 /* seek */ +#define SMBtcon 0x70 /* tree connect */ +#define SMBtconX 0x75 /* tree connect and X*/ +#define SMBtdis 0x71 /* tree disconnect */ +#define SMBnegprot 0x72 /* negotiate protocol */ +#define SMBdskattr 0x80 /* get disk attributes */ +#define SMBsearch 0x81 /* search directory */ +#define SMBsplopen 0xC0 /* open print spool file */ +#define SMBsplwr 0xC1 /* write to print spool file */ +#define SMBsplclose 0xC2 /* close print spool file */ +#define SMBsplretq 0xC3 /* return print queue */ +#define SMBsends 0xD0 /* send single block message */ +#define SMBsendb 0xD1 /* send broadcast message */ +#define SMBfwdname 0xD2 /* forward user name */ +#define SMBcancelf 0xD3 /* cancel forward */ +#define SMBgetmac 0xD4 /* get machine name */ +#define SMBsendstrt 0xD5 /* send start of multi-block message */ +#define SMBsendend 0xD6 /* send end of multi-block message */ +#define SMBsendtxt 0xD7 /* send text of multi-block message */ + +/* Core+ protocol */ +#define SMBlockread 0x13 /* Lock a range and read */ +#define SMBwriteunlock 0x14 /* Unlock a range then write */ +#define SMBreadbraw 0x1a /* read a block of data with no smb header */ +#define SMBwritebraw 0x1d /* write a block of data with no smb header */ +#define SMBwritec 0x20 /* secondary write request */ +#define SMBwriteclose 0x2c /* write a file then close it */ + +/* dos extended protocol */ +#define SMBreadBraw 0x1A /* read block raw */ +#define SMBreadBmpx 0x1B /* read block multiplexed */ +#define SMBreadBs 0x1C /* read block (secondary response) */ +#define SMBwriteBraw 0x1D /* write block raw */ +#define SMBwriteBmpx 0x1E /* write block multiplexed */ +#define SMBwriteBs 0x1F /* write block (secondary request) */ +#define SMBwriteC 0x20 /* write complete response */ +#define SMBsetattrE 0x22 /* set file attributes expanded */ +#define SMBgetattrE 0x23 /* get file attributes expanded */ +#define SMBlockingX 0x24 /* lock/unlock byte ranges and X */ +#define SMBtrans 0x25 /* transaction - name, bytes in/out */ +#define SMBtranss 0x26 /* transaction (secondary request/response) */ +#define SMBioctl 0x27 /* IOCTL */ +#define SMBioctls 0x28 /* IOCTL (secondary request/response) */ +#define SMBcopy 0x29 /* copy */ +#define SMBmove 0x2A /* move */ +#define SMBecho 0x2B /* echo */ +#define SMBopenX 0x2D /* open and X */ +#define SMBreadX 0x2E /* read and X */ +#define SMBwriteX 0x2F /* write and X */ +#define SMBsesssetupX 0x73 /* Session Set Up & X (including User Logon) */ +#define SMBtconX 0x75 /* tree connect and X */ +#define SMBffirst 0x82 /* find first */ +#define SMBfunique 0x83 /* find unique */ +#define SMBfclose 0x84 /* find close */ +#define SMBinvalid 0xFE /* invalid command */ + + +/* Extended 2.0 protocol */ +#define SMBtrans2 0x32 /* TRANS2 protocol set */ +#define SMBtranss2 0x33 /* TRANS2 protocol set, secondary command */ +#define SMBfindclose 0x34 /* Terminate a TRANSACT2_FINDFIRST */ +#define SMBfindnclose 0x35 /* Terminate a TRANSACT2_FINDNOTIFYFIRST */ +#define SMBulogoffX 0x74 /* user logoff */ + +/* these are the TRANS2 sub commands */ +#define TRANSACT2_OPEN 0 +#define TRANSACT2_FINDFIRST 1 +#define TRANSACT2_FINDNEXT 2 +#define TRANSACT2_QFSINFO 3 +#define TRANSACT2_SETFSINFO 4 +#define TRANSACT2_QPATHINFO 5 +#define TRANSACT2_SETPATHINFO 6 +#define TRANSACT2_QFILEINFO 7 +#define TRANSACT2_SETFILEINFO 8 +#define TRANSACT2_FSCTL 9 +#define TRANSACT2_IOCTL 10 +#define TRANSACT2_FINDNOTIFYFIRST 11 +#define TRANSACT2_FINDNOTIFYNEXT 12 +#define TRANSACT2_MKDIR 13 + +/* Information Levels - Shared? */ +#define SMB_INFO_STANDARD 1 +#define SMB_INFO_QUERY_EA_SIZE 2 +#define SMB_INFO_QUERY_EAS_FROM_LIST 3 +#define SMB_INFO_QUERY_ALL_EAS 4 +#define SMB_INFO_IS_NAME_VALID 6 + +/* Information Levels - TRANSACT2_FINDFIRST */ +#define SMB_FIND_FILE_DIRECTORY_INFO 0x101 +#define SMB_FIND_FILE_FULL_DIRECTORY_INFO 0x102 +#define SMB_FIND_FILE_NAMES_INFO 0x103 +#define SMB_FIND_FILE_BOTH_DIRECTORY_INFO 0x104 + +/* Information Levels - TRANSACT2_QPATHINFO */ +#define SMB_QUERY_FILE_BASIC_INFO 0x101 +#define SMB_QUERY_FILE_STANDARD_INFO 0x102 +#define SMB_QUERY_FILE_EA_INFO 0x103 +#define SMB_QUERY_FILE_NAME_INFO 0x104 +#define SMB_QUERY_FILE_ALL_INFO 0x107 +#define SMB_QUERY_FILE_ALT_NAME_INFO 0x108 +#define SMB_QUERY_FILE_STREAM_INFO 0x109 +#define SMB_QUERY_FILE_COMPRESSION_INFO 0x10b + +/* Information Levels - TRANSACT2_SETFILEINFO */ +#define SMB_SET_FILE_BASIC_INFO 0x101 +#define SMB_SET_FILE_DISPOSITION_INFO 0x102 +#define SMB_SET_FILE_ALLOCATION_INFO 0x103 +#define SMB_SET_FILE_END_OF_FILE_INFO 0x104 + +/* smb_flg field flags */ +#define SMB_FLAGS_SUPPORT_LOCKREAD 0x01 +#define SMB_FLAGS_CLIENT_BUF_AVAIL 0x02 +#define SMB_FLAGS_RESERVED 0x04 +#define SMB_FLAGS_CASELESS_PATHNAMES 0x08 +#define SMB_FLAGS_CANONICAL_PATHNAMES 0x10 +#define SMB_FLAGS_REQUEST_OPLOCK 0x20 +#define SMB_FLAGS_REQUEST_BATCH_OPLOCK 0x40 +#define SMB_FLAGS_REPLY 0x80 + +/* smb_flg2 field flags (samba-2.2.0/source/include/smb.h) */ +#define SMB_FLAGS2_LONG_PATH_COMPONENTS 0x0001 +#define SMB_FLAGS2_EXTENDED_ATTRIBUTES 0x0002 +#define SMB_FLAGS2_DFS_PATHNAMES 0x1000 +#define SMB_FLAGS2_READ_PERMIT_NO_EXECUTE 0x2000 +#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 0xFFFFFFFFFFFFFFFFULL +#define SMB_SIZE_NO_CHANGE 0xFFFFFFFFFFFFFFFFULL + +/* 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_ */ diff --git a/drivers/staging/smbfs/sock.c b/drivers/staging/smbfs/sock.c new file mode 100644 index 000000000000..9e264090e611 --- /dev/null +++ b/drivers/staging/smbfs/sock.c @@ -0,0 +1,385 @@ +/* + * sock.c + * + * Copyright (C) 1995, 1996 by Paal-Kr. Engstad and Volker Lendecke + * Copyright (C) 1997 by Volker Lendecke + * + * 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 + +#include +#include + +#include "smb_fs.h" +#include "smb.h" +#include "smbno.h" +#include "smb_debug.h" +#include "proto.h" +#include "request.h" + + +static int +_recvfrom(struct socket *socket, unsigned char *ubuf, int size, unsigned flags) +{ + struct kvec iov = {ubuf, size}; + struct msghdr msg = {.msg_flags = flags}; + msg.msg_flags |= MSG_DONTWAIT | MSG_NOSIGNAL; + return kernel_recvmsg(socket, &msg, &iov, 1, size, msg.msg_flags); +} + +/* + * Return the server this socket belongs to + */ +static struct smb_sb_info * +server_from_socket(struct socket *socket) +{ + return socket->sk->sk_user_data; +} + +/* + * Called when there is data on the socket. + */ +void +smb_data_ready(struct sock *sk, int len) +{ + struct smb_sb_info *server = server_from_socket(sk->sk_socket); + void (*data_ready)(struct sock *, int) = server->data_ready; + + data_ready(sk, len); + VERBOSE("(%p, %d)\n", sk, len); + smbiod_wake_up(); +} + +int +smb_valid_socket(struct inode * inode) +{ + return (inode && S_ISSOCK(inode->i_mode) && + SOCKET_I(inode)->type == SOCK_STREAM); +} + +static struct socket * +server_sock(struct smb_sb_info *server) +{ + struct file *file; + + if (server && (file = server->sock_file)) + { +#ifdef SMBFS_PARANOIA + if (!smb_valid_socket(file->f_path.dentry->d_inode)) + PARANOIA("bad socket!\n"); +#endif + return SOCKET_I(file->f_path.dentry->d_inode); + } + return NULL; +} + +void +smb_close_socket(struct smb_sb_info *server) +{ + struct file * file = server->sock_file; + + if (file) { + struct socket *sock = server_sock(server); + + VERBOSE("closing socket %p\n", sock); + sock->sk->sk_data_ready = server->data_ready; + server->sock_file = NULL; + fput(file); + } +} + +static int +smb_get_length(struct socket *socket, unsigned char *header) +{ + int result; + + result = _recvfrom(socket, header, 4, MSG_PEEK); + if (result == -EAGAIN) + return -ENODATA; + if (result < 0) { + PARANOIA("recv error = %d\n", -result); + return result; + } + if (result < 4) + return -ENODATA; + + switch (header[0]) { + case 0x00: + case 0x82: + break; + + case 0x85: + DEBUG1("Got SESSION KEEP ALIVE\n"); + _recvfrom(socket, header, 4, 0); /* read away */ + return -ENODATA; + + default: + PARANOIA("Invalid NBT packet, code=%x\n", header[0]); + return -EIO; + } + + /* The length in the RFC NB header is the raw data length */ + return smb_len(header); +} + +int +smb_recv_available(struct smb_sb_info *server) +{ + mm_segment_t oldfs; + int avail, err; + struct socket *sock = server_sock(server); + + oldfs = get_fs(); + set_fs(get_ds()); + err = sock->ops->ioctl(sock, SIOCINQ, (unsigned long) &avail); + set_fs(oldfs); + return (err >= 0) ? avail : err; +} + +/* + * Adjust the kvec to move on 'n' bytes (from nfs/sunrpc) + */ +static int +smb_move_iov(struct kvec **data, size_t *num, struct kvec *vec, unsigned amount) +{ + struct kvec *iv = *data; + int i; + int len; + + /* + * Eat any sent kvecs + */ + while (iv->iov_len <= amount) { + amount -= iv->iov_len; + iv++; + (*num)--; + } + + /* + * And chew down the partial one + */ + vec[0].iov_len = iv->iov_len-amount; + vec[0].iov_base =((unsigned char *)iv->iov_base)+amount; + iv++; + + len = vec[0].iov_len; + + /* + * And copy any others + */ + for (i = 1; i < *num; i++) { + vec[i] = *iv++; + len += vec[i].iov_len; + } + + *data = vec; + return len; +} + +/* + * smb_receive_header + * Only called by the smbiod thread. + */ +int +smb_receive_header(struct smb_sb_info *server) +{ + struct socket *sock; + int result = 0; + unsigned char peek_buf[4]; + + result = -EIO; + sock = server_sock(server); + if (!sock) + goto out; + if (sock->sk->sk_state != TCP_ESTABLISHED) + goto out; + + if (!server->smb_read) { + result = smb_get_length(sock, peek_buf); + if (result < 0) { + if (result == -ENODATA) + result = 0; + goto out; + } + server->smb_len = result + 4; + + if (server->smb_len < SMB_HEADER_LEN) { + PARANOIA("short packet: %d\n", result); + server->rstate = SMB_RECV_DROP; + result = -EIO; + goto out; + } + if (server->smb_len > SMB_MAX_PACKET_SIZE) { + PARANOIA("long packet: %d\n", result); + server->rstate = SMB_RECV_DROP; + result = -EIO; + goto out; + } + } + + result = _recvfrom(sock, server->header + server->smb_read, + SMB_HEADER_LEN - server->smb_read, 0); + VERBOSE("_recvfrom: %d\n", result); + if (result < 0) { + VERBOSE("receive error: %d\n", result); + goto out; + } + server->smb_read += result; + + if (server->smb_read == SMB_HEADER_LEN) + server->rstate = SMB_RECV_HCOMPLETE; +out: + return result; +} + +static char drop_buffer[PAGE_SIZE]; + +/* + * smb_receive_drop - read and throw away the data + * Only called by the smbiod thread. + * + * FIXME: we are in the kernel, could we just tell the socket that we want + * to drop stuff from the buffer? + */ +int +smb_receive_drop(struct smb_sb_info *server) +{ + struct socket *sock; + unsigned int flags; + struct kvec iov; + struct msghdr msg; + int rlen = smb_len(server->header) - server->smb_read + 4; + int result = -EIO; + + if (rlen > PAGE_SIZE) + rlen = PAGE_SIZE; + + sock = server_sock(server); + if (!sock) + goto out; + if (sock->sk->sk_state != TCP_ESTABLISHED) + goto out; + + flags = MSG_DONTWAIT | MSG_NOSIGNAL; + iov.iov_base = drop_buffer; + iov.iov_len = PAGE_SIZE; + msg.msg_flags = flags; + msg.msg_name = NULL; + msg.msg_namelen = 0; + msg.msg_control = NULL; + + result = kernel_recvmsg(sock, &msg, &iov, 1, rlen, flags); + + VERBOSE("read: %d\n", result); + if (result < 0) { + VERBOSE("receive error: %d\n", result); + goto out; + } + server->smb_read += result; + + if (server->smb_read >= server->smb_len) + server->rstate = SMB_RECV_END; + +out: + return result; +} + +/* + * smb_receive + * Only called by the smbiod thread. + */ +int +smb_receive(struct smb_sb_info *server, struct smb_request *req) +{ + struct socket *sock; + unsigned int flags; + struct kvec iov[4]; + struct kvec *p = req->rq_iov; + size_t num = req->rq_iovlen; + struct msghdr msg; + int rlen; + int result = -EIO; + + sock = server_sock(server); + if (!sock) + goto out; + if (sock->sk->sk_state != TCP_ESTABLISHED) + goto out; + + flags = MSG_DONTWAIT | MSG_NOSIGNAL; + msg.msg_flags = flags; + msg.msg_name = NULL; + msg.msg_namelen = 0; + msg.msg_control = NULL; + + /* Dont repeat bytes and count available bufferspace */ + rlen = min_t(int, smb_move_iov(&p, &num, iov, req->rq_bytes_recvd), + (req->rq_rlen - req->rq_bytes_recvd)); + + result = kernel_recvmsg(sock, &msg, p, num, rlen, flags); + + VERBOSE("read: %d\n", result); + if (result < 0) { + VERBOSE("receive error: %d\n", result); + goto out; + } + req->rq_bytes_recvd += result; + server->smb_read += result; + +out: + return result; +} + +/* + * Try to send a SMB request. This may return after sending only parts of the + * request. SMB_REQ_TRANSMITTED will be set if a request was fully sent. + * + * Parts of this was taken from xprt_sendmsg from net/sunrpc/xprt.c + */ +int +smb_send_request(struct smb_request *req) +{ + struct smb_sb_info *server = req->rq_server; + struct socket *sock; + struct msghdr msg = {.msg_flags = MSG_NOSIGNAL | MSG_DONTWAIT}; + int slen = req->rq_slen - req->rq_bytes_sent; + int result = -EIO; + struct kvec iov[4]; + struct kvec *p = req->rq_iov; + size_t num = req->rq_iovlen; + + sock = server_sock(server); + if (!sock) + goto out; + if (sock->sk->sk_state != TCP_ESTABLISHED) + goto out; + + /* Dont repeat bytes */ + if (req->rq_bytes_sent) + smb_move_iov(&p, &num, iov, req->rq_bytes_sent); + + result = kernel_sendmsg(sock, &msg, p, num, slen); + + if (result >= 0) { + req->rq_bytes_sent += result; + if (req->rq_bytes_sent >= req->rq_slen) + req->rq_flags |= SMB_REQ_TRANSMITTED; + } +out: + return result; +} diff --git a/drivers/staging/smbfs/symlink.c b/drivers/staging/smbfs/symlink.c new file mode 100644 index 000000000000..632c4acd062d --- /dev/null +++ b/drivers/staging/smbfs/symlink.c @@ -0,0 +1,67 @@ +/* + * 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 "smbno.h" +#include "smb_fs.h" +#include "smb_debug.h" +#include "proto.h" + +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); +} + +static void *smb_follow_link(struct dentry *dentry, struct nameidata *nd) +{ + char *link = __getname(); + DEBUG1("followlink of %s/%s\n", DENTRY_PATH(dentry)); + + if (!link) { + link = ERR_PTR(-ENOMEM); + } else { + int len = smb_proc_read_link(server_from_dentry(dentry), + dentry, link, PATH_MAX - 1); + if (len < 0) { + __putname(link); + link = ERR_PTR(len); + } else { + link[len] = 0; + } + } + nd_set_link(nd, link); + return NULL; +} + +static void smb_put_link(struct dentry *dentry, struct nameidata *nd, void *p) +{ + char *s = nd_get_link(nd); + if (!IS_ERR(s)) + __putname(s); +} + +const struct inode_operations smb_link_inode_operations = +{ + .readlink = generic_readlink, + .follow_link = smb_follow_link, + .put_link = smb_put_link, +}; diff --git a/fs/Kconfig b/fs/Kconfig index 30da8ee16a96..25ce2dc1c6d4 100644 --- a/fs/Kconfig +++ b/fs/Kconfig @@ -233,7 +233,6 @@ config NFS_COMMON default y source "net/sunrpc/Kconfig" -source "fs/smbfs/Kconfig" source "fs/ceph/Kconfig" source "fs/cifs/Kconfig" source "fs/ncpfs/Kconfig" diff --git a/fs/Makefile b/fs/Makefile index e571feddd7b7..9284c74c2db9 100644 --- a/fs/Makefile +++ b/fs/Makefile @@ -91,7 +91,6 @@ obj-$(CONFIG_NFSD) += nfsd/ obj-$(CONFIG_LOCKD) += lockd/ obj-$(CONFIG_NLS) += nls/ obj-$(CONFIG_SYSV_FS) += sysv/ -obj-$(CONFIG_SMB_FS) += smbfs/ obj-$(CONFIG_CIFS) += cifs/ obj-$(CONFIG_NCP_FS) += ncpfs/ obj-$(CONFIG_HPFS_FS) += hpfs/ diff --git a/fs/compat.c b/fs/compat.c index 718c7062aec1..b42f29a44edb 100644 --- a/fs/compat.c +++ b/fs/compat.c @@ -29,8 +29,6 @@ #include #include #include -#include -#include #include #include #include @@ -745,30 +743,6 @@ static void *do_ncp_super_data_conv(void *raw_data) return raw_data; } -struct compat_smb_mount_data { - compat_int_t version; - __compat_uid_t mounted_uid; - __compat_uid_t uid; - __compat_gid_t gid; - compat_mode_t file_mode; - compat_mode_t dir_mode; -}; - -static void *do_smb_super_data_conv(void *raw_data) -{ - struct smb_mount_data *s = raw_data; - struct compat_smb_mount_data *c_s = raw_data; - - if (c_s->version != SMB_MOUNT_OLDVERSION) - goto out; - s->dir_mode = c_s->dir_mode; - s->file_mode = c_s->file_mode; - s->gid = c_s->gid; - s->uid = c_s->uid; - s->mounted_uid = c_s->mounted_uid; - out: - return raw_data; -} struct compat_nfs_string { compat_uint_t len; @@ -835,7 +809,6 @@ static int do_nfs4_super_data_conv(void *raw_data) return 0; } -#define SMBFS_NAME "smbfs" #define NCPFS_NAME "ncpfs" #define NFS4_NAME "nfs4" @@ -870,9 +843,7 @@ asmlinkage long compat_sys_mount(const char __user * dev_name, retval = -EINVAL; if (kernel_type && data_page) { - if (!strcmp(kernel_type, SMBFS_NAME)) { - do_smb_super_data_conv((void *)data_page); - } else if (!strcmp(kernel_type, NCPFS_NAME)) { + if (!strcmp(kernel_type, NCPFS_NAME)) { do_ncp_super_data_conv((void *)data_page); } else if (!strcmp(kernel_type, NFS4_NAME)) { if (do_nfs4_super_data_conv((void *) data_page)) diff --git a/fs/compat_ioctl.c b/fs/compat_ioctl.c index 03e59aa318eb..34cf03cd791f 100644 --- a/fs/compat_ioctl.c +++ b/fs/compat_ioctl.c @@ -46,7 +46,6 @@ #include #include #include -#include #include #include #include @@ -558,25 +557,6 @@ static int mt_ioctl_trans(unsigned int fd, unsigned int cmd, void __user *argp) #endif /* CONFIG_BLOCK */ -static int do_smb_getmountuid(unsigned int fd, unsigned int cmd, - compat_uid_t __user *argp) -{ - mm_segment_t old_fs = get_fs(); - __kernel_uid_t kuid; - int err; - - cmd = SMB_IOC_GETMOUNTUID; - - set_fs(KERNEL_DS); - err = sys_ioctl(fd, cmd, (unsigned long)&kuid); - set_fs(old_fs); - - if (err >= 0) - err = put_user(kuid, argp); - - return err; -} - /* Bluetooth ioctls */ #define HCIUARTSETPROTO _IOW('U', 200, int) #define HCIUARTGETPROTO _IOR('U', 201, int) @@ -1265,8 +1245,6 @@ COMPATIBLE_IOCTL(OSS_GETVERSION) /* Raw devices */ COMPATIBLE_IOCTL(RAW_SETBIND) COMPATIBLE_IOCTL(RAW_GETBIND) -/* SMB ioctls which do not need any translations */ -COMPATIBLE_IOCTL(SMB_IOC_NEWCONN) /* Watchdog */ COMPATIBLE_IOCTL(WDIOC_GETSUPPORT) COMPATIBLE_IOCTL(WDIOC_GETSTATUS) @@ -1528,10 +1506,6 @@ static long do_ioctl_trans(int fd, unsigned int cmd, case RAW_GETBIND: return raw_ioctl(fd, cmd, argp); #endif - /* One SMB ioctl needs translations. */ -#define SMB_IOC_GETMOUNTUID_32 _IOR('u', 1, compat_uid_t) - case SMB_IOC_GETMOUNTUID_32: - return do_smb_getmountuid(fd, cmd, argp); /* Serial */ case TIOCGSERIAL: case TIOCSSERIAL: diff --git a/fs/smbfs/Kconfig b/fs/smbfs/Kconfig deleted file mode 100644 index e668127c8b2e..000000000000 --- a/fs/smbfs/Kconfig +++ /dev/null @@ -1,55 +0,0 @@ -config SMB_FS - tristate "SMB file system support (OBSOLETE, please use CIFS)" - depends on INET - select NLS - help - SMB (Server Message Block) is the protocol Windows for Workgroups - (WfW), Windows 95/98, Windows NT and OS/2 Lan Manager use to share - files and printers over local networks. Saying Y here allows you to - mount their file systems (often called "shares" in this context) and - access them just like any other Unix directory. Currently, this - works only if the Windows machines use TCP/IP as the underlying - transport protocol, and not NetBEUI. For details, read - and the SMB-HOWTO, - available from . - - Note: if you just want your box to act as an SMB *server* and make - files and printing services available to Windows clients (which need - to have a TCP/IP stack), you don't need to say Y here; you can use - the program SAMBA (available from ) - for that. - - General information about how to connect Linux, Windows machines and - Macs is on the WWW at . - - To compile the SMB support as a module, choose M here: - the module will be called smbfs. Most people say N, however. - -config SMB_NLS_DEFAULT - bool "Use a default NLS" - depends on SMB_FS - help - Enabling this will make smbfs use nls translations by default. You - need to specify the local charset (CONFIG_NLS_DEFAULT) in the nls - settings and you need to give the default nls for the SMB server as - CONFIG_SMB_NLS_REMOTE. - - The nls settings can be changed at mount time, if your smbmount - supports that, using the codepage and iocharset parameters. - - smbmount from samba 2.2.0 or later supports this. - -config SMB_NLS_REMOTE - string "Default Remote NLS Option" - depends on SMB_NLS_DEFAULT - default "cp437" - help - This setting allows you to specify a default value for which - codepage the server uses. If this field is left blank no - translations will be done by default. The local codepage/charset - default to CONFIG_NLS_DEFAULT. - - The nls settings can be changed at mount time, if your smbmount - supports that, using the codepage and iocharset parameters. - - smbmount from samba 2.2.0 or later supports this. diff --git a/fs/smbfs/Makefile b/fs/smbfs/Makefile deleted file mode 100644 index 4faf8c4722c3..000000000000 --- a/fs/smbfs/Makefile +++ /dev/null @@ -1,18 +0,0 @@ -# -# Makefile for the linux smb-filesystem routines. -# - -obj-$(CONFIG_SMB_FS) += smbfs.o - -smbfs-objs := proc.o dir.o cache.o sock.o inode.o file.o ioctl.o getopt.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. - -EXTRA_CFLAGS += -DSMBFS_PARANOIA -#EXTRA_CFLAGS += -DSMBFS_DEBUG -#EXTRA_CFLAGS += -DSMBFS_DEBUG_VERBOSE -#EXTRA_CFLAGS += -DDEBUG_SMB_TIMESTAMP -#EXTRA_CFLAGS += -Werror - diff --git a/fs/smbfs/cache.c b/fs/smbfs/cache.c deleted file mode 100644 index 8c177eb7e344..000000000000 --- a/fs/smbfs/cache.c +++ /dev/null @@ -1,208 +0,0 @@ -/* - * cache.c - * - * Copyright (C) 1997 by Bill Hawes - * - * Routines to support directory cacheing using the page cache. - * This cache code is almost directly taken from ncpfs. - * - * Please add a note about your changes to smbfs in the ChangeLog file. - */ - -#include -#include -#include -#include -#include -#include -#include - -#include - -#include "smb_debug.h" -#include "proto.h" - -/* - * Force the next attempt to use the cache to be a timeout. - * If we can't find the page that's fine, it will cause a refresh. - */ -void -smb_invalid_dir_cache(struct inode * dir) -{ - struct smb_sb_info *server = server_from_inode(dir); - union smb_dir_cache *cache = NULL; - struct page *page = NULL; - - page = grab_cache_page(&dir->i_data, 0); - if (!page) - goto out; - - if (!PageUptodate(page)) - goto out_unlock; - - cache = kmap(page); - cache->head.time = jiffies - SMB_MAX_AGE(server); - - kunmap(page); - SetPageUptodate(page); -out_unlock: - unlock_page(page); - page_cache_release(page); -out: - return; -} - -/* - * Mark all dentries for 'parent' as invalid, forcing them to be re-read - */ -void -smb_invalidate_dircache_entries(struct dentry *parent) -{ - struct smb_sb_info *server = server_from_dentry(parent); - struct list_head *next; - struct dentry *dentry; - - spin_lock(&dcache_lock); - next = parent->d_subdirs.next; - while (next != &parent->d_subdirs) { - dentry = list_entry(next, struct dentry, d_u.d_child); - dentry->d_fsdata = NULL; - smb_age_dentry(server, dentry); - next = next->next; - } - spin_unlock(&dcache_lock); -} - -/* - * dget, but require that fpos and parent matches what the dentry contains. - * dentry is not known to be a valid pointer at entry. - */ -struct dentry * -smb_dget_fpos(struct dentry *dentry, struct dentry *parent, unsigned long fpos) -{ - struct dentry *dent = dentry; - struct list_head *next; - - if (d_validate(dent, parent)) { - if (dent->d_name.len <= SMB_MAXNAMELEN && - (unsigned long)dent->d_fsdata == fpos) { - if (!dent->d_inode) { - dput(dent); - dent = NULL; - } - return dent; - } - dput(dent); - } - - /* If a pointer is invalid, we search the dentry. */ - spin_lock(&dcache_lock); - next = parent->d_subdirs.next; - while (next != &parent->d_subdirs) { - dent = list_entry(next, struct dentry, d_u.d_child); - if ((unsigned long)dent->d_fsdata == fpos) { - if (dent->d_inode) - dget_locked(dent); - else - dent = NULL; - goto out_unlock; - } - next = next->next; - } - dent = NULL; -out_unlock: - spin_unlock(&dcache_lock); - return dent; -} - - -/* - * Create dentry/inode for this file and add it to the dircache. - */ -int -smb_fill_cache(struct file *filp, void *dirent, filldir_t filldir, - struct smb_cache_control *ctrl, struct qstr *qname, - struct smb_fattr *entry) -{ - struct dentry *newdent, *dentry = filp->f_path.dentry; - struct inode *newino, *inode = dentry->d_inode; - struct smb_cache_control ctl = *ctrl; - int valid = 0; - int hashed = 0; - ino_t ino = 0; - - qname->hash = full_name_hash(qname->name, qname->len); - - if (dentry->d_op && dentry->d_op->d_hash) - if (dentry->d_op->d_hash(dentry, qname) != 0) - goto end_advance; - - newdent = d_lookup(dentry, qname); - - if (!newdent) { - newdent = d_alloc(dentry, qname); - if (!newdent) - goto end_advance; - } else { - hashed = 1; - memcpy((char *) newdent->d_name.name, qname->name, - newdent->d_name.len); - } - - if (!newdent->d_inode) { - smb_renew_times(newdent); - entry->f_ino = iunique(inode->i_sb, 2); - newino = smb_iget(inode->i_sb, entry); - if (newino) { - smb_new_dentry(newdent); - d_instantiate(newdent, newino); - if (!hashed) - d_rehash(newdent); - } - } else - smb_set_inode_attr(newdent->d_inode, entry); - - if (newdent->d_inode) { - ino = newdent->d_inode->i_ino; - newdent->d_fsdata = (void *) ctl.fpos; - smb_new_dentry(newdent); - } - - if (ctl.idx >= SMB_DIRCACHE_SIZE) { - if (ctl.page) { - kunmap(ctl.page); - SetPageUptodate(ctl.page); - unlock_page(ctl.page); - page_cache_release(ctl.page); - } - ctl.cache = NULL; - ctl.idx -= SMB_DIRCACHE_SIZE; - ctl.ofs += 1; - ctl.page = grab_cache_page(&inode->i_data, ctl.ofs); - if (ctl.page) - ctl.cache = kmap(ctl.page); - } - if (ctl.cache) { - ctl.cache->dentry[ctl.idx] = newdent; - valid = 1; - } - dput(newdent); - -end_advance: - if (!valid) - ctl.valid = 0; - if (!ctl.filled && (ctl.fpos == filp->f_pos)) { - if (!ino) - ino = find_inode_number(dentry, qname); - if (!ino) - ino = iunique(inode->i_sb, 2); - ctl.filled = filldir(dirent, qname->name, qname->len, - filp->f_pos, ino, DT_UNKNOWN); - if (!ctl.filled) - filp->f_pos += 1; - } - ctl.fpos += 1; - ctl.idx += 1; - *ctrl = ctl; - return (ctl.valid || !ctl.filled); -} diff --git a/fs/smbfs/dir.c b/fs/smbfs/dir.c deleted file mode 100644 index 00a70cab1f36..000000000000 --- a/fs/smbfs/dir.c +++ /dev/null @@ -1,702 +0,0 @@ -/* - * dir.c - * - * Copyright (C) 1995, 1996 by Paal-Kr. Engstad and Volker Lendecke - * Copyright (C) 1997 by Volker Lendecke - * - * Please add a note about your changes to smbfs in the ChangeLog file. - */ - -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#include "smb_debug.h" -#include "proto.h" - -static int smb_readdir(struct file *, void *, filldir_t); -static int smb_dir_open(struct inode *, struct file *); - -static struct dentry *smb_lookup(struct inode *, struct dentry *, struct nameidata *); -static int smb_create(struct inode *, struct dentry *, int, struct nameidata *); -static int smb_mkdir(struct inode *, struct dentry *, int); -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,dev_t); -static int smb_link(struct dentry *, struct inode *, struct dentry *); - -const struct file_operations smb_dir_operations = -{ - .llseek = generic_file_llseek, - .read = generic_read_dir, - .readdir = smb_readdir, - .unlocked_ioctl = smb_ioctl, - .open = smb_dir_open, -}; - -const struct inode_operations smb_dir_inode_operations = -{ - .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, -}; - -const 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. - * - * The cache code is almost directly taken from ncpfs - */ -static int -smb_readdir(struct file *filp, void *dirent, filldir_t filldir) -{ - struct dentry *dentry = filp->f_path.dentry; - struct inode *dir = dentry->d_inode; - struct smb_sb_info *server = server_from_dentry(dentry); - union smb_dir_cache *cache = NULL; - struct smb_cache_control ctl; - struct page *page = NULL; - int result; - - ctl.page = NULL; - ctl.cache = NULL; - - VERBOSE("reading %s/%s, f_pos=%d\n", - DENTRY_PATH(dentry), (int) filp->f_pos); - - result = 0; - - lock_kernel(); - - switch ((unsigned int) filp->f_pos) { - case 0: - if (filldir(dirent, ".", 1, 0, dir->i_ino, DT_DIR) < 0) - goto out; - filp->f_pos = 1; - /* fallthrough */ - case 1: - if (filldir(dirent, "..", 2, 1, parent_ino(dentry), DT_DIR) < 0) - goto out; - filp->f_pos = 2; - } - - /* - * Make sure our inode is up-to-date. - */ - result = smb_revalidate_inode(dentry); - if (result) - goto out; - - - page = grab_cache_page(&dir->i_data, 0); - if (!page) - goto read_really; - - ctl.cache = cache = kmap(page); - ctl.head = cache->head; - - if (!PageUptodate(page) || !ctl.head.eof) { - VERBOSE("%s/%s, page uptodate=%d, eof=%d\n", - DENTRY_PATH(dentry), PageUptodate(page),ctl.head.eof); - goto init_cache; - } - - if (filp->f_pos == 2) { - if (jiffies - ctl.head.time >= SMB_MAX_AGE(server)) - goto init_cache; - - /* - * N.B. ncpfs checks mtime of dentry too here, we don't. - * 1. common smb servers do not update mtime on dir changes - * 2. it requires an extra smb request - * (revalidate has the same timeout as ctl.head.time) - * - * Instead smbfs invalidates its own cache on local changes - * and remote changes are not seen until timeout. - */ - } - - if (filp->f_pos > ctl.head.end) - goto finished; - - ctl.fpos = filp->f_pos + (SMB_DIRCACHE_START - 2); - ctl.ofs = ctl.fpos / SMB_DIRCACHE_SIZE; - ctl.idx = ctl.fpos % SMB_DIRCACHE_SIZE; - - for (;;) { - if (ctl.ofs != 0) { - ctl.page = find_lock_page(&dir->i_data, ctl.ofs); - if (!ctl.page) - goto invalid_cache; - ctl.cache = kmap(ctl.page); - if (!PageUptodate(ctl.page)) - goto invalid_cache; - } - while (ctl.idx < SMB_DIRCACHE_SIZE) { - struct dentry *dent; - int res; - - dent = smb_dget_fpos(ctl.cache->dentry[ctl.idx], - dentry, filp->f_pos); - if (!dent) - goto invalid_cache; - - res = filldir(dirent, dent->d_name.name, - dent->d_name.len, filp->f_pos, - dent->d_inode->i_ino, DT_UNKNOWN); - dput(dent); - if (res) - goto finished; - filp->f_pos += 1; - ctl.idx += 1; - if (filp->f_pos > ctl.head.end) - goto finished; - } - if (ctl.page) { - kunmap(ctl.page); - SetPageUptodate(ctl.page); - unlock_page(ctl.page); - page_cache_release(ctl.page); - ctl.page = NULL; - } - ctl.idx = 0; - ctl.ofs += 1; - } -invalid_cache: - if (ctl.page) { - kunmap(ctl.page); - unlock_page(ctl.page); - page_cache_release(ctl.page); - ctl.page = NULL; - } - ctl.cache = cache; -init_cache: - smb_invalidate_dircache_entries(dentry); - ctl.head.time = jiffies; - ctl.head.eof = 0; - ctl.fpos = 2; - ctl.ofs = 0; - ctl.idx = SMB_DIRCACHE_START; - ctl.filled = 0; - ctl.valid = 1; -read_really: - result = server->ops->readdir(filp, dirent, filldir, &ctl); - if (result == -ERESTARTSYS && page) - ClearPageUptodate(page); - if (ctl.idx == -1) - goto invalid_cache; /* retry */ - ctl.head.end = ctl.fpos - 1; - ctl.head.eof = ctl.valid; -finished: - if (page) { - cache->head = ctl.head; - kunmap(page); - if (result != -ERESTARTSYS) - SetPageUptodate(page); - unlock_page(page); - page_cache_release(page); - } - if (ctl.page) { - kunmap(ctl.page); - SetPageUptodate(ctl.page); - unlock_page(ctl.page); - page_cache_release(ctl.page); - } -out: - unlock_kernel(); - return result; -} - -static int -smb_dir_open(struct inode *dir, struct file *file) -{ - struct dentry *dentry = file->f_path.dentry; - struct smb_sb_info *server; - int error = 0; - - VERBOSE("(%s/%s)\n", dentry->d_parent->d_name.name, - file->f_path.dentry->d_name.name); - - /* - * Directory timestamps in the core protocol aren't updated - * when a file is added, so we give them a very short TTL. - */ - lock_kernel(); - server = server_from_dentry(dentry); - if (server->opt.protocol < SMB_PROTOCOL_LANMAN2) { - unsigned long age = jiffies - SMB_I(dir)->oldmtime; - if (age > 2*HZ) - smb_invalid_dir_cache(dir); - } - - /* - * Note: in order to allow the smbmount process to open the - * mount point, we only revalidate if the connection is valid or - * if the process is trying to access something other than the root. - */ - if (server->state == CONN_VALID || !IS_ROOT(dentry)) - error = smb_revalidate_inode(dentry); - unlock_kernel(); - return error; -} - -/* - * Dentry operations routines - */ -static int smb_lookup_validate(struct dentry *, struct nameidata *); -static int smb_hash_dentry(struct dentry *, struct qstr *); -static int smb_compare_dentry(struct dentry *, struct qstr *, struct qstr *); -static int smb_delete_dentry(struct dentry *); - -static const struct dentry_operations smbfs_dentry_operations = -{ - .d_revalidate = smb_lookup_validate, - .d_hash = smb_hash_dentry, - .d_compare = smb_compare_dentry, - .d_delete = smb_delete_dentry, -}; - -static const struct dentry_operations smbfs_dentry_operations_case = -{ - .d_revalidate = smb_lookup_validate, - .d_delete = smb_delete_dentry, -}; - - -/* - * This is the callback when the dcache has a lookup hit. - */ -static int -smb_lookup_validate(struct dentry * dentry, struct nameidata *nd) -{ - struct smb_sb_info *server = server_from_dentry(dentry); - struct inode * inode = dentry->d_inode; - unsigned long age = jiffies - dentry->d_time; - int valid; - - /* - * The default validation is based on dentry age: - * we believe in dentries for a few seconds. (But each - * successful server lookup renews the timestamp.) - */ - valid = (age <= SMB_MAX_AGE(server)); -#ifdef SMBFS_DEBUG_VERBOSE - if (!valid) - VERBOSE("%s/%s not valid, age=%lu\n", - DENTRY_PATH(dentry), age); -#endif - - if (inode) { - lock_kernel(); - if (is_bad_inode(inode)) { - PARANOIA("%s/%s has dud inode\n", DENTRY_PATH(dentry)); - valid = 0; - } else if (!valid) - valid = (smb_revalidate_inode(dentry) == 0); - unlock_kernel(); - } else { - /* - * What should we do for negative dentries? - */ - } - return valid; -} - -static int -smb_hash_dentry(struct dentry *dir, struct qstr *this) -{ - unsigned long hash; - int i; - - hash = init_name_hash(); - for (i=0; i < this->len ; i++) - hash = partial_name_hash(tolower(this->name[i]), hash); - this->hash = end_name_hash(hash); - - return 0; -} - -static int -smb_compare_dentry(struct dentry *dir, struct qstr *a, struct qstr *b) -{ - int i, result = 1; - - if (a->len != b->len) - goto out; - for (i=0; i < a->len; i++) { - if (tolower(a->name[i]) != tolower(b->name[i])) - goto out; - } - result = 0; -out: - return result; -} - -/* - * This is the callback from dput() when d_count is going to 0. - * We use this to unhash dentries with bad inodes. - */ -static int -smb_delete_dentry(struct dentry * dentry) -{ - if (dentry->d_inode) { - if (is_bad_inode(dentry->d_inode)) { - PARANOIA("bad inode, unhashing %s/%s\n", - DENTRY_PATH(dentry)); - return 1; - } - } else { - /* N.B. Unhash negative dentries? */ - } - return 0; -} - -/* - * Initialize a new dentry - */ -void -smb_new_dentry(struct dentry *dentry) -{ - struct smb_sb_info *server = server_from_dentry(dentry); - - if (server->mnt->flags & SMB_MOUNT_CASE) - dentry->d_op = &smbfs_dentry_operations_case; - else - dentry->d_op = &smbfs_dentry_operations; - dentry->d_time = jiffies; -} - - -/* - * Whenever a lookup succeeds, we know the parent directories - * are all valid, so we want to update the dentry timestamps. - * N.B. Move this to dcache? - */ -void -smb_renew_times(struct dentry * dentry) -{ - dget(dentry); - spin_lock(&dentry->d_lock); - for (;;) { - struct dentry *parent; - - dentry->d_time = jiffies; - if (IS_ROOT(dentry)) - break; - parent = dentry->d_parent; - dget(parent); - spin_unlock(&dentry->d_lock); - dput(dentry); - dentry = parent; - spin_lock(&dentry->d_lock); - } - spin_unlock(&dentry->d_lock); - dput(dentry); -} - -static struct dentry * -smb_lookup(struct inode *dir, struct dentry *dentry, struct nameidata *nd) -{ - struct smb_fattr finfo; - struct inode *inode; - int error; - struct smb_sb_info *server; - - error = -ENAMETOOLONG; - if (dentry->d_name.len > SMB_MAXNAMELEN) - goto out; - - /* Do not allow lookup of names with backslashes in */ - error = -EINVAL; - if (memchr(dentry->d_name.name, '\\', dentry->d_name.len)) - goto out; - - lock_kernel(); - error = smb_proc_getattr(dentry, &finfo); -#ifdef SMBFS_PARANOIA - if (error && error != -ENOENT) - PARANOIA("find %s/%s failed, error=%d\n", - DENTRY_PATH(dentry), error); -#endif - - inode = NULL; - if (error == -ENOENT) - goto add_entry; - if (!error) { - error = -EACCES; - finfo.f_ino = iunique(dentry->d_sb, 2); - inode = smb_iget(dir->i_sb, &finfo); - if (inode) { - add_entry: - server = server_from_dentry(dentry); - if (server->mnt->flags & SMB_MOUNT_CASE) - dentry->d_op = &smbfs_dentry_operations_case; - else - dentry->d_op = &smbfs_dentry_operations; - - d_add(dentry, inode); - smb_renew_times(dentry); - error = 0; - } - } - unlock_kernel(); -out: - return ERR_PTR(error); -} - -/* - * This code is common to all routines creating a new inode. - */ -static int -smb_instantiate(struct dentry *dentry, __u16 fileid, int have_id) -{ - struct smb_sb_info *server = server_from_dentry(dentry); - struct inode *inode; - int error; - struct smb_fattr fattr; - - VERBOSE("file %s/%s, fileid=%u\n", DENTRY_PATH(dentry), fileid); - - error = smb_proc_getattr(dentry, &fattr); - if (error) - goto out_close; - - smb_renew_times(dentry); - fattr.f_ino = iunique(dentry->d_sb, 2); - inode = smb_iget(dentry->d_sb, &fattr); - if (!inode) - goto out_no_inode; - - if (have_id) { - struct smb_inode_info *ei = SMB_I(inode); - ei->fileid = fileid; - ei->access = SMB_O_RDWR; - ei->open = server->generation; - } - d_instantiate(dentry, inode); -out: - return error; - -out_no_inode: - error = -EACCES; -out_close: - if (have_id) { - PARANOIA("%s/%s failed, error=%d, closing %u\n", - DENTRY_PATH(dentry), error, fileid); - smb_close_fileid(dentry, fileid); - } - goto out; -} - -/* N.B. How should the mode argument be used? */ -static int -smb_create(struct inode *dir, struct dentry *dentry, int mode, - struct nameidata *nd) -{ - 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); - - lock_kernel(); - smb_invalid_dir_cache(dir); - error = smb_proc_create(dentry, 0, get_seconds(), &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", - DENTRY_PATH(dentry), error); - } - unlock_kernel(); - return error; -} - -/* N.B. How should the mode argument be used? */ -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(); - return error; -} - -static int -smb_rmdir(struct inode *dir, struct dentry *dentry) -{ - struct inode *inode = dentry->d_inode; - int error; - - /* - * Close the directory if it's open. - */ - lock_kernel(); - smb_close(inode); - - /* - * Check that nobody else is using the directory.. - */ - error = -EBUSY; - if (!d_unhashed(dentry)) - goto out; - - smb_invalid_dir_cache(dir); - error = smb_proc_rmdir(dentry); - -out: - unlock_kernel(); - return error; -} - -static int -smb_unlink(struct inode *dir, struct dentry *dentry) -{ - int error; - - /* - * Close the file if it's open. - */ - lock_kernel(); - smb_close(dentry->d_inode); - - smb_invalid_dir_cache(dir); - error = smb_proc_unlink(dentry); - if (!error) - smb_renew_times(dentry); - unlock_kernel(); - return error; -} - -static int -smb_rename(struct inode *old_dir, struct dentry *old_dentry, - struct inode *new_dir, struct dentry *new_dentry) -{ - int error; - - /* - * Close any open files, and check whether to delete the - * target before attempting the rename. - */ - lock_kernel(); - if (old_dentry->d_inode) - smb_close(old_dentry->d_inode); - if (new_dentry->d_inode) { - smb_close(new_dentry->d_inode); - error = smb_proc_unlink(new_dentry); - if (error) { - VERBOSE("unlink %s/%s, error=%d\n", - DENTRY_PATH(new_dentry), error); - goto out; - } - /* FIXME */ - d_delete(new_dentry); - } - - smb_invalid_dir_cache(old_dir); - smb_invalid_dir_cache(new_dir); - error = smb_proc_mv(old_dentry, new_dentry); - if (!error) { - smb_renew_times(old_dentry); - smb_renew_times(new_dentry); - } -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, dev_t dev) -{ - int error; - struct iattr attr; - - attr.ia_valid = ATTR_MODE | ATTR_UID | ATTR_GID; - attr.ia_mode = mode; - current_euid_egid(&attr.ia_uid, &attr.ia_gid); - - if (!new_valid_dev(dev)) - return -EINVAL; - - 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/file.c b/fs/smbfs/file.c deleted file mode 100644 index 8e187a0f94bb..000000000000 --- a/fs/smbfs/file.c +++ /dev/null @@ -1,454 +0,0 @@ -/* - * file.c - * - * Copyright (C) 1995, 1996, 1997 by Paal-Kr. Engstad and Volker Lendecke - * Copyright (C) 1997 by Volker Lendecke - * - * 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 - -#include "smb_debug.h" -#include "proto.h" - -static int -smb_fsync(struct file *file, int datasync) -{ - struct dentry *dentry = file->f_path.dentry; - struct smb_sb_info *server = server_from_dentry(dentry); - int result; - - VERBOSE("sync file %s/%s\n", DENTRY_PATH(dentry)); - - /* - * The VFS will writepage() all dirty pages for us, but we - * should send a SMBflush to the server, letting it know that - * we want things synchronized with actual storage. - * - * Note: this function requires all pages to have been written already - * (should be ok with writepage_sync) - */ - result = smb_proc_flush(server, SMB_I(dentry->d_inode)->fileid); - return result; -} - -/* - * Read a page synchronously. - */ -static int -smb_readpage_sync(struct dentry *dentry, struct page *page) -{ - char *buffer = kmap(page); - loff_t offset = (loff_t)page->index << PAGE_CACHE_SHIFT; - struct smb_sb_info *server = server_from_dentry(dentry); - unsigned int rsize = smb_get_rsize(server); - int count = PAGE_SIZE; - int result; - - VERBOSE("file %s/%s, count=%d@%Ld, rsize=%d\n", - DENTRY_PATH(dentry), count, offset, rsize); - - result = smb_open(dentry, SMB_O_RDONLY); - if (result < 0) - goto io_error; - - do { - if (count < rsize) - rsize = count; - - result = server->ops->read(dentry->d_inode,offset,rsize,buffer); - if (result < 0) - goto io_error; - - count -= result; - offset += result; - buffer += result; - dentry->d_inode->i_atime = - current_fs_time(dentry->d_inode->i_sb); - if (result < rsize) - break; - } while (count); - - memset(buffer, 0, count); - flush_dcache_page(page); - SetPageUptodate(page); - result = 0; - -io_error: - kunmap(page); - unlock_page(page); - return result; -} - -/* - * We are called with the page locked and we unlock it when done. - */ -static int -smb_readpage(struct file *file, struct page *page) -{ - int error; - struct dentry *dentry = file->f_path.dentry; - - page_cache_get(page); - error = smb_readpage_sync(dentry, page); - page_cache_release(page); - return error; -} - -/* - * Write a page synchronously. - * Offset is the data offset within the page. - */ -static int -smb_writepage_sync(struct inode *inode, struct page *page, - unsigned long pageoffset, unsigned int count) -{ - loff_t offset; - char *buffer = kmap(page) + pageoffset; - struct smb_sb_info *server = server_from_inode(inode); - unsigned int wsize = smb_get_wsize(server); - int ret = 0; - - offset = ((loff_t)page->index << PAGE_CACHE_SHIFT) + pageoffset; - VERBOSE("file ino=%ld, fileid=%d, count=%d@%Ld, wsize=%d\n", - inode->i_ino, SMB_I(inode)->fileid, count, offset, wsize); - - do { - int write_ret; - - if (count < wsize) - wsize = count; - - write_ret = server->ops->write(inode, offset, wsize, buffer); - if (write_ret < 0) { - PARANOIA("failed write, wsize=%d, write_ret=%d\n", - wsize, write_ret); - ret = write_ret; - break; - } - /* N.B. what if result < wsize?? */ -#ifdef SMBFS_PARANOIA - if (write_ret < wsize) - PARANOIA("short write, wsize=%d, write_ret=%d\n", - wsize, write_ret); -#endif - buffer += wsize; - offset += wsize; - count -= wsize; - /* - * Update the inode now rather than waiting for a refresh. - */ - inode->i_mtime = inode->i_atime = current_fs_time(inode->i_sb); - SMB_I(inode)->flags |= SMB_F_LOCALWRITE; - if (offset > inode->i_size) - inode->i_size = offset; - } while (count); - - kunmap(page); - return ret; -} - -/* - * Write a page to the server. This will be used for NFS swapping only - * (for now), and we currently do this synchronously only. - * - * We are called with the page locked and we unlock it when done. - */ -static int -smb_writepage(struct page *page, struct writeback_control *wbc) -{ - struct address_space *mapping = page->mapping; - struct inode *inode; - unsigned long end_index; - unsigned offset = PAGE_CACHE_SIZE; - int err; - - BUG_ON(!mapping); - inode = mapping->host; - BUG_ON(!inode); - - end_index = inode->i_size >> PAGE_CACHE_SHIFT; - - /* easy case */ - if (page->index < end_index) - goto do_it; - /* things got complicated... */ - offset = inode->i_size & (PAGE_CACHE_SIZE-1); - /* OK, are we completely out? */ - if (page->index >= end_index+1 || !offset) - return 0; /* truncated - don't care */ -do_it: - page_cache_get(page); - err = smb_writepage_sync(inode, page, 0, offset); - SetPageUptodate(page); - unlock_page(page); - page_cache_release(page); - return err; -} - -static int -smb_updatepage(struct file *file, struct page *page, unsigned long offset, - unsigned int count) -{ - struct dentry *dentry = file->f_path.dentry; - - DEBUG1("(%s/%s %d@%lld)\n", DENTRY_PATH(dentry), count, - ((unsigned long long)page->index << PAGE_CACHE_SHIFT) + offset); - - return smb_writepage_sync(dentry->d_inode, page, offset, count); -} - -static ssize_t -smb_file_aio_read(struct kiocb *iocb, const struct iovec *iov, - unsigned long nr_segs, loff_t pos) -{ - struct file * file = iocb->ki_filp; - struct dentry * dentry = file->f_path.dentry; - ssize_t status; - - VERBOSE("file %s/%s, count=%lu@%lu\n", DENTRY_PATH(dentry), - (unsigned long) iocb->ki_left, (unsigned long) pos); - - status = smb_revalidate_inode(dentry); - if (status) { - PARANOIA("%s/%s validation failed, error=%Zd\n", - DENTRY_PATH(dentry), status); - goto out; - } - - VERBOSE("before read, size=%ld, flags=%x, atime=%ld\n", - (long)dentry->d_inode->i_size, - dentry->d_inode->i_flags, dentry->d_inode->i_atime.tv_sec); - - status = generic_file_aio_read(iocb, iov, nr_segs, pos); -out: - return status; -} - -static int -smb_file_mmap(struct file * file, struct vm_area_struct * vma) -{ - struct dentry * dentry = file->f_path.dentry; - int status; - - VERBOSE("file %s/%s, address %lu - %lu\n", - DENTRY_PATH(dentry), vma->vm_start, vma->vm_end); - - status = smb_revalidate_inode(dentry); - if (status) { - PARANOIA("%s/%s validation failed, error=%d\n", - DENTRY_PATH(dentry), status); - goto out; - } - status = generic_file_mmap(file, vma); -out: - return status; -} - -static ssize_t -smb_file_splice_read(struct file *file, loff_t *ppos, - struct pipe_inode_info *pipe, size_t count, - unsigned int flags) -{ - struct dentry *dentry = file->f_path.dentry; - ssize_t status; - - VERBOSE("file %s/%s, pos=%Ld, count=%lu\n", - DENTRY_PATH(dentry), *ppos, count); - - status = smb_revalidate_inode(dentry); - if (status) { - PARANOIA("%s/%s validation failed, error=%Zd\n", - DENTRY_PATH(dentry), status); - goto out; - } - status = generic_file_splice_read(file, ppos, pipe, count, flags); -out: - return status; -} - -/* - * This does the "real" work of the write. The generic routine has - * allocated the page, locked it, done all the page alignment stuff - * calculations etc. Now we should just copy the data from user - * space and write it back to the real medium.. - * - * If the writer ends up delaying the write, the writer needs to - * increment the page use counts until he is done with the page. - */ -static int smb_write_begin(struct file *file, struct address_space *mapping, - loff_t pos, unsigned len, unsigned flags, - struct page **pagep, void **fsdata) -{ - pgoff_t index = pos >> PAGE_CACHE_SHIFT; - *pagep = grab_cache_page_write_begin(mapping, index, flags); - if (!*pagep) - return -ENOMEM; - return 0; -} - -static int smb_write_end(struct file *file, struct address_space *mapping, - loff_t pos, unsigned len, unsigned copied, - struct page *page, void *fsdata) -{ - int status; - unsigned offset = pos & (PAGE_CACHE_SIZE - 1); - - lock_kernel(); - status = smb_updatepage(file, page, offset, copied); - unlock_kernel(); - - if (!status) { - if (!PageUptodate(page) && copied == PAGE_CACHE_SIZE) - SetPageUptodate(page); - status = copied; - } - - unlock_page(page); - page_cache_release(page); - - return status; -} - -const struct address_space_operations smb_file_aops = { - .readpage = smb_readpage, - .writepage = smb_writepage, - .write_begin = smb_write_begin, - .write_end = smb_write_end, -}; - -/* - * Write to a file (through the page cache). - */ -static ssize_t -smb_file_aio_write(struct kiocb *iocb, const struct iovec *iov, - unsigned long nr_segs, loff_t pos) -{ - struct file * file = iocb->ki_filp; - struct dentry * dentry = file->f_path.dentry; - ssize_t result; - - VERBOSE("file %s/%s, count=%lu@%lu\n", - DENTRY_PATH(dentry), - (unsigned long) iocb->ki_left, (unsigned long) pos); - - result = smb_revalidate_inode(dentry); - if (result) { - PARANOIA("%s/%s validation failed, error=%Zd\n", - DENTRY_PATH(dentry), result); - goto out; - } - - result = smb_open(dentry, SMB_O_WRONLY); - if (result) - goto out; - - if (iocb->ki_left > 0) { - result = generic_file_aio_write(iocb, iov, nr_segs, pos); - VERBOSE("pos=%ld, size=%ld, mtime=%ld, atime=%ld\n", - (long) file->f_pos, (long) dentry->d_inode->i_size, - dentry->d_inode->i_mtime.tv_sec, - dentry->d_inode->i_atime.tv_sec); - } -out: - return result; -} - -static int -smb_file_open(struct inode *inode, struct file * file) -{ - int result; - struct dentry *dentry = file->f_path.dentry; - int smb_mode = (file->f_mode & O_ACCMODE) - 1; - - lock_kernel(); - result = smb_open(dentry, smb_mode); - if (result) - goto out; - SMB_I(inode)->openers++; -out: - unlock_kernel(); - return result; -} - -static int -smb_file_release(struct inode *inode, struct file * file) -{ - lock_kernel(); - if (!--SMB_I(inode)->openers) { - /* We must flush any dirty pages now as we won't be able to - write anything after close. mmap can trigger this. - "openers" should perhaps include mmap'ers ... */ - filemap_write_and_wait(inode->i_mapping); - smb_close(inode); - } - unlock_kernel(); - return 0; -} - -/* - * Check whether the required access is compatible with - * an inode's permission. SMB doesn't recognize superuser - * privileges, so we need our own check for this. - */ -static int -smb_file_permission(struct inode *inode, int mask) -{ - int mode = inode->i_mode; - int error = 0; - - VERBOSE("mode=%x, mask=%x\n", mode, mask); - - /* Look at user permissions */ - mode >>= 6; - if (mask & ~mode & (MAY_READ | MAY_WRITE | MAY_EXEC)) - error = -EACCES; - return error; -} - -static loff_t smb_remote_llseek(struct file *file, loff_t offset, int origin) -{ - loff_t ret; - lock_kernel(); - ret = generic_file_llseek_unlocked(file, offset, origin); - unlock_kernel(); - return ret; -} - -const struct file_operations smb_file_operations = -{ - .llseek = smb_remote_llseek, - .read = do_sync_read, - .aio_read = smb_file_aio_read, - .write = do_sync_write, - .aio_write = smb_file_aio_write, - .unlocked_ioctl = smb_ioctl, - .mmap = smb_file_mmap, - .open = smb_file_open, - .release = smb_file_release, - .fsync = smb_fsync, - .splice_read = smb_file_splice_read, -}; - -const struct inode_operations smb_file_inode_operations = -{ - .permission = smb_file_permission, - .getattr = smb_getattr, - .setattr = smb_notify_change, -}; diff --git a/fs/smbfs/getopt.c b/fs/smbfs/getopt.c deleted file mode 100644 index 7ae0f5273ab1..000000000000 --- a/fs/smbfs/getopt.c +++ /dev/null @@ -1,64 +0,0 @@ -/* - * getopt.c - */ - -#include -#include -#include - -#include "getopt.h" - -/** - * smb_getopt - option parser - * @caller: name of the caller, for error messages - * @options: the options string - * @opts: an array of &struct option entries controlling parser operations - * @optopt: output; will contain the current option - * @optarg: output; will contain the value (if one exists) - * @flag: output; may be NULL; should point to a long for or'ing flags - * @value: output; may be NULL; will be overwritten with the integer value - * of the current argument. - * - * Helper to parse options on the format used by mount ("a=b,c=d,e,f"). - * Returns opts->val if a matching entry in the 'opts' array is found, - * 0 when no more tokens are found, -1 if an error is encountered. - */ -int smb_getopt(char *caller, char **options, struct option *opts, - char **optopt, char **optarg, unsigned long *flag, - unsigned long *value) -{ - char *token; - char *val; - int i; - - do { - if ((token = strsep(options, ",")) == NULL) - return 0; - } while (*token == '\0'); - *optopt = token; - - *optarg = NULL; - if ((val = strchr (token, '=')) != NULL) { - *val++ = 0; - if (value) - *value = simple_strtoul(val, NULL, 0); - *optarg = val; - } - - for (i = 0; opts[i].name != NULL; i++) { - if (!strcmp(opts[i].name, token)) { - if (!opts[i].flag && (!val || !*val)) { - printk("%s: the %s option requires an argument\n", - caller, token); - return -1; - } - - if (flag && opts[i].flag) - *flag |= opts[i].flag; - - return opts[i].val; - } - } - printk("%s: Unrecognized mount option %s\n", caller, token); - return -1; -} diff --git a/fs/smbfs/getopt.h b/fs/smbfs/getopt.h deleted file mode 100644 index 146219ac7c46..000000000000 --- a/fs/smbfs/getopt.h +++ /dev/null @@ -1,14 +0,0 @@ -#ifndef _LINUX_GETOPT_H -#define _LINUX_GETOPT_H - -struct option { - const char *name; - unsigned long flag; - int val; -}; - -extern int smb_getopt(char *caller, char **options, struct option *opts, - char **optopt, char **optarg, unsigned long *flag, - unsigned long *value); - -#endif /* _LINUX_GETOPT_H */ diff --git a/fs/smbfs/inode.c b/fs/smbfs/inode.c deleted file mode 100644 index 450c91941988..000000000000 --- a/fs/smbfs/inode.c +++ /dev/null @@ -1,839 +0,0 @@ -/* - * inode.c - * - * Copyright (C) 1995, 1996 by Paal-Kr. Engstad and Volker Lendecke - * Copyright (C) 1997 by Volker Lendecke - * - * 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 -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -#include "smb_debug.h" -#include "getopt.h" -#include "proto.h" - -/* Always pick a default string */ -#ifdef CONFIG_SMB_NLS_REMOTE -#define SMB_NLS_REMOTE CONFIG_SMB_NLS_REMOTE -#else -#define SMB_NLS_REMOTE "" -#endif - -#define SMB_TTL_DEFAULT 1000 - -static void smb_evict_inode(struct inode *); -static void smb_put_super(struct super_block *); -static int smb_statfs(struct dentry *, struct kstatfs *); -static int smb_show_options(struct seq_file *, struct vfsmount *); - -static struct kmem_cache *smb_inode_cachep; - -static struct inode *smb_alloc_inode(struct super_block *sb) -{ - struct smb_inode_info *ei; - ei = (struct smb_inode_info *)kmem_cache_alloc(smb_inode_cachep, GFP_KERNEL); - if (!ei) - return NULL; - return &ei->vfs_inode; -} - -static void smb_destroy_inode(struct inode *inode) -{ - kmem_cache_free(smb_inode_cachep, SMB_I(inode)); -} - -static void init_once(void *foo) -{ - struct smb_inode_info *ei = (struct smb_inode_info *) foo; - - inode_init_once(&ei->vfs_inode); -} - -static int init_inodecache(void) -{ - smb_inode_cachep = kmem_cache_create("smb_inode_cache", - sizeof(struct smb_inode_info), - 0, (SLAB_RECLAIM_ACCOUNT| - SLAB_MEM_SPREAD), - init_once); - if (smb_inode_cachep == NULL) - return -ENOMEM; - return 0; -} - -static void destroy_inodecache(void) -{ - kmem_cache_destroy(smb_inode_cachep); -} - -static int smb_remount(struct super_block *sb, int *flags, char *data) -{ - *flags |= MS_NODIRATIME; - return 0; -} - -static const struct super_operations smb_sops = -{ - .alloc_inode = smb_alloc_inode, - .destroy_inode = smb_destroy_inode, - .drop_inode = generic_delete_inode, - .evict_inode = smb_evict_inode, - .put_super = smb_put_super, - .statfs = smb_statfs, - .show_options = smb_show_options, - .remount_fs = smb_remount, -}; - - -/* We are always generating a new inode here */ -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); - - result = new_inode(sb); - if (!result) - return result; - result->i_ino = fattr->f_ino; - SMB_I(result)->open = 0; - SMB_I(result)->fileid = 0; - SMB_I(result)->access = 0; - SMB_I(result)->flags = 0; - SMB_I(result)->closed = 0; - SMB_I(result)->openers = 0; - smb_set_inode_attr(result, fattr); - if (S_ISREG(result->i_mode)) { - result->i_op = &smb_file_inode_operations; - result->i_fop = &smb_file_operations; - result->i_data.a_ops = &smb_file_aops; - } else if (S_ISDIR(result->i_mode)) { - 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; -} - -/* - * Copy the inode data to a smb_fattr structure. - */ -void -smb_get_inode_attr(struct inode *inode, struct smb_fattr *fattr) -{ - memset(fattr, 0, sizeof(struct smb_fattr)); - fattr->f_mode = inode->i_mode; - fattr->f_nlink = inode->i_nlink; - fattr->f_ino = inode->i_ino; - fattr->f_uid = inode->i_uid; - fattr->f_gid = inode->i_gid; - fattr->f_size = inode->i_size; - fattr->f_mtime = inode->i_mtime; - fattr->f_ctime = inode->i_ctime; - fattr->f_atime = inode->i_atime; - fattr->f_blocks = inode->i_blocks; - - fattr->attr = SMB_I(inode)->attr; - /* - * Keep the attributes in sync with the inode permissions. - */ - if (fattr->f_mode & S_IWUSR) - fattr->attr &= ~aRONLY; - else - fattr->attr |= aRONLY; -} - -/* - * Update the inode, possibly causing it to invalidate its pages if mtime/size - * is different from last time. - */ -void -smb_set_inode_attr(struct inode *inode, struct smb_fattr *fattr) -{ - struct smb_inode_info *ei = SMB_I(inode); - - /* - * A size change should have a different mtime, or same mtime - * but different size. - */ - time_t last_time = inode->i_mtime.tv_sec; - loff_t last_sz = inode->i_size; - - inode->i_mode = fattr->f_mode; - inode->i_nlink = fattr->f_nlink; - inode->i_uid = fattr->f_uid; - inode->i_gid = fattr->f_gid; - inode->i_ctime = fattr->f_ctime; - inode->i_blocks = fattr->f_blocks; - inode->i_size = fattr->f_size; - inode->i_mtime = fattr->f_mtime; - inode->i_atime = fattr->f_atime; - ei->attr = fattr->attr; - - /* - * Update the "last time refreshed" field for revalidation. - */ - ei->oldmtime = jiffies; - - if (inode->i_mtime.tv_sec != last_time || inode->i_size != last_sz) { - VERBOSE("%ld changed, old=%ld, new=%ld, oz=%ld, nz=%ld\n", - inode->i_ino, - (long) last_time, (long) inode->i_mtime.tv_sec, - (long) last_sz, (long) inode->i_size); - - if (!S_ISDIR(inode->i_mode)) - invalidate_remote_inode(inode); - } -} - -/* - * This is called if the connection has gone bad ... - * try to kill off all the current inodes. - */ -void -smb_invalidate_inodes(struct smb_sb_info *server) -{ - VERBOSE("\n"); - shrink_dcache_sb(SB_of(server)); - invalidate_inodes(SB_of(server)); -} - -/* - * This is called to update the inode attributes after - * we've made changes to a file or directory. - */ -static int -smb_refresh_inode(struct dentry *dentry) -{ - struct inode *inode = dentry->d_inode; - int error; - struct smb_fattr fattr; - - error = smb_proc_getattr(dentry, &fattr); - if (!error) { - smb_renew_times(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 { - /* - * Big trouble! The inode has become a new object, - * so any operations attempted on it are invalid. - * - * To limit damage, mark the inode as bad so that - * subsequent lookup validations will fail. - */ - PARANOIA("%s/%s changed mode, %07o to %07o\n", - DENTRY_PATH(dentry), - inode->i_mode, fattr.f_mode); - - fattr.f_mode = inode->i_mode; /* save mode */ - make_bad_inode(inode); - inode->i_mode = fattr.f_mode; /* restore mode */ - /* - * No need to worry about unhashing the dentry: the - * lookup validation will see that the inode is bad. - * But we do want to invalidate the caches ... - */ - if (!S_ISDIR(inode->i_mode)) - invalidate_remote_inode(inode); - else - smb_invalid_dir_cache(inode); - error = -EIO; - } - } - return error; -} - -/* - * This is called when we want to check whether the inode - * has changed on the server. If it has changed, we must - * invalidate our local caches. - */ -int -smb_revalidate_inode(struct dentry *dentry) -{ - struct smb_sb_info *s = server_from_dentry(dentry); - struct inode *inode = dentry->d_inode; - int error = 0; - - DEBUG1("smb_revalidate_inode\n"); - lock_kernel(); - - /* - * Check whether we've recently refreshed the inode. - */ - if (time_before(jiffies, SMB_I(inode)->oldmtime + SMB_MAX_AGE(s))) { - VERBOSE("up-to-date, ino=%ld, jiffies=%lu, oldtime=%lu\n", - inode->i_ino, jiffies, SMB_I(inode)->oldmtime); - goto out; - } - - error = smb_refresh_inode(dentry); -out: - unlock_kernel(); - return error; -} - -/* - * This routine is called when i_nlink == 0 and i_count goes to 0. - * All blocking cleanup operations need to go here to avoid races. - */ -static void -smb_evict_inode(struct inode *ino) -{ - DEBUG1("ino=%ld\n", ino->i_ino); - truncate_inode_pages(&ino->i_data, 0); - end_writeback(ino); - lock_kernel(); - if (smb_close(ino)) - PARANOIA("could not close inode %ld\n", ino->i_ino); - unlock_kernel(); -} - -static struct option opts[] = { - { "version", 0, 'v' }, - { "win95", SMB_MOUNT_WIN95, 1 }, - { "oldattr", SMB_MOUNT_OLDATTR, 1 }, - { "dirattr", SMB_MOUNT_DIRATTR, 1 }, - { "case", SMB_MOUNT_CASE, 1 }, - { "uid", 0, 'u' }, - { "gid", 0, 'g' }, - { "file_mode", 0, 'f' }, - { "dir_mode", 0, 'd' }, - { "iocharset", 0, 'i' }, - { "codepage", 0, 'c' }, - { "ttl", 0, 't' }, - { NULL, 0, 0} -}; - -static int -parse_options(struct smb_mount_data_kernel *mnt, char *options) -{ - int c; - unsigned long flags; - unsigned long value; - char *optarg; - char *optopt; - - flags = 0; - while ( (c = smb_getopt("smbfs", &options, opts, - &optopt, &optarg, &flags, &value)) > 0) { - - VERBOSE("'%s' -> '%s'\n", optopt, optarg ? optarg : ""); - switch (c) { - case 1: - /* got a "flag" option */ - break; - case 'v': - if (value != SMB_MOUNT_VERSION) { - printk ("smbfs: Bad mount version %ld, expected %d\n", - value, SMB_MOUNT_VERSION); - return 0; - } - mnt->version = value; - break; - case 'u': - mnt->uid = value; - flags |= SMB_MOUNT_UID; - break; - case 'g': - mnt->gid = value; - flags |= SMB_MOUNT_GID; - break; - case 'f': - mnt->file_mode = (value & S_IRWXUGO) | S_IFREG; - flags |= SMB_MOUNT_FMODE; - break; - case 'd': - mnt->dir_mode = (value & S_IRWXUGO) | S_IFDIR; - flags |= SMB_MOUNT_DMODE; - break; - case 'i': - strlcpy(mnt->codepage.local_name, optarg, - SMB_NLS_MAXNAMELEN); - break; - case 'c': - strlcpy(mnt->codepage.remote_name, optarg, - SMB_NLS_MAXNAMELEN); - break; - case 't': - mnt->ttl = value; - break; - default: - printk ("smbfs: Unrecognized mount option %s\n", - optopt); - return -1; - } - } - mnt->flags = flags; - return c; -} - -/* - * smb_show_options() is for displaying mount options in /proc/mounts. - * It tries to avoid showing settings that were not changed from their - * defaults. - */ -static int -smb_show_options(struct seq_file *s, struct vfsmount *m) -{ - struct smb_mount_data_kernel *mnt = SMB_SB(m->mnt_sb)->mnt; - int i; - - for (i = 0; opts[i].name != NULL; i++) - if (mnt->flags & opts[i].flag) - seq_printf(s, ",%s", opts[i].name); - - if (mnt->flags & SMB_MOUNT_UID) - seq_printf(s, ",uid=%d", mnt->uid); - if (mnt->flags & SMB_MOUNT_GID) - seq_printf(s, ",gid=%d", mnt->gid); - if (mnt->mounted_uid != 0) - seq_printf(s, ",mounted_uid=%d", mnt->mounted_uid); - - /* - * Defaults for file_mode and dir_mode are unknown to us; they - * depend on the current umask of the user doing the mount. - */ - if (mnt->flags & SMB_MOUNT_FMODE) - seq_printf(s, ",file_mode=%04o", mnt->file_mode & S_IRWXUGO); - if (mnt->flags & SMB_MOUNT_DMODE) - seq_printf(s, ",dir_mode=%04o", mnt->dir_mode & S_IRWXUGO); - - if (strcmp(mnt->codepage.local_name, CONFIG_NLS_DEFAULT)) - seq_printf(s, ",iocharset=%s", mnt->codepage.local_name); - if (strcmp(mnt->codepage.remote_name, SMB_NLS_REMOTE)) - seq_printf(s, ",codepage=%s", mnt->codepage.remote_name); - - if (mnt->ttl != SMB_TTL_DEFAULT) - seq_printf(s, ",ttl=%d", mnt->ttl); - - return 0; -} - -static void -smb_unload_nls(struct smb_sb_info *server) -{ - unload_nls(server->remote_nls); - unload_nls(server->local_nls); -} - -static void -smb_put_super(struct super_block *sb) -{ - struct smb_sb_info *server = SMB_SB(sb); - - lock_kernel(); - - smb_lock_server(server); - server->state = CONN_INVALID; - smbiod_unregister_server(server); - - smb_close_socket(server); - - if (server->conn_pid) - kill_pid(server->conn_pid, SIGTERM, 1); - - bdi_destroy(&server->bdi); - kfree(server->ops); - smb_unload_nls(server); - sb->s_fs_info = NULL; - smb_unlock_server(server); - put_pid(server->conn_pid); - kfree(server); - - unlock_kernel(); -} - -static int smb_fill_super(struct super_block *sb, void *raw_data, int silent) -{ - struct smb_sb_info *server; - struct smb_mount_data_kernel *mnt; - struct smb_mount_data *oldmnt; - struct inode *root_inode; - struct smb_fattr root; - int ver; - void *mem; - static int warn_count; - - if (warn_count < 5) { - warn_count++; - printk(KERN_EMERG "smbfs is deprecated and will be removed" - " from the 2.6.27 kernel. Please migrate to cifs\n"); - } - - if (!raw_data) - goto out_no_data; - - oldmnt = (struct smb_mount_data *) raw_data; - ver = oldmnt->version; - if (ver != SMB_MOUNT_OLDVERSION && cpu_to_be32(ver) != SMB_MOUNT_ASCII) - goto out_wrong_data; - - sb->s_flags |= MS_NODIRATIME; - sb->s_blocksize = 1024; /* Eh... Is this correct? */ - sb->s_blocksize_bits = 10; - sb->s_magic = SMB_SUPER_MAGIC; - sb->s_op = &smb_sops; - sb->s_time_gran = 100; - - server = kzalloc(sizeof(struct smb_sb_info), GFP_KERNEL); - if (!server) - goto out_no_server; - sb->s_fs_info = server; - - if (bdi_setup_and_register(&server->bdi, "smbfs", BDI_CAP_MAP_COPY)) - goto out_bdi; - - sb->s_bdi = &server->bdi; - - server->super_block = sb; - server->mnt = NULL; - server->sock_file = NULL; - init_waitqueue_head(&server->conn_wq); - init_MUTEX(&server->sem); - INIT_LIST_HEAD(&server->entry); - INIT_LIST_HEAD(&server->xmitq); - INIT_LIST_HEAD(&server->recvq); - server->conn_error = 0; - server->conn_pid = NULL; - server->state = CONN_INVALID; /* no connection yet */ - server->generation = 0; - - /* Allocate the global temp buffer and some superblock helper structs */ - /* FIXME: move these to the smb_sb_info struct */ - VERBOSE("alloc chunk = %lu\n", sizeof(struct smb_ops) + - sizeof(struct smb_mount_data_kernel)); - mem = kmalloc(sizeof(struct smb_ops) + - sizeof(struct smb_mount_data_kernel), GFP_KERNEL); - if (!mem) - goto out_no_mem; - - server->ops = mem; - smb_install_null_ops(server->ops); - server->mnt = mem + sizeof(struct smb_ops); - - /* Setup NLS stuff */ - server->remote_nls = NULL; - server->local_nls = NULL; - - mnt = server->mnt; - - memset(mnt, 0, sizeof(struct smb_mount_data_kernel)); - strlcpy(mnt->codepage.local_name, CONFIG_NLS_DEFAULT, - SMB_NLS_MAXNAMELEN); - strlcpy(mnt->codepage.remote_name, SMB_NLS_REMOTE, - SMB_NLS_MAXNAMELEN); - - mnt->ttl = SMB_TTL_DEFAULT; - if (ver == SMB_MOUNT_OLDVERSION) { - mnt->version = oldmnt->version; - - SET_UID(mnt->uid, oldmnt->uid); - SET_GID(mnt->gid, oldmnt->gid); - - mnt->file_mode = (oldmnt->file_mode & S_IRWXUGO) | S_IFREG; - mnt->dir_mode = (oldmnt->dir_mode & S_IRWXUGO) | S_IFDIR; - - mnt->flags = (oldmnt->file_mode >> 9) | SMB_MOUNT_UID | - SMB_MOUNT_GID | SMB_MOUNT_FMODE | SMB_MOUNT_DMODE; - } else { - mnt->file_mode = S_IRWXU | S_IRGRP | S_IXGRP | - S_IROTH | S_IXOTH | S_IFREG; - mnt->dir_mode = S_IRWXU | S_IRGRP | S_IXGRP | - S_IROTH | S_IXOTH | S_IFDIR; - if (parse_options(mnt, raw_data)) - goto out_bad_option; - } - mnt->mounted_uid = current_uid(); - smb_setcodepage(server, &mnt->codepage); - - /* - * Display the enabled options - * Note: smb_proc_getattr uses these in 2.4 (but was changed in 2.2) - */ - if (mnt->flags & SMB_MOUNT_OLDATTR) - printk("SMBFS: Using core getattr (Win 95 speedup)\n"); - else if (mnt->flags & SMB_MOUNT_DIRATTR) - printk("SMBFS: Using dir ff getattr\n"); - - if (smbiod_register_server(server) < 0) { - printk(KERN_ERR "smbfs: failed to start smbiod\n"); - goto out_no_smbiod; - } - - /* - * Keep the super block locked while we get the root inode. - */ - smb_init_root_dirent(server, &root, sb); - root_inode = smb_iget(sb, &root); - if (!root_inode) - goto out_no_root; - - sb->s_root = d_alloc_root(root_inode); - if (!sb->s_root) - goto out_no_root; - - smb_new_dentry(sb->s_root); - - return 0; - -out_no_root: - iput(root_inode); -out_no_smbiod: - smb_unload_nls(server); -out_bad_option: - kfree(mem); -out_no_mem: - bdi_destroy(&server->bdi); -out_bdi: - if (!server->mnt) - printk(KERN_ERR "smb_fill_super: allocation failure\n"); - sb->s_fs_info = NULL; - kfree(server); - goto out_fail; -out_wrong_data: - printk(KERN_ERR "smbfs: mount_data version %d is not supported\n", ver); - goto out_fail; -out_no_data: - printk(KERN_ERR "smb_fill_super: missing data argument\n"); -out_fail: - return -EINVAL; -out_no_server: - printk(KERN_ERR "smb_fill_super: cannot allocate struct smb_sb_info\n"); - return -ENOMEM; -} - -static int -smb_statfs(struct dentry *dentry, struct kstatfs *buf) -{ - int result; - - lock_kernel(); - - result = smb_proc_dskattr(dentry, buf); - - unlock_kernel(); - - buf->f_type = SMB_SUPER_MAGIC; - buf->f_namelen = SMB_MAXPATHLEN; - return result; -} - -int smb_getattr(struct vfsmount *mnt, struct dentry *dentry, struct kstat *stat) -{ - int err = smb_revalidate_inode(dentry); - if (!err) - generic_fillattr(dentry->d_inode, stat); - return err; -} - -int -smb_notify_change(struct dentry *dentry, struct iattr *attr) -{ - struct inode *inode = dentry->d_inode; - struct smb_sb_info *server = server_from_dentry(dentry); - unsigned int mask = (S_IFREG | S_IFDIR | S_IRWXUGO); - int error, changed, refresh = 0; - struct smb_fattr fattr; - - lock_kernel(); - - 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; - - 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); - - filemap_write_and_wait(inode->i_mapping); - - error = smb_open(dentry, O_WRONLY); - if (error) - goto out; - error = server->ops->truncate(inode, attr->ia_size); - if (error) - goto out; - truncate_setsize(inode, attr->ia_size); - 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 - * change time, so we don't attempt to change it. - */ - smb_get_inode_attr(inode, &fattr); - - 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) - 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; - -out: - if (refresh) - smb_refresh_inode(dentry); - unlock_kernel(); - return error; -} - -static int smb_get_sb(struct file_system_type *fs_type, - int flags, const char *dev_name, void *data, struct vfsmount *mnt) -{ - return get_sb_nodev(fs_type, flags, data, smb_fill_super, mnt); -} - -static struct file_system_type smb_fs_type = { - .owner = THIS_MODULE, - .name = "smbfs", - .get_sb = smb_get_sb, - .kill_sb = kill_anon_super, - .fs_flags = FS_BINARY_MOUNTDATA, -}; - -static int __init init_smb_fs(void) -{ - int err; - DEBUG1("registering ...\n"); - - err = init_inodecache(); - if (err) - goto out_inode; - err = smb_init_request_cache(); - if (err) - goto out_request; - err = register_filesystem(&smb_fs_type); - if (err) - goto out; - return 0; -out: - smb_destroy_request_cache(); -out_request: - destroy_inodecache(); -out_inode: - return err; -} - -static void __exit exit_smb_fs(void) -{ - DEBUG1("unregistering ...\n"); - unregister_filesystem(&smb_fs_type); - smb_destroy_request_cache(); - destroy_inodecache(); -} - -module_init(init_smb_fs) -module_exit(exit_smb_fs) -MODULE_LICENSE("GPL"); diff --git a/fs/smbfs/ioctl.c b/fs/smbfs/ioctl.c deleted file mode 100644 index 07215312ad39..000000000000 --- a/fs/smbfs/ioctl.c +++ /dev/null @@ -1,69 +0,0 @@ -/* - * ioctl.c - * - * Copyright (C) 1995, 1996 by Volker Lendecke - * Copyright (C) 1997 by Volker Lendecke - * - * 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 "proto.h" - -long -smb_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) -{ - struct smb_sb_info *server = server_from_inode(filp->f_path.dentry->d_inode); - struct smb_conn_opt opt; - int result = -EINVAL; - - lock_kernel(); - switch (cmd) { - uid16_t uid16; - uid_t uid32; - case SMB_IOC_GETMOUNTUID: - SET_UID(uid16, server->mnt->mounted_uid); - result = put_user(uid16, (uid16_t __user *) arg); - break; - case SMB_IOC_GETMOUNTUID32: - SET_UID(uid32, server->mnt->mounted_uid); - result = put_user(uid32, (uid_t __user *) arg); - break; - - case SMB_IOC_NEWCONN: - /* arg is smb_conn_opt, or NULL if no connection was made */ - if (!arg) { - result = 0; - smb_lock_server(server); - server->state = CONN_RETRIED; - printk(KERN_ERR "Connection attempt failed! [%d]\n", - server->conn_error); - smbiod_flush(server); - smb_unlock_server(server); - break; - } - - result = -EFAULT; - if (!copy_from_user(&opt, (void __user *)arg, sizeof(opt))) - result = smb_newconn(server, &opt); - break; - default: - break; - } - unlock_kernel(); - - return result; -} diff --git a/fs/smbfs/proc.c b/fs/smbfs/proc.c deleted file mode 100644 index 71c29b6670b4..000000000000 --- a/fs/smbfs/proc.c +++ /dev/null @@ -1,3507 +0,0 @@ -/* - * proc.c - * - * Copyright (C) 1995, 1996 by Paal-Kr. Engstad and Volker Lendecke - * Copyright (C) 1997 by Volker Lendecke - * - * 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 -#include -#include - -#include - -#include -#include - -#include "smb_debug.h" -#include "proto.h" -#include "request.h" - - -/* Features. Undefine if they cause problems, this should perhaps be a - config option. */ -#define SMBFS_POSIX_UNLINK 1 - -/* Allow smb_retry to be interrupted. */ -#define SMB_RETRY_INTR - -#define SMB_VWV(packet) ((packet) + SMB_HEADER_LEN) -#define SMB_CMD(packet) (*(packet+8)) -#define SMB_WCT(packet) (*(packet+SMB_HEADER_LEN - 1)) - -#define SMB_DIRINFO_SIZE 43 -#define SMB_STATUS_SIZE 21 - -#define SMB_ST_BLKSIZE (PAGE_SIZE) -#define SMB_ST_BLKSHIFT (PAGE_SHIFT) - -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 struct smb_ops smb_ops_null; - -static void -smb_init_dirent(struct smb_sb_info *server, struct smb_fattr *fattr); -static void -smb_finish_dirent(struct smb_sb_info *server, struct smb_fattr *fattr); -static int -smb_proc_getattr_core(struct smb_sb_info *server, struct dentry *dir, - struct smb_fattr *fattr); -static int -smb_proc_getattr_ff(struct smb_sb_info *server, struct dentry *dentry, - struct smb_fattr *fattr); -static int -smb_proc_setattr_core(struct smb_sb_info *server, struct dentry *dentry, - u16 attr); -static int -smb_proc_setattr_ext(struct smb_sb_info *server, - struct inode *inode, struct smb_fattr *fattr); -static int -smb_proc_query_cifsunix(struct smb_sb_info *server); -static void -install_ops(struct smb_ops *dst, struct smb_ops *src); - - -static void -str_upper(char *name, int len) -{ - while (len--) - { - if (*name >= 'a' && *name <= 'z') - *name -= ('a' - 'A'); - name++; - } -} - -#if 0 -static void -str_lower(char *name, int len) -{ - while (len--) - { - if (*name >= 'A' && *name <= 'Z') - *name += ('a' - 'A'); - name++; - } -} -#endif - -/* reverse a string inline. This is used by the dircache walking routines */ -static void reverse_string(char *buf, int len) -{ - char c; - char *end = buf+len-1; - - while(buf < end) { - c = *buf; - *(buf++) = *end; - *(end--) = c; - } -} - -/* no conversion, just a wrapper for memcpy. */ -static int convert_memcpy(unsigned char *output, int olen, - const unsigned char *input, int ilen, - struct nls_table *nls_from, - struct nls_table *nls_to) -{ - if (olen < ilen) - return -ENAMETOOLONG; - memcpy(output, input, ilen); - return ilen; -} - -static inline int write_char(unsigned char ch, char *output, int olen) -{ - if (olen < 4) - return -ENAMETOOLONG; - sprintf(output, ":x%02x", ch); - return 4; -} - -static inline int write_unichar(wchar_t ch, char *output, int olen) -{ - if (olen < 5) - return -ENAMETOOLONG; - sprintf(output, ":%04x", ch); - return 5; -} - -/* convert from one "codepage" to another (possibly being utf8). */ -static int convert_cp(unsigned char *output, int olen, - const unsigned char *input, int ilen, - struct nls_table *nls_from, - struct nls_table *nls_to) -{ - int len = 0; - int n; - wchar_t ch; - - while (ilen > 0) { - /* convert by changing to unicode and back to the new cp */ - n = nls_from->char2uni(input, ilen, &ch); - if (n == -EINVAL) { - ilen--; - n = write_char(*input++, output, olen); - if (n < 0) - goto fail; - output += n; - olen -= n; - len += n; - continue; - } else if (n < 0) - goto fail; - input += n; - ilen -= n; - - n = nls_to->uni2char(ch, output, olen); - if (n == -EINVAL) - n = write_unichar(ch, output, olen); - if (n < 0) - goto fail; - output += n; - olen -= n; - - len += n; - } - return len; -fail: - return n; -} - -/* ----------------------------------------------------------- */ - -/* - * nls_unicode - * - * This encodes/decodes little endian unicode format - */ - -static int uni2char(wchar_t uni, unsigned char *out, int boundlen) -{ - if (boundlen < 2) - return -EINVAL; - *out++ = uni & 0xff; - *out++ = uni >> 8; - return 2; -} - -static int char2uni(const unsigned char *rawstring, int boundlen, wchar_t *uni) -{ - if (boundlen < 2) - return -EINVAL; - *uni = (rawstring[1] << 8) | rawstring[0]; - return 2; -} - -static struct nls_table unicode_table = { - .charset = "unicode", - .uni2char = uni2char, - .char2uni = char2uni, -}; - -/* ----------------------------------------------------------- */ - -static int setcodepage(struct nls_table **p, char *name) -{ - struct nls_table *nls; - - if (!name || !*name) { - nls = NULL; - } else if ( (nls = load_nls(name)) == NULL) { - printk (KERN_ERR "smbfs: failed to load nls '%s'\n", name); - return -EINVAL; - } - - /* if already set, unload the previous one. */ - if (*p && *p != &unicode_table) - unload_nls(*p); - *p = nls; - - return 0; -} - -/* Handles all changes to codepage settings. */ -int smb_setcodepage(struct smb_sb_info *server, struct smb_nls_codepage *cp) -{ - int n = 0; - - smb_lock_server(server); - - /* Don't load any nls_* at all, if no remote is requested */ - if (!*cp->remote_name) - goto out; - - /* local */ - n = setcodepage(&server->local_nls, cp->local_name); - if (n != 0) - goto out; - - /* remote */ - if (!strcmp(cp->remote_name, "unicode")) { - server->remote_nls = &unicode_table; - } else { - n = setcodepage(&server->remote_nls, cp->remote_name); - if (n != 0) - setcodepage(&server->local_nls, NULL); - } - -out: - if (server->local_nls != NULL && server->remote_nls != NULL) - server->ops->convert = convert_cp; - else - server->ops->convert = convert_memcpy; - - smb_unlock_server(server); - return n; -} - - -/*****************************************************************************/ -/* */ -/* Encoding/Decoding section */ -/* */ -/*****************************************************************************/ - -static __u8 * -smb_encode_smb_length(__u8 * p, __u32 len) -{ - *p = 0; - *(p+1) = 0; - *(p+2) = (len & 0xFF00) >> 8; - *(p+3) = (len & 0xFF); - if (len > 0xFFFF) - { - *(p+1) = 1; - } - return p + 4; -} - -/* - * smb_build_path: build the path to entry and name storing it in buf. - * The path returned will have the trailing '\0'. - */ -static int smb_build_path(struct smb_sb_info *server, unsigned char *buf, - int maxlen, - struct dentry *entry, struct qstr *name) -{ - unsigned char *path = buf; - int len; - int unicode = (server->mnt->flags & SMB_MOUNT_UNICODE) != 0; - - if (maxlen < (2< SMB_MAXPATHLEN + 1) - maxlen = SMB_MAXPATHLEN + 1; - - if (entry == NULL) - goto test_name_and_out; - - /* - * If IS_ROOT, we have to do no walking at all. - */ - if (IS_ROOT(entry) && !name) { - *path++ = '\\'; - if (unicode) *path++ = '\0'; - *path++ = '\0'; - if (unicode) *path++ = '\0'; - return path-buf; - } - - /* - * Build the path string walking the tree backward from end to ROOT - * and store it in reversed order [see reverse_string()] - */ - dget(entry); - spin_lock(&entry->d_lock); - while (!IS_ROOT(entry)) { - struct dentry *parent; - - if (maxlen < (3<d_lock); - dput(entry); - return -ENAMETOOLONG; - } - - len = server->ops->convert(path, maxlen-2, - entry->d_name.name, entry->d_name.len, - server->local_nls, server->remote_nls); - if (len < 0) { - spin_unlock(&entry->d_lock); - dput(entry); - return len; - } - reverse_string(path, len); - path += len; - if (unicode) { - /* Note: reverse order */ - *path++ = '\0'; - maxlen--; - } - *path++ = '\\'; - maxlen -= len+1; - - parent = entry->d_parent; - dget(parent); - spin_unlock(&entry->d_lock); - dput(entry); - entry = parent; - spin_lock(&entry->d_lock); - } - spin_unlock(&entry->d_lock); - dput(entry); - reverse_string(buf, path-buf); - - /* maxlen has space for at least one char */ -test_name_and_out: - if (name) { - if (maxlen < (3<ops->convert(path, maxlen-2, - name->name, name->len, - server->local_nls, server->remote_nls); - if (len < 0) - return len; - path += len; - maxlen -= len+1; - } - /* maxlen has space for at least one char */ - *path++ = '\0'; - if (unicode) *path++ = '\0'; - return path-buf; -} - -static int smb_encode_path(struct smb_sb_info *server, char *buf, int maxlen, - struct dentry *dir, struct qstr *name) -{ - int result; - - result = smb_build_path(server, buf, maxlen, dir, name); - if (result < 0) - goto out; - if (server->opt.protocol <= SMB_PROTOCOL_COREPLUS) - str_upper(buf, result); -out: - return result; -} - -/* encode_path for non-trans2 request SMBs */ -static int smb_simple_encode_path(struct smb_request *req, char **p, - struct dentry * entry, struct qstr * name) -{ - struct smb_sb_info *server = req->rq_server; - char *s = *p; - int res; - int maxlen = ((char *)req->rq_buffer + req->rq_bufsize) - s; - int unicode = (server->mnt->flags & SMB_MOUNT_UNICODE); - - if (!maxlen) - return -ENAMETOOLONG; - *s++ = 4; /* ASCII data format */ - - /* - * SMB Unicode strings must be 16bit aligned relative the start of the - * packet. If they are not they must be padded with 0. - */ - if (unicode) { - int align = s - (char *)req->rq_buffer; - if (!(align & 1)) { - *s++ = '\0'; - maxlen--; - } - } - - res = smb_encode_path(server, s, maxlen-1, entry, name); - if (res < 0) - return res; - *p = s + res; - return 0; -} - -/* The following are taken directly from msdos-fs */ - -/* Linear day numbers of the respective 1sts in non-leap years. */ - -static int day_n[] = -{0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 0, 0, 0, 0}; - /* JanFebMarApr May Jun Jul Aug Sep Oct Nov Dec */ - - -static time_t -utc2local(struct smb_sb_info *server, time_t time) -{ - return time - server->opt.serverzone*60; -} - -static time_t -local2utc(struct smb_sb_info *server, time_t time) -{ - return time + server->opt.serverzone*60; -} - -/* Convert a MS-DOS time/date pair to a UNIX date (seconds since 1 1 70). */ - -static time_t -date_dos2unix(struct smb_sb_info *server, __u16 date, __u16 time) -{ - int month, year; - time_t secs; - - /* first subtract and mask after that... Otherwise, if - date == 0, bad things happen */ - month = ((date >> 5) - 1) & 15; - year = date >> 9; - secs = (time & 31) * 2 + 60 * ((time >> 5) & 63) + (time >> 11) * 3600 + 86400 * - ((date & 31) - 1 + day_n[month] + (year / 4) + year * 365 - ((year & 3) == 0 && - month < 2 ? 1 : 0) + 3653); - /* days since 1.1.70 plus 80's leap day */ - return local2utc(server, secs); -} - - -/* Convert linear UNIX date to a MS-DOS time/date pair. */ - -static void -date_unix2dos(struct smb_sb_info *server, - int unix_date, __u16 *date, __u16 *time) -{ - int day, year, nl_day, month; - - unix_date = utc2local(server, unix_date); - if (unix_date < 315532800) - unix_date = 315532800; - - *time = (unix_date % 60) / 2 + - (((unix_date / 60) % 60) << 5) + - (((unix_date / 3600) % 24) << 11); - - day = unix_date / 86400 - 3652; - year = day / 365; - if ((year + 3) / 4 + 365 * year > day) - year--; - day -= (year + 3) / 4 + 365 * year; - if (day == 59 && !(year & 3)) { - nl_day = day; - month = 2; - } else { - nl_day = (year & 3) || day <= 59 ? day : day - 1; - for (month = 1; month < 12; month++) - if (day_n[month] > nl_day) - break; - } - *date = nl_day - day_n[month - 1] + 1 + (month << 5) + (year << 9); -} - -/* The following are taken from fs/ntfs/util.c */ - -#define NTFS_TIME_OFFSET ((u64)(369*365 + 89) * 24 * 3600 * 10000000) - -/* - * Convert the NT UTC (based 1601-01-01, in hundred nanosecond units) - * into Unix UTC (based 1970-01-01, in seconds). - */ -static struct timespec -smb_ntutc2unixutc(u64 ntutc) -{ - struct timespec ts; - /* FIXME: what about the timezone difference? */ - /* Subtract the NTFS time offset, then convert to 1s intervals. */ - u64 t = ntutc - NTFS_TIME_OFFSET; - ts.tv_nsec = do_div(t, 10000000) * 100; - ts.tv_sec = t; - return ts; -} - -/* Convert the Unix UTC into NT time */ -static u64 -smb_unixutc2ntutc(struct timespec ts) -{ - /* Note: timezone conversion is probably wrong. */ - /* return ((u64)utc2local(server, t)) * 10000000 + NTFS_TIME_OFFSET; */ - return ((u64)ts.tv_sec) * 10000000 + ts.tv_nsec/100 + 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 (S_ISREG(mode)) - return UNIX_TYPE_FILE; - if (S_ISDIR(mode)) - return UNIX_TYPE_DIR; - if (S_ISLNK(mode)) - return UNIX_TYPE_SYMLINK; - if (S_ISCHR(mode)) - return UNIX_TYPE_CHARDEV; - if (S_ISBLK(mode)) - return UNIX_TYPE_BLKDEV; - if (S_ISFIFO(mode)) - return UNIX_TYPE_FIFO; - if (S_ISSOCK(mode)) - return UNIX_TYPE_SOCKET; - return UNIX_TYPE_UNKNOWN; -} - - -/*****************************************************************************/ -/* */ -/* Support section. */ -/* */ -/*****************************************************************************/ - -__u32 -smb_len(__u8 * p) -{ - return ((*(p+1) & 0x1) << 16L) | (*(p+2) << 8L) | *(p+3); -} - -static __u16 -smb_bcc(__u8 * packet) -{ - int pos = SMB_HEADER_LEN + SMB_WCT(packet) * sizeof(__u16); - return WVAL(packet, pos); -} - -/* smb_valid_packet: We check if packet fulfills the basic - requirements of a smb packet */ - -static int -smb_valid_packet(__u8 * packet) -{ - return (packet[4] == 0xff - && packet[5] == 'S' - && packet[6] == 'M' - && packet[7] == 'B' - && (smb_len(packet) + 4 == SMB_HEADER_LEN - + SMB_WCT(packet) * 2 + smb_bcc(packet))); -} - -/* smb_verify: We check if we got the answer we expected, and if we - got enough data. If bcc == -1, we don't care. */ - -static int -smb_verify(__u8 * packet, int command, int wct, int bcc) -{ - if (SMB_CMD(packet) != command) - goto bad_command; - if (SMB_WCT(packet) < wct) - goto bad_wct; - if (bcc != -1 && smb_bcc(packet) < bcc) - goto bad_bcc; - return 0; - -bad_command: - printk(KERN_ERR "smb_verify: command=%x, SMB_CMD=%x??\n", - command, SMB_CMD(packet)); - goto fail; -bad_wct: - printk(KERN_ERR "smb_verify: command=%x, wct=%d, SMB_WCT=%d??\n", - command, wct, SMB_WCT(packet)); - goto fail; -bad_bcc: - printk(KERN_ERR "smb_verify: command=%x, bcc=%d, SMB_BCC=%d??\n", - command, bcc, smb_bcc(packet)); -fail: - return -EIO; -} - -/* - * Returns the maximum read or write size for the "payload". Making all of the - * packet fit within the negotiated max_xmit size. - * - * N.B. Since this value is usually computed before locking the server, - * the server's packet size must never be decreased! - */ -static inline int -smb_get_xmitsize(struct smb_sb_info *server, int overhead) -{ - return server->opt.max_xmit - overhead; -} - -/* - * Calculate the maximum read size - */ -int -smb_get_rsize(struct smb_sb_info *server) -{ - /* readX has 12 parameters, read has 5 */ - int overhead = SMB_HEADER_LEN + 12 * sizeof(__u16) + 2 + 1 + 2; - int size = smb_get_xmitsize(server, overhead); - - VERBOSE("xmit=%d, size=%d\n", server->opt.max_xmit, size); - - return size; -} - -/* - * Calculate the maximum write size - */ -int -smb_get_wsize(struct smb_sb_info *server) -{ - /* writeX has 14 parameters, write has 5 */ - int overhead = SMB_HEADER_LEN + 14 * sizeof(__u16) + 2 + 1 + 2; - int size = smb_get_xmitsize(server, overhead); - - VERBOSE("xmit=%d, size=%d\n", server->opt.max_xmit, size); - - return size; -} - -/* - * Convert SMB error codes to -E... errno values. - */ -int -smb_errno(struct smb_request *req) -{ - int errcls = req->rq_rcls; - int error = req->rq_err; - char *class = "Unknown"; - - VERBOSE("errcls %d code %d from command 0x%x\n", - errcls, error, SMB_CMD(req->rq_header)); - - if (errcls == ERRDOS) { - switch (error) { - case ERRbadfunc: - return -EINVAL; - case ERRbadfile: - case ERRbadpath: - return -ENOENT; - case ERRnofids: - return -EMFILE; - case ERRnoaccess: - return -EACCES; - case ERRbadfid: - return -EBADF; - case ERRbadmcb: - return -EREMOTEIO; - case ERRnomem: - return -ENOMEM; - case ERRbadmem: - return -EFAULT; - case ERRbadenv: - case ERRbadformat: - return -EREMOTEIO; - case ERRbadaccess: - return -EACCES; - case ERRbaddata: - return -E2BIG; - case ERRbaddrive: - return -ENXIO; - case ERRremcd: - return -EREMOTEIO; - case ERRdiffdevice: - return -EXDEV; - case ERRnofiles: - return -ENOENT; - case ERRbadshare: - return -ETXTBSY; - case ERRlock: - return -EDEADLK; - case ERRfilexists: - return -EEXIST; - case ERROR_INVALID_PARAMETER: - return -EINVAL; - case ERROR_DISK_FULL: - return -ENOSPC; - case ERROR_INVALID_NAME: - return -ENOENT; - case ERROR_DIR_NOT_EMPTY: - return -ENOTEMPTY; - case ERROR_NOT_LOCKED: - return -ENOLCK; - case ERROR_ALREADY_EXISTS: - return -EEXIST; - default: - class = "ERRDOS"; - goto err_unknown; - } - } else if (errcls == ERRSRV) { - switch (error) { - /* N.B. This is wrong ... EIO ? */ - case ERRerror: - return -ENFILE; - case ERRbadpw: - return -EINVAL; - case ERRbadtype: - case ERRtimeout: - return -EIO; - case ERRaccess: - return -EACCES; - /* - * This is a fatal error, as it means the "tree ID" - * for this connection is no longer valid. We map - * to a special error code and get a new connection. - */ - case ERRinvnid: - return -EBADSLT; - default: - class = "ERRSRV"; - goto err_unknown; - } - } else if (errcls == ERRHRD) { - switch (error) { - case ERRnowrite: - return -EROFS; - case ERRbadunit: - return -ENODEV; - case ERRnotready: - return -EUCLEAN; - case ERRbadcmd: - case ERRdata: - return -EIO; - case ERRbadreq: - return -ERANGE; - case ERRbadshare: - return -ETXTBSY; - case ERRlock: - return -EDEADLK; - case ERRdiskfull: - return -ENOSPC; - default: - class = "ERRHRD"; - goto err_unknown; - } - } else if (errcls == ERRCMD) { - class = "ERRCMD"; - } else if (errcls == SUCCESS) { - return 0; /* This is the only valid 0 return */ - } - -err_unknown: - printk(KERN_ERR "smb_errno: class %s, code %d from command 0x%x\n", - class, error, SMB_CMD(req->rq_header)); - return -EIO; -} - -/* smb_request_ok: We expect the server to be locked. Then we do the - request and check the answer completely. When smb_request_ok - returns 0, you can be quite sure that everything went well. When - the answer is <=0, the returned number is a valid unix errno. */ - -static int -smb_request_ok(struct smb_request *req, int command, int wct, int bcc) -{ - int result; - - req->rq_resp_wct = wct; - req->rq_resp_bcc = bcc; - - result = smb_add_request(req); - if (result != 0) { - DEBUG1("smb_request failed\n"); - goto out; - } - - if (smb_valid_packet(req->rq_header) != 0) { - PARANOIA("invalid packet!\n"); - goto out; - } - - result = smb_verify(req->rq_header, command, wct, bcc); - -out: - return result; -} - -/* - * This implements the NEWCONN ioctl. It installs the server pid, - * sets server->state to CONN_VALID, and wakes up the waiting process. - */ -int -smb_newconn(struct smb_sb_info *server, struct smb_conn_opt *opt) -{ - struct file *filp; - struct sock *sk; - int error; - - VERBOSE("fd=%d, pid=%d\n", opt->fd, current->pid); - - smb_lock_server(server); - - /* - * Make sure we don't already have a valid connection ... - */ - error = -EINVAL; - if (server->state == CONN_VALID) - goto out; - - error = -EACCES; - if (current_uid() != server->mnt->mounted_uid && - !capable(CAP_SYS_ADMIN)) - goto out; - - error = -EBADF; - filp = fget(opt->fd); - if (!filp) - goto out; - if (!smb_valid_socket(filp->f_path.dentry->d_inode)) - goto out_putf; - - server->sock_file = filp; - server->conn_pid = get_pid(task_pid(current)); - server->opt = *opt; - server->generation += 1; - server->state = CONN_VALID; - error = 0; - - if (server->conn_error) { - /* - * conn_error is the returncode we originally decided to - * drop the old connection on. This message should be positive - * and not make people ask questions on why smbfs is printing - * error messages ... - */ - printk(KERN_INFO "SMB connection re-established (%d)\n", - server->conn_error); - server->conn_error = 0; - } - - /* - * Store the server in sock user_data (Only used by sunrpc) - */ - sk = SOCKET_I(filp->f_path.dentry->d_inode)->sk; - sk->sk_user_data = server; - - /* chain into the data_ready callback */ - server->data_ready = xchg(&sk->sk_data_ready, smb_data_ready); - - /* check if we have an old smbmount that uses seconds for the - serverzone */ - if (server->opt.serverzone > 12*60 || server->opt.serverzone < -12*60) - server->opt.serverzone /= 60; - - /* now that we have an established connection we can detect the server - type and enable bug workarounds */ - if (server->opt.protocol < SMB_PROTOCOL_LANMAN2) - install_ops(server->ops, &smb_ops_core); - else if (server->opt.protocol == SMB_PROTOCOL_LANMAN2) - install_ops(server->ops, &smb_ops_os2); - else if (server->opt.protocol == SMB_PROTOCOL_NT1 && - (server->opt.max_xmit < 0x1000) && - !(server->opt.capabilities & SMB_CAP_NT_SMBS)) { - /* FIXME: can we kill the WIN95 flag now? */ - server->mnt->flags |= SMB_MOUNT_WIN95; - VERBOSE("detected WIN95 server\n"); - install_ops(server->ops, &smb_ops_win95); - } else { - /* - * Samba has max_xmit 65535 - * NT4spX has max_xmit 4536 (or something like that) - * win2k has ... - */ - VERBOSE("detected NT1 (Samba, NT4/5) server\n"); - install_ops(server->ops, &smb_ops_winNT); - } - - /* FIXME: the win9x code wants to modify these ... (seek/trunc bug) */ - if (server->mnt->flags & SMB_MOUNT_OLDATTR) { - server->ops->getattr = smb_proc_getattr_core; - } else if (server->mnt->flags & SMB_MOUNT_DIRATTR) { - server->ops->getattr = smb_proc_getattr_ff; - } - - /* Decode server capabilities */ - if (server->opt.capabilities & SMB_CAP_LARGE_FILES) { - /* Should be ok to set this now, as no one can access the - mount until the connection has been established. */ - SB_of(server)->s_maxbytes = ~0ULL >> 1; - VERBOSE("LFS enabled\n"); - } - if (server->opt.capabilities & SMB_CAP_UNICODE) { - server->mnt->flags |= SMB_MOUNT_UNICODE; - VERBOSE("Unicode enabled\n"); - } else { - server->mnt->flags &= ~SMB_MOUNT_UNICODE; - } -#if 0 - /* flags we may test for other patches ... */ - if (server->opt.capabilities & SMB_CAP_LARGE_READX) { - VERBOSE("Large reads enabled\n"); - } - if (server->opt.capabilities & SMB_CAP_LARGE_WRITEX) { - 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, - pid_nr(server->conn_pid), server->opt.capabilities); - - /* FIXME: this really should be done by smbmount. */ - if (server->opt.max_xmit > SMB_MAX_PACKET_SIZE) { - 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); - - server->conn_complete++; - wake_up_interruptible_all(&server->conn_wq); - return error; - -out: - smb_unlock_server(server); - smbiod_wake_up(); - return error; - -out_putf: - fput(filp); - goto out; -} - -/* smb_setup_header: We completely set up the packet. You only have to - insert the command-specific fields */ - -__u8 * -smb_setup_header(struct smb_request *req, __u8 command, __u16 wct, __u16 bcc) -{ - __u32 xmit_len = SMB_HEADER_LEN + wct * sizeof(__u16) + bcc + 2; - __u8 *p = req->rq_header; - struct smb_sb_info *server = req->rq_server; - - p = smb_encode_smb_length(p, xmit_len - 4); - - *p++ = 0xff; - *p++ = 'S'; - *p++ = 'M'; - *p++ = 'B'; - *p++ = command; - - memset(p, '\0', 19); - p += 19; - p += 8; - - if (server->opt.protocol > SMB_PROTOCOL_CORE) { - int flags = SMB_FLAGS_CASELESS_PATHNAMES; - int flags2 = SMB_FLAGS2_LONG_PATH_COMPONENTS | - SMB_FLAGS2_EXTENDED_ATTRIBUTES; /* EA? not really ... */ - - *(req->rq_header + smb_flg) = flags; - if (server->mnt->flags & SMB_MOUNT_UNICODE) - flags2 |= SMB_FLAGS2_UNICODE_STRINGS; - WSET(req->rq_header, smb_flg2, flags2); - } - *p++ = wct; /* wct */ - p += 2 * wct; - WSET(p, 0, bcc); - - /* Include the header in the data to send */ - req->rq_iovlen = 1; - req->rq_iov[0].iov_base = req->rq_header; - req->rq_iov[0].iov_len = xmit_len - bcc; - - return req->rq_buffer; -} - -static void -smb_setup_bcc(struct smb_request *req, __u8 *p) -{ - u16 bcc = p - req->rq_buffer; - u8 *pbcc = req->rq_header + SMB_HEADER_LEN + 2*SMB_WCT(req->rq_header); - - WSET(pbcc, 0, bcc); - - smb_encode_smb_length(req->rq_header, SMB_HEADER_LEN + - 2*SMB_WCT(req->rq_header) - 2 + bcc); - - /* Include the "bytes" in the data to send */ - req->rq_iovlen = 2; - req->rq_iov[1].iov_base = req->rq_buffer; - req->rq_iov[1].iov_len = bcc; -} - -static int -smb_proc_seek(struct smb_sb_info *server, __u16 fileid, - __u16 mode, off_t offset) -{ - int result; - struct smb_request *req; - - result = -ENOMEM; - if (! (req = smb_alloc_request(server, 0))) - goto out; - - smb_setup_header(req, SMBlseek, 4, 0); - WSET(req->rq_header, smb_vwv0, fileid); - WSET(req->rq_header, smb_vwv1, mode); - DSET(req->rq_header, smb_vwv2, offset); - req->rq_flags |= SMB_REQ_NORETRY; - - result = smb_request_ok(req, SMBlseek, 2, 0); - if (result < 0) { - result = 0; - goto out_free; - } - - result = DVAL(req->rq_header, smb_vwv0); -out_free: - smb_rput(req); -out: - return result; -} - -static int -smb_proc_open(struct smb_sb_info *server, struct dentry *dentry, int wish) -{ - struct inode *ino = dentry->d_inode; - struct smb_inode_info *ei = SMB_I(ino); - int mode, read_write = 0x42, read_only = 0x40; - int res; - char *p; - struct smb_request *req; - - /* - * Attempt to open r/w, unless there are no write privileges. - */ - mode = read_write; - if (!(ino->i_mode & (S_IWUSR | S_IWGRP | S_IWOTH))) - mode = read_only; -#if 0 - /* FIXME: why is this code not in? below we fix it so that a caller - wanting RO doesn't get RW. smb_revalidate_inode does some - optimization based on access mode. tail -f needs it to be correct. - - We must open rw since we don't do the open if called a second time - with different 'wish'. Is that not supported by smb servers? */ - if (!(wish & (O_WRONLY | O_RDWR))) - mode = read_only; -#endif - - res = -ENOMEM; - if (! (req = smb_alloc_request(server, PAGE_SIZE))) - goto out; - - retry: - p = smb_setup_header(req, SMBopen, 2, 0); - WSET(req->rq_header, smb_vwv0, mode); - WSET(req->rq_header, smb_vwv1, aSYSTEM | aHIDDEN | aDIR); - res = smb_simple_encode_path(req, &p, dentry, NULL); - if (res < 0) - goto out_free; - smb_setup_bcc(req, p); - - res = smb_request_ok(req, SMBopen, 7, 0); - if (res != 0) { - if (mode == read_write && - (res == -EACCES || res == -ETXTBSY || res == -EROFS)) - { - VERBOSE("%s/%s R/W failed, error=%d, retrying R/O\n", - DENTRY_PATH(dentry), res); - mode = read_only; - req->rq_flags = 0; - goto retry; - } - goto out_free; - } - /* We should now have data in vwv[0..6]. */ - - ei->fileid = WVAL(req->rq_header, smb_vwv0); - ei->attr = WVAL(req->rq_header, smb_vwv1); - /* smb_vwv2 has mtime */ - /* smb_vwv4 has size */ - ei->access = (WVAL(req->rq_header, smb_vwv6) & SMB_ACCMASK); - ei->open = server->generation; - -out_free: - smb_rput(req); -out: - return res; -} - -/* - * Make sure the file is open, and check that the access - * is compatible with the desired access. - */ -int -smb_open(struct dentry *dentry, int wish) -{ - struct inode *inode = dentry->d_inode; - int result; - __u16 access; - - result = -ENOENT; - if (!inode) { - printk(KERN_ERR "smb_open: no inode for dentry %s/%s\n", - DENTRY_PATH(dentry)); - goto out; - } - - if (!smb_is_open(inode)) { - struct smb_sb_info *server = server_from_inode(inode); - result = 0; - if (!smb_is_open(inode)) - result = smb_proc_open(server, dentry, wish); - if (result) - goto out; - /* - * A successful open means the path is still valid ... - */ - smb_renew_times(dentry); - } - - /* - * Check whether the access is compatible with the desired mode. - */ - result = 0; - access = SMB_I(inode)->access; - if (access != wish && access != SMB_O_RDWR) { - PARANOIA("%s/%s access denied, access=%x, wish=%x\n", - DENTRY_PATH(dentry), access, wish); - result = -EACCES; - } -out: - return result; -} - -static int -smb_proc_close(struct smb_sb_info *server, __u16 fileid, __u32 mtime) -{ - struct smb_request *req; - int result = -ENOMEM; - - if (! (req = smb_alloc_request(server, 0))) - goto out; - - smb_setup_header(req, SMBclose, 3, 0); - WSET(req->rq_header, smb_vwv0, fileid); - DSET(req->rq_header, smb_vwv1, utc2local(server, mtime)); - req->rq_flags |= SMB_REQ_NORETRY; - result = smb_request_ok(req, SMBclose, 0, 0); - - smb_rput(req); -out: - return result; -} - -/* - * Win NT 4.0 has an apparent bug in that it fails to update the - * modify time when writing to a file. As a workaround, we update - * both modify and access time locally, and post the times to the - * server when closing the file. - */ -static int -smb_proc_close_inode(struct smb_sb_info *server, struct inode * ino) -{ - struct smb_inode_info *ei = SMB_I(ino); - int result = 0; - if (smb_is_open(ino)) - { - /* - * We clear the open flag in advance, in case another - * process observes the value while we block below. - */ - ei->open = 0; - - /* - * Kludge alert: SMB timestamps are accurate only to - * two seconds ... round the times to avoid needless - * cache invalidations! - */ - if (ino->i_mtime.tv_sec & 1) { - ino->i_mtime.tv_sec--; - ino->i_mtime.tv_nsec = 0; - } - if (ino->i_atime.tv_sec & 1) { - ino->i_atime.tv_sec--; - ino->i_atime.tv_nsec = 0; - } - /* - * If the file is open with write permissions, - * update the time stamps to sync mtime and atime. - */ - if ((server->opt.capabilities & SMB_CAP_UNIX) == 0 && - (server->opt.protocol >= SMB_PROTOCOL_LANMAN2) && - !(ei->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, ei->fileid, ino->i_mtime.tv_sec); - /* - * Force a revalidation after closing ... some servers - * don't post the size until the file has been closed. - */ - if (server->opt.protocol < SMB_PROTOCOL_NT1) - ei->oldmtime = 0; - ei->closed = jiffies; - } - return result; -} - -int -smb_close(struct inode *ino) -{ - int result = 0; - - if (smb_is_open(ino)) { - struct smb_sb_info *server = server_from_inode(ino); - result = smb_proc_close_inode(server, ino); - } - return result; -} - -/* - * This is used to close a file following a failed instantiate. - * Since we don't have an inode, we can't use any of the above. - */ -int -smb_close_fileid(struct dentry *dentry, __u16 fileid) -{ - struct smb_sb_info *server = server_from_dentry(dentry); - int result; - - result = smb_proc_close(server, fileid, get_seconds()); - return result; -} - -/* In smb_proc_read and smb_proc_write we do not retry, because the - file-id would not be valid after a reconnection. */ - -static void -smb_proc_read_data(struct smb_request *req) -{ - req->rq_iov[0].iov_base = req->rq_buffer; - req->rq_iov[0].iov_len = 3; - - req->rq_iov[1].iov_base = req->rq_page; - req->rq_iov[1].iov_len = req->rq_rsize; - req->rq_iovlen = 2; - - req->rq_rlen = smb_len(req->rq_header) + 4 - req->rq_bytes_recvd; -} - -static int -smb_proc_read(struct inode *inode, loff_t offset, int count, char *data) -{ - struct smb_sb_info *server = server_from_inode(inode); - __u16 returned_count, data_len; - unsigned char *buf; - int result; - struct smb_request *req; - u8 rbuf[4]; - - result = -ENOMEM; - if (! (req = smb_alloc_request(server, 0))) - goto out; - - smb_setup_header(req, SMBread, 5, 0); - buf = req->rq_header; - WSET(buf, smb_vwv0, SMB_I(inode)->fileid); - WSET(buf, smb_vwv1, count); - DSET(buf, smb_vwv2, offset); - WSET(buf, smb_vwv4, 0); - - req->rq_page = data; - req->rq_rsize = count; - req->rq_callback = smb_proc_read_data; - req->rq_buffer = rbuf; - req->rq_flags |= SMB_REQ_NORETRY | SMB_REQ_STATIC; - - result = smb_request_ok(req, SMBread, 5, -1); - if (result < 0) - goto out_free; - returned_count = WVAL(req->rq_header, smb_vwv0); - - data_len = WVAL(rbuf, 1); - - if (returned_count != data_len) { - printk(KERN_NOTICE "smb_proc_read: returned != data_len\n"); - printk(KERN_NOTICE "smb_proc_read: ret_c=%d, data_len=%d\n", - returned_count, data_len); - } - result = data_len; - -out_free: - smb_rput(req); -out: - VERBOSE("ino=%ld, fileid=%d, count=%d, result=%d\n", - inode->i_ino, SMB_I(inode)->fileid, count, result); - return result; -} - -static int -smb_proc_write(struct inode *inode, loff_t offset, int count, const char *data) -{ - struct smb_sb_info *server = server_from_inode(inode); - int result; - u16 fileid = SMB_I(inode)->fileid; - u8 buf[4]; - struct smb_request *req; - - result = -ENOMEM; - if (! (req = smb_alloc_request(server, 0))) - goto out; - - VERBOSE("ino=%ld, fileid=%d, count=%d@%Ld\n", - inode->i_ino, fileid, count, offset); - - smb_setup_header(req, SMBwrite, 5, count + 3); - WSET(req->rq_header, smb_vwv0, fileid); - WSET(req->rq_header, smb_vwv1, count); - DSET(req->rq_header, smb_vwv2, offset); - WSET(req->rq_header, smb_vwv4, 0); - - buf[0] = 1; - WSET(buf, 1, count); /* yes, again ... */ - req->rq_iov[1].iov_base = buf; - req->rq_iov[1].iov_len = 3; - req->rq_iov[2].iov_base = (char *) data; - req->rq_iov[2].iov_len = count; - req->rq_iovlen = 3; - req->rq_flags |= SMB_REQ_NORETRY; - - result = smb_request_ok(req, SMBwrite, 1, 0); - if (result >= 0) - result = WVAL(req->rq_header, smb_vwv0); - - smb_rput(req); -out: - return result; -} - -/* - * In smb_proc_readX and smb_proc_writeX we do not retry, because the - * file-id would not be valid after a reconnection. - */ - -#define SMB_READX_MAX_PAD 64 -static void -smb_proc_readX_data(struct smb_request *req) -{ - /* header length, excluding the netbios length (-4) */ - int hdrlen = SMB_HEADER_LEN + req->rq_resp_wct*2 - 2; - int data_off = WVAL(req->rq_header, smb_vwv6); - - /* - * Some genius made the padding to the data bytes arbitrary. - * So we must first calculate the amount of padding used by the server. - */ - data_off -= hdrlen; - if (data_off > SMB_READX_MAX_PAD || data_off < 0) { - PARANOIA("offset is larger than SMB_READX_MAX_PAD or negative!\n"); - PARANOIA("%d > %d || %d < 0\n", data_off, SMB_READX_MAX_PAD, data_off); - req->rq_rlen = req->rq_bufsize + 1; - return; - } - req->rq_iov[0].iov_base = req->rq_buffer; - req->rq_iov[0].iov_len = data_off; - - req->rq_iov[1].iov_base = req->rq_page; - req->rq_iov[1].iov_len = req->rq_rsize; - req->rq_iovlen = 2; - - req->rq_rlen = smb_len(req->rq_header) + 4 - req->rq_bytes_recvd; -} - -static int -smb_proc_readX(struct inode *inode, loff_t offset, int count, char *data) -{ - struct smb_sb_info *server = server_from_inode(inode); - unsigned char *buf; - int result; - struct smb_request *req; - static char pad[SMB_READX_MAX_PAD]; - - result = -ENOMEM; - if (! (req = smb_alloc_request(server, 0))) - goto out; - - smb_setup_header(req, SMBreadX, 12, 0); - buf = req->rq_header; - WSET(buf, smb_vwv0, 0x00ff); - WSET(buf, smb_vwv1, 0); - WSET(buf, smb_vwv2, SMB_I(inode)->fileid); - DSET(buf, smb_vwv3, (u32)offset); /* low 32 bits */ - WSET(buf, smb_vwv5, count); - WSET(buf, smb_vwv6, 0); - DSET(buf, smb_vwv7, 0); - WSET(buf, smb_vwv9, 0); - DSET(buf, smb_vwv10, (u32)(offset >> 32)); /* high 32 bits */ - WSET(buf, smb_vwv11, 0); - - req->rq_page = data; - req->rq_rsize = count; - req->rq_callback = smb_proc_readX_data; - req->rq_buffer = pad; - req->rq_bufsize = SMB_READX_MAX_PAD; - req->rq_flags |= SMB_REQ_STATIC | SMB_REQ_NORETRY; - - result = smb_request_ok(req, SMBreadX, 12, -1); - if (result < 0) - goto out_free; - result = WVAL(req->rq_header, smb_vwv5); - -out_free: - smb_rput(req); -out: - VERBOSE("ino=%ld, fileid=%d, count=%d, result=%d\n", - inode->i_ino, SMB_I(inode)->fileid, count, result); - return result; -} - -static int -smb_proc_writeX(struct inode *inode, loff_t offset, int count, const char *data) -{ - struct smb_sb_info *server = server_from_inode(inode); - int result; - u8 *p; - static u8 pad[4]; - struct smb_request *req; - - result = -ENOMEM; - if (! (req = smb_alloc_request(server, 0))) - goto out; - - VERBOSE("ino=%ld, fileid=%d, count=%d@%Ld\n", - inode->i_ino, SMB_I(inode)->fileid, count, offset); - - p = smb_setup_header(req, SMBwriteX, 14, count + 1); - WSET(req->rq_header, smb_vwv0, 0x00ff); - WSET(req->rq_header, smb_vwv1, 0); - WSET(req->rq_header, smb_vwv2, SMB_I(inode)->fileid); - DSET(req->rq_header, smb_vwv3, (u32)offset); /* low 32 bits */ - DSET(req->rq_header, smb_vwv5, 0); - WSET(req->rq_header, smb_vwv7, 0); /* write mode */ - WSET(req->rq_header, smb_vwv8, 0); - WSET(req->rq_header, smb_vwv9, 0); - WSET(req->rq_header, smb_vwv10, count); /* data length */ - WSET(req->rq_header, smb_vwv11, smb_vwv12 + 2 + 1); - DSET(req->rq_header, smb_vwv12, (u32)(offset >> 32)); - - req->rq_iov[1].iov_base = pad; - req->rq_iov[1].iov_len = 1; - req->rq_iov[2].iov_base = (char *) data; - req->rq_iov[2].iov_len = count; - req->rq_iovlen = 3; - req->rq_flags |= SMB_REQ_NORETRY; - - result = smb_request_ok(req, SMBwriteX, 6, 0); - if (result >= 0) - result = WVAL(req->rq_header, smb_vwv2); - - smb_rput(req); -out: - return result; -} - -int -smb_proc_create(struct dentry *dentry, __u16 attr, time_t ctime, __u16 *fileid) -{ - struct smb_sb_info *server = server_from_dentry(dentry); - char *p; - int result; - struct smb_request *req; - - result = -ENOMEM; - if (! (req = smb_alloc_request(server, PAGE_SIZE))) - goto out; - - p = smb_setup_header(req, SMBcreate, 3, 0); - WSET(req->rq_header, smb_vwv0, attr); - DSET(req->rq_header, smb_vwv1, utc2local(server, ctime)); - result = smb_simple_encode_path(req, &p, dentry, NULL); - if (result < 0) - goto out_free; - smb_setup_bcc(req, p); - - result = smb_request_ok(req, SMBcreate, 1, 0); - if (result < 0) - goto out_free; - - *fileid = WVAL(req->rq_header, smb_vwv0); - result = 0; - -out_free: - smb_rput(req); -out: - return result; -} - -int -smb_proc_mv(struct dentry *old_dentry, struct dentry *new_dentry) -{ - struct smb_sb_info *server = server_from_dentry(old_dentry); - char *p; - int result; - struct smb_request *req; - - result = -ENOMEM; - if (! (req = smb_alloc_request(server, PAGE_SIZE))) - goto out; - - p = smb_setup_header(req, SMBmv, 1, 0); - WSET(req->rq_header, smb_vwv0, aSYSTEM | aHIDDEN | aDIR); - result = smb_simple_encode_path(req, &p, old_dentry, NULL); - if (result < 0) - goto out_free; - result = smb_simple_encode_path(req, &p, new_dentry, NULL); - if (result < 0) - goto out_free; - smb_setup_bcc(req, p); - - if ((result = smb_request_ok(req, SMBmv, 0, 0)) < 0) - goto out_free; - result = 0; - -out_free: - smb_rput(req); -out: - return result; -} - -/* - * Code common to mkdir and rmdir. - */ -static int -smb_proc_generic_command(struct dentry *dentry, __u8 command) -{ - struct smb_sb_info *server = server_from_dentry(dentry); - char *p; - int result; - struct smb_request *req; - - result = -ENOMEM; - if (! (req = smb_alloc_request(server, PAGE_SIZE))) - goto out; - - p = smb_setup_header(req, command, 0, 0); - result = smb_simple_encode_path(req, &p, dentry, NULL); - if (result < 0) - goto out_free; - smb_setup_bcc(req, p); - - result = smb_request_ok(req, command, 0, 0); - if (result < 0) - goto out_free; - result = 0; - -out_free: - smb_rput(req); -out: - return result; -} - -int -smb_proc_mkdir(struct dentry *dentry) -{ - return smb_proc_generic_command(dentry, SMBmkdir); -} - -int -smb_proc_rmdir(struct dentry *dentry) -{ - return smb_proc_generic_command(dentry, SMBrmdir); -} - -#if SMBFS_POSIX_UNLINK -/* - * Removes readonly attribute from a file. Used by unlink to give posix - * semantics. - */ -static int -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); - smb_finish_dirent(server, &fattr); - if (result < 0) - return result; - - /* if RONLY attribute is set, remove it */ - if (fattr.attr & aRONLY) { /* read only attribute is set */ - fattr.attr &= ~aRONLY; - result = smb_proc_setattr_core(server, dentry, fattr.attr); - } - return result; -} -#endif - -int -smb_proc_unlink(struct dentry *dentry) -{ - struct smb_sb_info *server = server_from_dentry(dentry); - int flag = 0; - char *p; - int result; - struct smb_request *req; - - result = -ENOMEM; - if (! (req = smb_alloc_request(server, PAGE_SIZE))) - goto out; - - retry: - p = smb_setup_header(req, SMBunlink, 1, 0); - WSET(req->rq_header, smb_vwv0, aSYSTEM | aHIDDEN); - result = smb_simple_encode_path(req, &p, dentry, NULL); - if (result < 0) - goto out_free; - smb_setup_bcc(req, p); - - if ((result = smb_request_ok(req, SMBunlink, 0, 0)) < 0) { -#if SMBFS_POSIX_UNLINK - if (result == -EACCES && !flag) { - /* Posix semantics is for the read-only state - of a file to be ignored in unlink(). In the - SMB world a unlink() is refused on a - read-only file. To make things easier for - unix users we try to override the files - permission if the unlink fails with the - right error. - This introduces a race condition that could - lead to a file being written by someone who - shouldn't have access, but as far as I can - tell that is unavoidable */ - - /* remove RONLY attribute and try again */ - result = smb_set_rw(dentry,server); - if (result == 0) { - flag = 1; - req->rq_flags = 0; - goto retry; - } - } -#endif - goto out_free; - } - result = 0; - -out_free: - smb_rput(req); -out: - return result; -} - -int -smb_proc_flush(struct smb_sb_info *server, __u16 fileid) -{ - int result; - struct smb_request *req; - - result = -ENOMEM; - if (! (req = smb_alloc_request(server, 0))) - goto out; - - smb_setup_header(req, SMBflush, 1, 0); - WSET(req->rq_header, smb_vwv0, fileid); - req->rq_flags |= SMB_REQ_NORETRY; - result = smb_request_ok(req, SMBflush, 0, 0); - - smb_rput(req); -out: - return result; -} - -static int -smb_proc_trunc32(struct inode *inode, loff_t length) -{ - /* - * Writing 0bytes is old-SMB magic for truncating files. - * MAX_NON_LFS should prevent this from being called with a too - * large offset. - */ - return smb_proc_write(inode, length, 0, NULL); -} - -static int -smb_proc_trunc64(struct inode *inode, loff_t length) -{ - struct smb_sb_info *server = server_from_inode(inode); - int result; - char *param; - char *data; - struct smb_request *req; - - result = -ENOMEM; - if (! (req = smb_alloc_request(server, 14))) - goto out; - - param = req->rq_buffer; - data = req->rq_buffer + 6; - - /* FIXME: must we also set allocation size? winNT seems to do that */ - WSET(param, 0, SMB_I(inode)->fileid); - WSET(param, 2, SMB_SET_FILE_END_OF_FILE_INFO); - WSET(param, 4, 0); - LSET(data, 0, length); - - req->rq_trans2_command = TRANSACT2_SETFILEINFO; - req->rq_ldata = 8; - req->rq_data = data; - req->rq_lparm = 6; - req->rq_parm = param; - req->rq_flags |= SMB_REQ_NORETRY; - result = smb_add_request(req); - if (result < 0) - goto out_free; - - result = 0; - if (req->rq_rcls != 0) - result = smb_errno(req); - -out_free: - smb_rput(req); -out: - return result; -} - -static int -smb_proc_trunc95(struct inode *inode, loff_t length) -{ - struct smb_sb_info *server = server_from_inode(inode); - int result = smb_proc_trunc32(inode, length); - - /* - * win9x doesn't appear to update the size immediately. - * It will return the old file size after the truncate, - * confusing smbfs. So we force an update. - * - * FIXME: is this still necessary? - */ - smb_proc_flush(server, SMB_I(inode)->fileid); - return result; -} - -static void -smb_init_dirent(struct smb_sb_info *server, struct smb_fattr *fattr) -{ - memset(fattr, 0, sizeof(*fattr)); - - fattr->f_nlink = 1; - fattr->f_uid = server->mnt->uid; - fattr->f_gid = server->mnt->gid; - 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) { - 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); - return; -} - -void -smb_init_root_dirent(struct smb_sb_info *server, struct smb_fattr *fattr, - struct super_block *sb) -{ - smb_init_dirent(server, fattr); - fattr->attr = aDIR; - fattr->f_ino = 2; /* traditional root inode number */ - fattr->f_mtime = current_fs_time(sb); - smb_finish_dirent(server, fattr); -} - -/* - * Decode a dirent for old protocols - * - * qname is filled with the decoded, and possibly translated, name. - * fattr receives decoded attributes - * - * Bugs Noted: - * (1) Pathworks servers may pad the name with extra spaces. - */ -static char * -smb_decode_short_dirent(struct smb_sb_info *server, char *p, - struct qstr *qname, struct smb_fattr *fattr, - unsigned char *name_buf) -{ - int len; - - /* - * SMB doesn't have a concept of inode numbers ... - */ - smb_init_dirent(server, fattr); - fattr->f_ino = 0; /* FIXME: do we need this? */ - - p += SMB_STATUS_SIZE; /* reserved (search_status) */ - fattr->attr = *p; - fattr->f_mtime.tv_sec = date_dos2unix(server, WVAL(p, 3), WVAL(p, 1)); - fattr->f_mtime.tv_nsec = 0; - fattr->f_size = DVAL(p, 5); - fattr->f_ctime = fattr->f_mtime; - fattr->f_atime = fattr->f_mtime; - qname->name = p + 9; - len = strnlen(qname->name, 12); - - /* - * Trim trailing blanks for Pathworks servers - */ - while (len > 2 && qname->name[len-1] == ' ') - len--; - - smb_finish_dirent(server, fattr); - -#if 0 - /* FIXME: These only work for ascii chars, and recent smbmount doesn't - allow the flag to be set anyway. It kills const. Remove? */ - switch (server->opt.case_handling) { - case SMB_CASE_UPPER: - str_upper(entry->name, len); - break; - case SMB_CASE_LOWER: - str_lower(entry->name, len); - break; - default: - break; - } -#endif - - qname->len = 0; - len = server->ops->convert(name_buf, SMB_MAXNAMELEN, - qname->name, len, - server->remote_nls, server->local_nls); - if (len > 0) { - qname->len = len; - qname->name = name_buf; - DEBUG1("len=%d, name=%.*s\n",qname->len,qname->len,qname->name); - } - - return p + 22; -} - -/* - * This routine is used to read in directory entries from the network. - * Note that it is for short directory name seeks, i.e.: protocol < - * SMB_PROTOCOL_LANMAN2 - */ -static int -smb_proc_readdir_short(struct file *filp, void *dirent, filldir_t filldir, - struct smb_cache_control *ctl) -{ - struct dentry *dir = filp->f_path.dentry; - struct smb_sb_info *server = server_from_dentry(dir); - struct qstr qname; - struct smb_fattr fattr; - char *p; - int result; - int i, first, entries_seen, entries; - int entries_asked = (server->opt.max_xmit - 100) / SMB_DIRINFO_SIZE; - __u16 bcc; - __u16 count; - char status[SMB_STATUS_SIZE]; - static struct qstr mask = { - .name = "*.*", - .len = 3, - }; - unsigned char *last_status; - struct smb_request *req; - unsigned char *name_buf; - - VERBOSE("%s/%s\n", DENTRY_PATH(dir)); - - lock_kernel(); - - result = -ENOMEM; - if (! (name_buf = kmalloc(SMB_MAXNAMELEN, GFP_KERNEL))) - goto out; - - first = 1; - entries = 0; - entries_seen = 2; /* implicit . and .. */ - - result = -ENOMEM; - if (! (req = smb_alloc_request(server, server->opt.max_xmit))) - goto out_name; - - while (1) { - p = smb_setup_header(req, SMBsearch, 2, 0); - WSET(req->rq_header, smb_vwv0, entries_asked); - WSET(req->rq_header, smb_vwv1, aDIR); - if (first == 1) { - result = smb_simple_encode_path(req, &p, dir, &mask); - if (result < 0) - goto out_free; - if (p + 3 > (char *)req->rq_buffer + req->rq_bufsize) { - result = -ENAMETOOLONG; - goto out_free; - } - *p++ = 5; - WSET(p, 0, 0); - p += 2; - first = 0; - } else { - if (p + 5 + SMB_STATUS_SIZE > - (char *)req->rq_buffer + req->rq_bufsize) { - result = -ENAMETOOLONG; - goto out_free; - } - - *p++ = 4; - *p++ = 0; - *p++ = 5; - WSET(p, 0, SMB_STATUS_SIZE); - p += 2; - memcpy(p, status, SMB_STATUS_SIZE); - p += SMB_STATUS_SIZE; - } - - smb_setup_bcc(req, p); - - result = smb_request_ok(req, SMBsearch, 1, -1); - if (result < 0) { - if ((req->rq_rcls == ERRDOS) && - (req->rq_err == ERRnofiles)) - break; - goto out_free; - } - count = WVAL(req->rq_header, smb_vwv0); - if (count <= 0) - break; - - result = -EIO; - bcc = smb_bcc(req->rq_header); - if (bcc != count * SMB_DIRINFO_SIZE + 3) - goto out_free; - p = req->rq_buffer + 3; - - - /* Make sure the response fits in the buffer. Fixed sized - entries means we don't have to check in the decode loop. */ - - last_status = req->rq_buffer + 3 + (count-1) * SMB_DIRINFO_SIZE; - - if (last_status + SMB_DIRINFO_SIZE >= - req->rq_buffer + req->rq_bufsize) { - printk(KERN_ERR "smb_proc_readdir_short: " - "last dir entry outside buffer! " - "%d@%p %d@%p\n", SMB_DIRINFO_SIZE, last_status, - req->rq_bufsize, req->rq_buffer); - goto out_free; - } - - /* Read the last entry into the status field. */ - memcpy(status, last_status, SMB_STATUS_SIZE); - - - /* Now we are ready to parse smb directory entries. */ - - for (i = 0; i < count; i++) { - p = smb_decode_short_dirent(server, p, - &qname, &fattr, name_buf); - if (qname.len == 0) - continue; - - if (entries_seen == 2 && qname.name[0] == '.') { - if (qname.len == 1) - continue; - if (qname.name[1] == '.' && qname.len == 2) - continue; - } - if (!smb_fill_cache(filp, dirent, filldir, ctl, - &qname, &fattr)) - ; /* stop reading? */ - entries_seen++; - } - } - result = entries; - -out_free: - smb_rput(req); -out_name: - kfree(name_buf); -out: - unlock_kernel(); - return result; -} - -static void smb_decode_unix_basic(struct smb_fattr *fattr, struct smb_sb_info *server, char *p) -{ - u64 size, disk_bytes; - - /* FIXME: verify nls support. all is sent as utf8? */ - - 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 */ - - size = LVAL(p, 0); - disk_bytes = LVAL(p, 8); - - /* - * Some samba versions round up on-disk byte usage - * to 1MB boundaries, making it useless. When seeing - * that, use the size instead. - */ - if (!(disk_bytes & 0xfffff)) - disk_bytes = size+511; - - fattr->f_size = size; - fattr->f_blocks = disk_bytes >> 9; - fattr->f_ctime = smb_ntutc2unixutc(LVAL(p, 16)); - fattr->f_atime = smb_ntutc2unixutc(LVAL(p, 24)); - fattr->f_mtime = smb_ntutc2unixutc(LVAL(p, 32)); - - if (server->mnt->flags & SMB_MOUNT_UID) - fattr->f_uid = server->mnt->uid; - else - fattr->f_uid = LVAL(p, 40); - - if (server->mnt->flags & SMB_MOUNT_GID) - fattr->f_gid = server->mnt->gid; - else - 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)) { - __u64 major = LVAL(p, 60); - __u64 minor = LVAL(p, 68); - - fattr->f_rdev = MKDEV(major & 0xffffffff, minor & 0xffffffff); - if (MAJOR(fattr->f_rdev) != (major & 0xffffffff) || - MINOR(fattr->f_rdev) != (minor & 0xffffffff)) - fattr->f_rdev = 0; - } - - fattr->f_mode |= LVAL(p, 84); - - if ( (server->mnt->flags & SMB_MOUNT_DMODE) && - (S_ISDIR(fattr->f_mode)) ) - fattr->f_mode = (server->mnt->dir_mode & S_IRWXUGO) | S_IFDIR; - else if ( (server->mnt->flags & SMB_MOUNT_FMODE) && - !(S_ISDIR(fattr->f_mode)) ) - fattr->f_mode = (server->mnt->file_mode & S_IRWXUGO) | - (fattr->f_mode & S_IFMT); - -} - -/* - * Interpret a long filename structure using the specified info level: - * level 1 for anything below NT1 protocol - * level 260 for NT1 protocol - * - * qname is filled with the decoded, and possibly translated, name - * fattr receives decoded attributes. - * - * Bugs Noted: - * (1) Win NT 4.0 appends a null byte to names and counts it in the length! - */ -static char * -smb_decode_long_dirent(struct smb_sb_info *server, char *p, int level, - struct qstr *qname, struct smb_fattr *fattr, - unsigned char *name_buf) -{ - char *result; - unsigned int len = 0; - int n; - __u16 date, time; - int unicode = (server->mnt->flags & SMB_MOUNT_UNICODE); - - /* - * SMB doesn't have a concept of inode numbers ... - */ - smb_init_dirent(server, fattr); - fattr->f_ino = 0; /* FIXME: do we need this? */ - - switch (level) { - case 1: - len = *((unsigned char *) p + 22); - qname->name = p + 23; - result = p + 24 + len; - - date = WVAL(p, 0); - time = WVAL(p, 2); - fattr->f_ctime.tv_sec = date_dos2unix(server, date, time); - fattr->f_ctime.tv_nsec = 0; - - date = WVAL(p, 4); - time = WVAL(p, 6); - fattr->f_atime.tv_sec = date_dos2unix(server, date, time); - fattr->f_atime.tv_nsec = 0; - - date = WVAL(p, 8); - time = WVAL(p, 10); - fattr->f_mtime.tv_sec = date_dos2unix(server, date, time); - fattr->f_mtime.tv_nsec = 0; - fattr->f_size = DVAL(p, 12); - /* ULONG allocation size */ - fattr->attr = WVAL(p, 20); - - VERBOSE("info 1 at %p, len=%d, name=%.*s\n", - p, len, len, qname->name); - break; - case 260: - result = p + WVAL(p, 0); - len = DVAL(p, 60); - if (len > 255) len = 255; - /* NT4 null terminates, unless we are using unicode ... */ - qname->name = p + 94; - if (!unicode && len && qname->name[len-1] == '\0') - len--; - - fattr->f_ctime = smb_ntutc2unixutc(LVAL(p, 8)); - fattr->f_atime = smb_ntutc2unixutc(LVAL(p, 16)); - fattr->f_mtime = smb_ntutc2unixutc(LVAL(p, 24)); - /* change time (32) */ - fattr->f_size = LVAL(p, 40); - /* alloc size (48) */ - fattr->attr = DVAL(p, 56); - - 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, server, 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); - goto out; - } - - smb_finish_dirent(server, fattr); - -#if 0 - /* FIXME: These only work for ascii chars, and recent smbmount doesn't - allow the flag to be set anyway. Remove? */ - switch (server->opt.case_handling) { - case SMB_CASE_UPPER: - str_upper(qname->name, len); - break; - case SMB_CASE_LOWER: - str_lower(qname->name, len); - break; - default: - break; - } -#endif - - qname->len = 0; - n = server->ops->convert(name_buf, SMB_MAXNAMELEN, - qname->name, len, - server->remote_nls, server->local_nls); - if (n > 0) { - qname->len = n; - qname->name = name_buf; - } - -out: - return result; -} - -/* findfirst/findnext flags */ -#define SMB_CLOSE_AFTER_FIRST (1<<0) -#define SMB_CLOSE_IF_END (1<<1) -#define SMB_REQUIRE_RESUME_KEY (1<<2) -#define SMB_CONTINUE_BIT (1<<3) - -/* - * Note: samba-2.0.7 (at least) has a very similar routine, cli_list, in - * source/libsmb/clilist.c. When looking for smb bugs in the readdir code, - * go there for advise. - * - * Bugs Noted: - * (1) When using Info Level 1 Win NT 4.0 truncates directory listings - * for certain patterns of names and/or lengths. The breakage pattern - * is completely reproducible and can be toggled by the creation of a - * single file. (E.g. echo hi >foo breaks, rm -f foo works.) - */ -static int -smb_proc_readdir_long(struct file *filp, void *dirent, filldir_t filldir, - struct smb_cache_control *ctl) -{ - struct dentry *dir = filp->f_path.dentry; - struct smb_sb_info *server = server_from_dentry(dir); - struct qstr qname; - struct smb_fattr fattr; - - unsigned char *p, *lastname; - char *mask, *param; - __u16 command; - int first, entries_seen; - - /* Both NT and OS/2 accept info level 1 (but see note below). */ - int info_level = 260; - const int max_matches = 512; - - unsigned int ff_searchcount = 0; - unsigned int ff_eos = 0; - unsigned int ff_lastname = 0; - unsigned int ff_dir_handle = 0; - unsigned int loop_count = 0; - unsigned int mask_len, i; - int result; - struct smb_request *req; - unsigned char *name_buf; - static struct qstr star = { - .name = "*", - .len = 1, - }; - - lock_kernel(); - - /* - * We always prefer unix style. Use info level 1 for older - * servers that don't do 260. - */ - 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; - if (! (name_buf = kmalloc(SMB_MAXNAMELEN+2, GFP_KERNEL))) - goto out; - if (! (req = smb_alloc_request(server, server->opt.max_xmit))) - goto out_name; - param = req->rq_buffer; - - /* - * Encode the initial path - */ - mask = param + 12; - - result = smb_encode_path(server, mask, SMB_MAXPATHLEN+1, dir, &star); - if (result <= 0) - goto out_free; - mask_len = result - 1; /* mask_len is strlen, not #bytes */ - result = 0; - first = 1; - VERBOSE("starting mask_len=%d, mask=%s\n", mask_len, mask); - - entries_seen = 2; - ff_eos = 0; - - while (ff_eos == 0) { - loop_count += 1; - if (loop_count > 10) { - printk(KERN_WARNING "smb_proc_readdir_long: " - "Looping in FIND_NEXT??\n"); - result = -EIO; - break; - } - - if (first != 0) { - command = TRANSACT2_FINDFIRST; - WSET(param, 0, aSYSTEM | aHIDDEN | aDIR); - WSET(param, 2, max_matches); /* max count */ - WSET(param, 4, SMB_CLOSE_IF_END); - WSET(param, 6, info_level); - DSET(param, 8, 0); - } else { - command = TRANSACT2_FINDNEXT; - - VERBOSE("handle=0x%X, lastname=%d, mask=%.*s\n", - ff_dir_handle, ff_lastname, mask_len, mask); - - WSET(param, 0, ff_dir_handle); /* search handle */ - WSET(param, 2, max_matches); /* max count */ - WSET(param, 4, info_level); - DSET(param, 6, 0); - WSET(param, 10, SMB_CONTINUE_BIT|SMB_CLOSE_IF_END); - } - - req->rq_trans2_command = command; - req->rq_ldata = 0; - req->rq_data = NULL; - req->rq_lparm = 12 + mask_len + 1; - req->rq_parm = param; - req->rq_flags = 0; - result = smb_add_request(req); - if (result < 0) { - PARANOIA("error=%d, breaking\n", result); - break; - } - - if (req->rq_rcls == ERRSRV && req->rq_err == ERRerror) { - /* a damn Win95 bug - sometimes it clags if you - ask it too fast */ - schedule_timeout_interruptible(msecs_to_jiffies(200)); - continue; - } - - if (req->rq_rcls != 0) { - result = smb_errno(req); - PARANOIA("name=%s, result=%d, rcls=%d, err=%d\n", - mask, result, req->rq_rcls, req->rq_err); - break; - } - - /* parse out some important return info */ - if (first != 0) { - ff_dir_handle = WVAL(req->rq_parm, 0); - ff_searchcount = WVAL(req->rq_parm, 2); - ff_eos = WVAL(req->rq_parm, 4); - ff_lastname = WVAL(req->rq_parm, 8); - } else { - ff_searchcount = WVAL(req->rq_parm, 0); - ff_eos = WVAL(req->rq_parm, 2); - ff_lastname = WVAL(req->rq_parm, 6); - } - - if (ff_searchcount == 0) - break; - - /* Now we are ready to parse smb directory entries. */ - - /* point to the data bytes */ - p = req->rq_data; - for (i = 0; i < ff_searchcount; i++) { - /* make sure we stay within the buffer */ - if (p >= req->rq_data + req->rq_ldata) { - printk(KERN_ERR "smb_proc_readdir_long: " - "dirent pointer outside buffer! " - "%p %d@%p\n", - p, req->rq_ldata, req->rq_data); - result = -EIO; /* always a comm. error? */ - goto out_free; - } - - p = smb_decode_long_dirent(server, p, info_level, - &qname, &fattr, name_buf); - - /* ignore . and .. from the server */ - if (entries_seen == 2 && qname.name[0] == '.') { - if (qname.len == 1) - continue; - if (qname.name[1] == '.' && qname.len == 2) - continue; - } - - if (!smb_fill_cache(filp, dirent, filldir, ctl, - &qname, &fattr)) - ; /* stop reading? */ - entries_seen++; - } - - VERBOSE("received %d entries, eos=%d\n", ff_searchcount,ff_eos); - - /* - * We might need the lastname for continuations. - * - * Note that some servers (win95?) point to the filename and - * others (NT4, Samba using NT1) to the dir entry. We assume - * 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. - * 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 (info_level != SMB_FIND_FILE_UNIX && - ff_lastname > 0 && ff_lastname < req->rq_ldata) { - lastname = req->rq_data + ff_lastname; - - switch (info_level) { - case 260: - mask_len = req->rq_ldata - ff_lastname; - break; - case 1: - /* lastname points to a length byte */ - mask_len = *lastname++; - if (ff_lastname + 1 + mask_len > req->rq_ldata) - mask_len = req->rq_ldata - ff_lastname - 1; - break; - } - - /* - * Update the mask string for the next message. - */ - if (mask_len > 255) - mask_len = 255; - if (mask_len) - strncpy(mask, lastname, mask_len); - } - mask_len = strnlen(mask, mask_len); - VERBOSE("new mask, len=%d@%d of %d, mask=%.*s\n", - mask_len, ff_lastname, req->rq_ldata, mask_len, mask); - - first = 0; - loop_count = 0; - } - -out_free: - smb_rput(req); -out_name: - kfree(name_buf); -out: - unlock_kernel(); - return result; -} - -/* - * This version uses the trans2 TRANSACT2_FINDFIRST message - * to get the attribute data. - * - * Bugs Noted: - */ -static int -smb_proc_getattr_ff(struct smb_sb_info *server, struct dentry *dentry, - struct smb_fattr *fattr) -{ - char *param, *mask; - __u16 date, time; - int mask_len, result; - struct smb_request *req; - - result = -ENOMEM; - if (! (req = smb_alloc_request(server, PAGE_SIZE))) - goto out; - param = req->rq_buffer; - mask = param + 12; - - mask_len = smb_encode_path(server, mask, SMB_MAXPATHLEN+1, dentry,NULL); - if (mask_len < 0) { - result = mask_len; - goto out_free; - } - VERBOSE("name=%s, len=%d\n", mask, mask_len); - WSET(param, 0, aSYSTEM | aHIDDEN | aDIR); - WSET(param, 2, 1); /* max count */ - WSET(param, 4, 1); /* close after this call */ - WSET(param, 6, 1); /* info_level */ - DSET(param, 8, 0); - - req->rq_trans2_command = TRANSACT2_FINDFIRST; - req->rq_ldata = 0; - req->rq_data = NULL; - req->rq_lparm = 12 + mask_len; - req->rq_parm = param; - req->rq_flags = 0; - result = smb_add_request(req); - if (result < 0) - goto out_free; - if (req->rq_rcls != 0) { - result = smb_errno(req); -#ifdef SMBFS_PARANOIA - if (result != -ENOENT) - PARANOIA("error for %s, rcls=%d, err=%d\n", - mask, req->rq_rcls, req->rq_err); -#endif - goto out_free; - } - /* Make sure we got enough data ... */ - result = -EINVAL; - if (req->rq_ldata < 22 || WVAL(req->rq_parm, 2) != 1) { - PARANOIA("bad result for %s, len=%d, count=%d\n", - mask, req->rq_ldata, WVAL(req->rq_parm, 2)); - goto out_free; - } - - /* - * Decode the response into the fattr ... - */ - date = WVAL(req->rq_data, 0); - time = WVAL(req->rq_data, 2); - fattr->f_ctime.tv_sec = date_dos2unix(server, date, time); - fattr->f_ctime.tv_nsec = 0; - - date = WVAL(req->rq_data, 4); - time = WVAL(req->rq_data, 6); - fattr->f_atime.tv_sec = date_dos2unix(server, date, time); - fattr->f_atime.tv_nsec = 0; - - date = WVAL(req->rq_data, 8); - time = WVAL(req->rq_data, 10); - fattr->f_mtime.tv_sec = date_dos2unix(server, date, time); - fattr->f_mtime.tv_nsec = 0; - VERBOSE("name=%s, date=%x, time=%x, mtime=%ld\n", - mask, date, time, fattr->f_mtime.tv_sec); - fattr->f_size = DVAL(req->rq_data, 12); - /* ULONG allocation size */ - fattr->attr = WVAL(req->rq_data, 20); - result = 0; - -out_free: - smb_rput(req); -out: - return result; -} - -static int -smb_proc_getattr_core(struct smb_sb_info *server, struct dentry *dir, - struct smb_fattr *fattr) -{ - int result; - char *p; - struct smb_request *req; - - result = -ENOMEM; - if (! (req = smb_alloc_request(server, PAGE_SIZE))) - goto out; - - p = smb_setup_header(req, SMBgetatr, 0, 0); - result = smb_simple_encode_path(req, &p, dir, NULL); - if (result < 0) - goto out_free; - smb_setup_bcc(req, p); - - if ((result = smb_request_ok(req, SMBgetatr, 10, 0)) < 0) - goto out_free; - fattr->attr = WVAL(req->rq_header, smb_vwv0); - fattr->f_mtime.tv_sec = local2utc(server, DVAL(req->rq_header, smb_vwv1)); - fattr->f_mtime.tv_nsec = 0; - fattr->f_size = DVAL(req->rq_header, smb_vwv3); - fattr->f_ctime = fattr->f_mtime; - fattr->f_atime = fattr->f_mtime; -#ifdef SMBFS_DEBUG_TIMESTAMP - printk("getattr_core: %s/%s, mtime=%ld\n", - DENTRY_PATH(dir), fattr->f_mtime); -#endif - result = 0; - -out_free: - smb_rput(req); -out: - return result; -} - -/* - * Bugs Noted: - * (1) Win 95 swaps the date and time fields in the standard info level. - */ -static int -smb_proc_getattr_trans2(struct smb_sb_info *server, struct dentry *dir, - struct smb_request *req, int infolevel) -{ - char *p, *param; - int result; - - param = req->rq_buffer; - WSET(param, 0, infolevel); - DSET(param, 2, 0); - result = smb_encode_path(server, param+6, SMB_MAXPATHLEN+1, dir, NULL); - if (result < 0) - goto out; - 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; - if (req->rq_rcls != 0) { - VERBOSE("for %s: result=%d, rcls=%d, err=%d\n", - ¶m[6], result, req->rq_rcls, req->rq_err); - result = smb_errno(req); - goto out; - } - result = -ENOENT; - if (req->rq_ldata < 22) { - PARANOIA("not enough data for %s, len=%d\n", - ¶m[6], req->rq_ldata); - goto out; - } - - result = 0; -out: - return result; -} - -static int -smb_proc_getattr_trans2_std(struct smb_sb_info *server, struct dentry *dir, - struct smb_fattr *attr) -{ - u16 date, time; - int off_date = 0, off_time = 2; - int result; - struct smb_request *req; - - result = -ENOMEM; - if (! (req = smb_alloc_request(server, PAGE_SIZE))) - goto out; - - result = smb_proc_getattr_trans2(server, dir, req, SMB_INFO_STANDARD); - if (result < 0) - goto out_free; - - /* - * 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(req->rq_data, off_date); - time = WVAL(req->rq_data, off_time); - attr->f_ctime.tv_sec = date_dos2unix(server, date, time); - attr->f_ctime.tv_nsec = 0; - - date = WVAL(req->rq_data, 4 + off_date); - time = WVAL(req->rq_data, 4 + off_time); - attr->f_atime.tv_sec = date_dos2unix(server, date, time); - attr->f_atime.tv_nsec = 0; - - date = WVAL(req->rq_data, 8 + off_date); - time = WVAL(req->rq_data, 8 + off_time); - attr->f_mtime.tv_sec = date_dos2unix(server, date, time); - attr->f_mtime.tv_nsec = 0; -#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); -#endif - attr->f_size = DVAL(req->rq_data, 12); - attr->attr = WVAL(req->rq_data, 20); - -out_free: - smb_rput(req); -out: - return result; -} - -static int -smb_proc_getattr_trans2_all(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_ALL_INFO); - if (result < 0) - goto out_free; - - attr->f_ctime = smb_ntutc2unixutc(LVAL(req->rq_data, 0)); - attr->f_atime = smb_ntutc2unixutc(LVAL(req->rq_data, 8)); - attr->f_mtime = smb_ntutc2unixutc(LVAL(req->rq_data, 16)); - /* change (24) */ - attr->attr = WVAL(req->rq_data, 32); - /* pad? (34) */ - /* allocated size (40) */ - attr->f_size = LVAL(req->rq_data, 48); - -out_free: - smb_rput(req); -out: - return result; -} - -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, server, 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) -{ - struct inode *inode = dir->d_inode; - int result; - - /* FIXME: why not use the "all" version? */ - result = smb_proc_getattr_trans2_std(server, dir, attr); - if (result < 0) - goto out; - - /* - * None of the getattr versions here can make win9x return the right - * filesize if there are changes made to an open file. - * A seek-to-end does return the right size, but we only need to do - * that on files we have written. - */ - if (inode && SMB_I(inode)->flags & SMB_F_LOCALWRITE && - smb_is_open(inode)) - { - __u16 fileid = SMB_I(inode)->fileid; - attr->f_size = smb_proc_seek(server, fileid, 2, 0); - } - -out: - return result; -} - -static int -smb_proc_ops_wait(struct smb_sb_info *server) -{ - int result; - - result = wait_event_interruptible_timeout(server->conn_wq, - server->conn_complete, 30*HZ); - - if (!result || signal_pending(current)) - return -EIO; - - return 0; -} - -static int -smb_proc_getattr_null(struct smb_sb_info *server, struct dentry *dir, - struct smb_fattr *fattr) -{ - int result; - - if (smb_proc_ops_wait(server) < 0) - return -EIO; - - smb_init_dirent(server, fattr); - result = server->ops->getattr(server, dir, fattr); - smb_finish_dirent(server, fattr); - - return result; -} - -static int -smb_proc_readdir_null(struct file *filp, void *dirent, filldir_t filldir, - struct smb_cache_control *ctl) -{ - struct smb_sb_info *server = server_from_dentry(filp->f_path.dentry); - - if (smb_proc_ops_wait(server) < 0) - return -EIO; - - return server->ops->readdir(filp, dirent, filldir, ctl); -} - -int -smb_proc_getattr(struct dentry *dir, struct smb_fattr *fattr) -{ - struct smb_sb_info *server = server_from_dentry(dir); - int result; - - smb_init_dirent(server, fattr); - result = server->ops->getattr(server, dir, fattr); - smb_finish_dirent(server, fattr); - - return result; -} - - -/* - * Because of bugs in the core protocol, we use this only to set - * attributes. See smb_proc_settime() below for timestamp handling. - * - * Bugs Noted: - * (1) If mtime is non-zero, both Win 3.1 and Win 95 fail - * with an undocumented error (ERRDOS code 50). Setting - * mtime to 0 allows the attributes to be set. - * (2) The extra parameters following the name string aren't - * in the CIFS docs, but seem to be necessary for operation. - */ -static int -smb_proc_setattr_core(struct smb_sb_info *server, struct dentry *dentry, - __u16 attr) -{ - char *p; - int result; - struct smb_request *req; - - result = -ENOMEM; - if (! (req = smb_alloc_request(server, PAGE_SIZE))) - goto out; - - p = smb_setup_header(req, SMBsetatr, 8, 0); - WSET(req->rq_header, smb_vwv0, attr); - DSET(req->rq_header, smb_vwv1, 0); /* mtime */ - WSET(req->rq_header, smb_vwv3, 0); /* reserved values */ - WSET(req->rq_header, smb_vwv4, 0); - WSET(req->rq_header, smb_vwv5, 0); - WSET(req->rq_header, smb_vwv6, 0); - WSET(req->rq_header, smb_vwv7, 0); - result = smb_simple_encode_path(req, &p, dentry, NULL); - if (result < 0) - goto out_free; - if (p + 2 > (char *)req->rq_buffer + req->rq_bufsize) { - result = -ENAMETOOLONG; - goto out_free; - } - *p++ = 4; - *p++ = 0; - smb_setup_bcc(req, p); - - result = smb_request_ok(req, SMBsetatr, 0, 0); - if (result < 0) - goto out_free; - result = 0; - -out_free: - smb_rput(req); -out: - return result; -} - -/* - * 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) -{ - struct smb_sb_info *server = server_from_dentry(dir); - int result; - - VERBOSE("setting %s/%s, open=%d\n", - DENTRY_PATH(dir), smb_is_open(dir->d_inode)); - result = smb_proc_setattr_core(server, dir, fattr->attr); - return result; -} - -/* - * Sets the timestamps for an file open with write permissions. - */ -static int -smb_proc_setattr_ext(struct smb_sb_info *server, - struct inode *inode, struct smb_fattr *fattr) -{ - __u16 date, time; - int result; - struct smb_request *req; - - result = -ENOMEM; - if (! (req = smb_alloc_request(server, 0))) - goto out; - - smb_setup_header(req, SMBsetattrE, 7, 0); - WSET(req->rq_header, smb_vwv0, SMB_I(inode)->fileid); - /* We don't change the creation time */ - WSET(req->rq_header, smb_vwv1, 0); - WSET(req->rq_header, smb_vwv2, 0); - date_unix2dos(server, fattr->f_atime.tv_sec, &date, &time); - WSET(req->rq_header, smb_vwv3, date); - WSET(req->rq_header, smb_vwv4, time); - date_unix2dos(server, fattr->f_mtime.tv_sec, &date, &time); - WSET(req->rq_header, smb_vwv5, date); - WSET(req->rq_header, smb_vwv6, time); -#ifdef SMBFS_DEBUG_TIMESTAMP - printk(KERN_DEBUG "smb_proc_setattr_ext: date=%d, time=%d, mtime=%ld\n", - date, time, fattr->f_mtime); -#endif - - req->rq_flags |= SMB_REQ_NORETRY; - result = smb_request_ok(req, SMBsetattrE, 0, 0); - if (result < 0) - goto out_free; - result = 0; -out_free: - smb_rput(req); -out: - return result; -} - -/* - * Bugs Noted: - * (1) The TRANSACT2_SETPATHINFO message under Win NT 4.0 doesn't - * set the file's attribute flags. - */ -static int -smb_proc_setattr_trans2(struct smb_sb_info *server, - struct dentry *dir, struct smb_fattr *fattr) -{ - __u16 date, time; - char *p, *param; - int result; - char data[26]; - struct smb_request *req; - - result = -ENOMEM; - if (! (req = smb_alloc_request(server, PAGE_SIZE))) - goto out; - param = req->rq_buffer; - - WSET(param, 0, 1); /* Info level SMB_INFO_STANDARD */ - DSET(param, 2, 0); - result = smb_encode_path(server, param+6, SMB_MAXPATHLEN+1, dir, NULL); - if (result < 0) - goto out_free; - p = param + 6 + result; - - WSET(data, 0, 0); /* creation time */ - WSET(data, 2, 0); - date_unix2dos(server, fattr->f_atime.tv_sec, &date, &time); - WSET(data, 4, date); - WSET(data, 6, time); - date_unix2dos(server, fattr->f_mtime.tv_sec, &date, &time); - WSET(data, 8, date); - WSET(data, 10, time); -#ifdef SMBFS_DEBUG_TIMESTAMP - printk(KERN_DEBUG "setattr_trans2: %s/%s, date=%x, time=%x, mtime=%ld\n", - DENTRY_PATH(dir), date, time, fattr->f_mtime); -#endif - DSET(data, 12, 0); /* size */ - DSET(data, 16, 0); /* blksize */ - WSET(data, 20, 0); /* attr */ - DSET(data, 22, 0); /* ULONG EA size */ - - req->rq_trans2_command = TRANSACT2_SETPATHINFO; - req->rq_ldata = 26; - req->rq_data = data; - req->rq_lparm = p - param; - req->rq_parm = param; - req->rq_flags = 0; - result = smb_add_request(req); - if (result < 0) - goto out_free; - result = 0; - if (req->rq_rcls != 0) - result = smb_errno(req); - -out_free: - smb_rput(req); -out: - return result; -} - -/* - * 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, - unsigned int major, unsigned 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); - DSET(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 - * setting both attributes and timestamps at once. - * - * Bugs Noted: - * (1) Win 95 doesn't support the TRANSACT2_SETFILEINFO message - * with info level 1 (INFO_STANDARD). - * (2) Win 95 seems not to support setting directory timestamps. - * (3) Under the core protocol apparently the only way to set the - * timestamp is to open and close the file. - */ -int -smb_proc_settime(struct dentry *dentry, struct smb_fattr *fattr) -{ - struct smb_sb_info *server = server_from_dentry(dentry); - struct inode *inode = dentry->d_inode; - int result; - - VERBOSE("setting %s/%s, open=%d\n", - DENTRY_PATH(dentry), smb_is_open(inode)); - - /* setting the time on a Win95 server fails (tridge) */ - if (server->opt.protocol >= SMB_PROTOCOL_LANMAN2 && - !(server->mnt->flags & SMB_MOUNT_WIN95)) { - if (smb_is_open(inode) && SMB_I(inode)->access != SMB_O_RDONLY) - result = smb_proc_setattr_ext(server, inode, fattr); - else - result = smb_proc_setattr_trans2(server, dentry, fattr); - } else { - /* - * Fail silently on directories ... timestamp can't be set? - */ - result = 0; - if (S_ISREG(inode->i_mode)) { - /* - * Set the mtime by opening and closing the file. - * Note that the file is opened read-only, but this - * still allows us to set the date (tridge) - */ - result = -EACCES; - if (!smb_is_open(inode)) - smb_proc_open(server, dentry, SMB_O_RDONLY); - if (smb_is_open(inode)) { - inode->i_mtime = fattr->f_mtime; - result = smb_proc_close_inode(server, inode); - } - } - } - - return result; -} - -int -smb_proc_dskattr(struct dentry *dentry, struct kstatfs *attr) -{ - struct smb_sb_info *server = SMB_SB(dentry->d_sb); - int result; - char *p; - long unit; - struct smb_request *req; - - result = -ENOMEM; - if (! (req = smb_alloc_request(server, 0))) - goto out; - - smb_setup_header(req, SMBdskattr, 0, 0); - if ((result = smb_request_ok(req, SMBdskattr, 5, 0)) < 0) - goto out_free; - p = SMB_VWV(req->rq_header); - unit = (WVAL(p, 2) * WVAL(p, 4)) >> SMB_ST_BLKSHIFT; - attr->f_blocks = WVAL(p, 0) * unit; - attr->f_bsize = SMB_ST_BLKSIZE; - attr->f_bavail = attr->f_bfree = WVAL(p, 6) * unit; - result = 0; - -out_free: - smb_rput(req); -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, req->rq_rcls, req->rq_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, req->rq_rcls, req->rq_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, req->rq_rcls, req->rq_err); - result = 0; - -out_free: - smb_rput(req); -out: - return result; -} - -static 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) -{ - memcpy(dst, src, sizeof(void *) * SMB_OPS_NUM_STATIC); -} - -/* < LANMAN2 */ -static struct smb_ops smb_ops_core = -{ - .read = smb_proc_read, - .write = smb_proc_write, - .readdir = smb_proc_readdir_short, - .getattr = smb_proc_getattr_core, - .truncate = smb_proc_trunc32, -}; - -/* LANMAN2, OS/2, others? */ -static struct smb_ops smb_ops_os2 = -{ - .read = smb_proc_read, - .write = smb_proc_write, - .readdir = smb_proc_readdir_long, - .getattr = smb_proc_getattr_trans2_std, - .truncate = smb_proc_trunc32, -}; - -/* Win95, and possibly some NetApp versions too */ -static struct smb_ops smb_ops_win95 = -{ - .read = smb_proc_read, /* does not support 12word readX */ - .write = smb_proc_write, - .readdir = smb_proc_readdir_long, - .getattr = smb_proc_getattr_95, - .truncate = smb_proc_trunc95, -}; - -/* Samba, NT4 and NT5 */ -static struct smb_ops smb_ops_winNT = -{ - .read = smb_proc_readX, - .write = smb_proc_writeX, - .readdir = smb_proc_readdir_long, - .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, -}; - -/* Place holder until real ops are in place */ -static struct smb_ops smb_ops_null = -{ - .readdir = smb_proc_readdir_null, - .getattr = smb_proc_getattr_null, -}; - -void smb_install_null_ops(struct smb_ops *ops) -{ - install_ops(ops, &smb_ops_null); -} diff --git a/fs/smbfs/proto.h b/fs/smbfs/proto.h deleted file mode 100644 index 05939a6f43e6..000000000000 --- a/fs/smbfs/proto.h +++ /dev/null @@ -1,87 +0,0 @@ -/* - * Autogenerated with cproto on: Sat Sep 13 17:18:51 CEST 2003 - */ - -struct smb_request; -struct sock; -struct statfs; - -/* proc.c */ -extern int smb_setcodepage(struct smb_sb_info *server, struct smb_nls_codepage *cp); -extern __u32 smb_len(__u8 *p); -extern int smb_get_rsize(struct smb_sb_info *server); -extern int smb_get_wsize(struct smb_sb_info *server); -extern int smb_errno(struct smb_request *req); -extern int smb_newconn(struct smb_sb_info *server, struct smb_conn_opt *opt); -extern __u8 *smb_setup_header(struct smb_request *req, __u8 command, __u16 wct, __u16 bcc); -extern int smb_open(struct dentry *dentry, int wish); -extern int smb_close(struct inode *ino); -extern int smb_close_fileid(struct dentry *dentry, __u16 fileid); -extern int smb_proc_create(struct dentry *dentry, __u16 attr, time_t ctime, __u16 *fileid); -extern int smb_proc_mv(struct dentry *old_dentry, struct dentry *new_dentry); -extern int smb_proc_mkdir(struct dentry *dentry); -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, - struct super_block *sb); -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, unsigned int major, unsigned int minor); -extern int smb_proc_settime(struct dentry *dentry, struct smb_fattr *fattr); -extern int smb_proc_dskattr(struct dentry *dentry, struct kstatfs *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 void smb_install_null_ops(struct smb_ops *ops); -/* dir.c */ -extern const struct file_operations smb_dir_operations; -extern const struct inode_operations smb_dir_inode_operations; -extern const 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 */ -extern void smb_invalid_dir_cache(struct inode *dir); -extern void smb_invalidate_dircache_entries(struct dentry *parent); -extern struct dentry *smb_dget_fpos(struct dentry *dentry, struct dentry *parent, unsigned long fpos); -extern int smb_fill_cache(struct file *filp, void *dirent, filldir_t filldir, struct smb_cache_control *ctrl, struct qstr *qname, struct smb_fattr *entry); -/* sock.c */ -extern void smb_data_ready(struct sock *sk, int len); -extern int smb_valid_socket(struct inode *inode); -extern void smb_close_socket(struct smb_sb_info *server); -extern int smb_recv_available(struct smb_sb_info *server); -extern int smb_receive_header(struct smb_sb_info *server); -extern int smb_receive_drop(struct smb_sb_info *server); -extern int smb_receive(struct smb_sb_info *server, struct smb_request *req); -extern int smb_send_request(struct smb_request *req); -/* inode.c */ -extern struct inode *smb_iget(struct super_block *sb, struct smb_fattr *fattr); -extern void smb_get_inode_attr(struct inode *inode, struct smb_fattr *fattr); -extern void smb_set_inode_attr(struct inode *inode, struct smb_fattr *fattr); -extern void smb_invalidate_inodes(struct smb_sb_info *server); -extern int smb_revalidate_inode(struct dentry *dentry); -extern int smb_getattr(struct vfsmount *mnt, struct dentry *dentry, struct kstat *stat); -extern int smb_notify_change(struct dentry *dentry, struct iattr *attr); -/* file.c */ -extern const struct address_space_operations smb_file_aops; -extern const struct file_operations smb_file_operations; -extern const struct inode_operations smb_file_inode_operations; -/* ioctl.c */ -extern long smb_ioctl(struct file *filp, unsigned int cmd, unsigned long arg); -/* smbiod.c */ -extern void smbiod_wake_up(void); -extern int smbiod_register_server(struct smb_sb_info *server); -extern void smbiod_unregister_server(struct smb_sb_info *server); -extern void smbiod_flush(struct smb_sb_info *server); -extern int smbiod_retry(struct smb_sb_info *server); -/* request.c */ -extern int smb_init_request_cache(void); -extern void smb_destroy_request_cache(void); -extern struct smb_request *smb_alloc_request(struct smb_sb_info *server, int bufsize); -extern void smb_rput(struct smb_request *req); -extern int smb_add_request(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_symlink(struct inode *inode, struct dentry *dentry, const char *oldname); -extern const struct inode_operations smb_link_inode_operations; diff --git a/fs/smbfs/request.c b/fs/smbfs/request.c deleted file mode 100644 index 45f45933e862..000000000000 --- a/fs/smbfs/request.c +++ /dev/null @@ -1,818 +0,0 @@ -/* - * request.c - * - * Copyright (C) 2001 by Urban Widmark - * - * Please add a note about your changes to smbfs in the ChangeLog file. - */ - -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#include "smb_debug.h" -#include "request.h" -#include "proto.h" - -/* #define SMB_SLAB_DEBUG (SLAB_RED_ZONE | SLAB_POISON) */ -#define SMB_SLAB_DEBUG 0 - -/* cache for request structures */ -static struct kmem_cache *req_cachep; - -static int smb_request_send_req(struct smb_request *req); - -/* - /proc/slabinfo: - name, active, num, objsize, active_slabs, num_slaps, #pages -*/ - - -int smb_init_request_cache(void) -{ - req_cachep = kmem_cache_create("smb_request", - sizeof(struct smb_request), 0, - SMB_SLAB_DEBUG | SLAB_HWCACHE_ALIGN, - NULL); - if (req_cachep == NULL) - return -ENOMEM; - - return 0; -} - -void smb_destroy_request_cache(void) -{ - kmem_cache_destroy(req_cachep); -} - -/* - * Allocate and initialise a request structure - */ -static struct smb_request *smb_do_alloc_request(struct smb_sb_info *server, - int bufsize) -{ - struct smb_request *req; - unsigned char *buf = NULL; - - req = kmem_cache_zalloc(req_cachep, GFP_KERNEL); - VERBOSE("allocating request: %p\n", req); - if (!req) - goto out; - - if (bufsize > 0) { - buf = kmalloc(bufsize, GFP_NOFS); - if (!buf) { - kmem_cache_free(req_cachep, req); - return NULL; - } - } - - req->rq_buffer = buf; - req->rq_bufsize = bufsize; - req->rq_server = server; - init_waitqueue_head(&req->rq_wait); - INIT_LIST_HEAD(&req->rq_queue); - atomic_set(&req->rq_count, 1); - -out: - return req; -} - -struct smb_request *smb_alloc_request(struct smb_sb_info *server, int bufsize) -{ - struct smb_request *req = NULL; - - for (;;) { - atomic_inc(&server->nr_requests); - if (atomic_read(&server->nr_requests) <= MAX_REQUEST_HARD) { - req = smb_do_alloc_request(server, bufsize); - if (req != NULL) - break; - } - -#if 0 - /* - * Try to free up at least one request in order to stay - * below the hard limit - */ - if (nfs_try_to_free_pages(server)) - continue; - - if (fatal_signal_pending(current)) - return ERR_PTR(-ERESTARTSYS); - current->policy = SCHED_YIELD; - schedule(); -#else - /* FIXME: we want something like nfs does above, but that - requires changes to all callers and can wait. */ - break; -#endif - } - return req; -} - -static void smb_free_request(struct smb_request *req) -{ - atomic_dec(&req->rq_server->nr_requests); - if (req->rq_buffer && !(req->rq_flags & SMB_REQ_STATIC)) - kfree(req->rq_buffer); - kfree(req->rq_trans2buffer); - kmem_cache_free(req_cachep, req); -} - -/* - * What prevents a rget to race with a rput? The count must never drop to zero - * while it is in use. Only rput if it is ok that it is free'd. - */ -static void smb_rget(struct smb_request *req) -{ - atomic_inc(&req->rq_count); -} -void smb_rput(struct smb_request *req) -{ - if (atomic_dec_and_test(&req->rq_count)) { - list_del_init(&req->rq_queue); - smb_free_request(req); - } -} - -/* setup to receive the data part of the SMB */ -static int smb_setup_bcc(struct smb_request *req) -{ - int result = 0; - req->rq_rlen = smb_len(req->rq_header) + 4 - req->rq_bytes_recvd; - - if (req->rq_rlen > req->rq_bufsize) { - PARANOIA("Packet too large %d > %d\n", - req->rq_rlen, req->rq_bufsize); - return -ENOBUFS; - } - - req->rq_iov[0].iov_base = req->rq_buffer; - req->rq_iov[0].iov_len = req->rq_rlen; - req->rq_iovlen = 1; - - return result; -} - -/* - * Prepare a "normal" request structure. - */ -static int smb_setup_request(struct smb_request *req) -{ - int len = smb_len(req->rq_header) + 4; - req->rq_slen = len; - - /* if we expect a data part in the reply we set the iov's to read it */ - if (req->rq_resp_bcc) - req->rq_setup_read = smb_setup_bcc; - - /* This tries to support re-using the same request */ - req->rq_bytes_sent = 0; - req->rq_rcls = 0; - req->rq_err = 0; - req->rq_errno = 0; - req->rq_fragment = 0; - kfree(req->rq_trans2buffer); - req->rq_trans2buffer = NULL; - - return 0; -} - -/* - * Prepare a transaction2 request structure - */ -static int smb_setup_trans2request(struct smb_request *req) -{ - struct smb_sb_info *server = req->rq_server; - int mparam, mdata; - static unsigned char padding[4]; - - /* I know the following is very ugly, but I want to build the - smb packet as efficiently as possible. */ - - const int smb_parameters = 15; - const int header = SMB_HEADER_LEN + 2 * smb_parameters + 2; - const int oparam = ALIGN(header + 3, sizeof(u32)); - const int odata = ALIGN(oparam + req->rq_lparm, sizeof(u32)); - const int bcc = (req->rq_data ? odata + req->rq_ldata : - oparam + req->rq_lparm) - header; - - if ((bcc + oparam) > server->opt.max_xmit) - return -ENOMEM; - smb_setup_header(req, SMBtrans2, smb_parameters, bcc); - - /* - * max parameters + max data + max setup == bufsize to make NT4 happy - * and not abort the transfer or split into multiple responses. It also - * makes smbfs happy as handling packets larger than the buffer size - * is extra work. - * - * OS/2 is probably going to hate me for this ... - */ - mparam = SMB_TRANS2_MAX_PARAM; - mdata = req->rq_bufsize - mparam; - - mdata = server->opt.max_xmit - mparam - 100; - if (mdata < 1024) { - mdata = 1024; - mparam = 20; - } - -#if 0 - /* NT/win2k has ~4k max_xmit, so with this we request more than it wants - to return as one SMB. Useful for testing the fragmented trans2 - handling. */ - mdata = 8192; -#endif - - WSET(req->rq_header, smb_tpscnt, req->rq_lparm); - WSET(req->rq_header, smb_tdscnt, req->rq_ldata); - WSET(req->rq_header, smb_mprcnt, mparam); - WSET(req->rq_header, smb_mdrcnt, mdata); - WSET(req->rq_header, smb_msrcnt, 0); /* max setup always 0 ? */ - WSET(req->rq_header, smb_flags, 0); - DSET(req->rq_header, smb_timeout, 0); - WSET(req->rq_header, smb_pscnt, req->rq_lparm); - WSET(req->rq_header, smb_psoff, oparam - 4); - WSET(req->rq_header, smb_dscnt, req->rq_ldata); - WSET(req->rq_header, smb_dsoff, req->rq_data ? odata - 4 : 0); - *(req->rq_header + smb_suwcnt) = 0x01; /* setup count */ - *(req->rq_header + smb_suwcnt + 1) = 0x00; /* reserved */ - WSET(req->rq_header, smb_setup0, req->rq_trans2_command); - - req->rq_iovlen = 2; - req->rq_iov[0].iov_base = (void *) req->rq_header; - req->rq_iov[0].iov_len = oparam; - req->rq_iov[1].iov_base = (req->rq_parm==NULL) ? padding : req->rq_parm; - req->rq_iov[1].iov_len = req->rq_lparm; - req->rq_slen = oparam + req->rq_lparm; - - if (req->rq_data) { - req->rq_iovlen += 2; - req->rq_iov[2].iov_base = padding; - req->rq_iov[2].iov_len = odata - oparam - req->rq_lparm; - req->rq_iov[3].iov_base = req->rq_data; - req->rq_iov[3].iov_len = req->rq_ldata; - req->rq_slen = odata + req->rq_ldata; - } - - /* always a data part for trans2 replies */ - req->rq_setup_read = smb_setup_bcc; - - return 0; -} - -/* - * Add a request and tell smbiod to process it - */ -int smb_add_request(struct smb_request *req) -{ - long timeleft; - struct smb_sb_info *server = req->rq_server; - int result = 0; - - smb_setup_request(req); - if (req->rq_trans2_command) { - if (req->rq_buffer == NULL) { - PARANOIA("trans2 attempted without response buffer!\n"); - return -EIO; - } - result = smb_setup_trans2request(req); - } - if (result < 0) - return result; - -#ifdef SMB_DEBUG_PACKET_SIZE - add_xmit_stats(req); -#endif - - /* add 'req' to the queue of requests */ - if (smb_lock_server_interruptible(server)) - return -EINTR; - - /* - * Try to send the request as the process. If that fails we queue the - * request and let smbiod send it later. - */ - - /* FIXME: each server has a number on the maximum number of parallel - requests. 10, 50 or so. We should not allow more requests to be - active. */ - if (server->mid > 0xf000) - server->mid = 0; - req->rq_mid = server->mid++; - WSET(req->rq_header, smb_mid, req->rq_mid); - - result = 0; - if (server->state == CONN_VALID) { - if (list_empty(&server->xmitq)) - result = smb_request_send_req(req); - if (result < 0) { - /* Connection lost? */ - server->conn_error = result; - server->state = CONN_INVALID; - } - } - if (result != 1) - list_add_tail(&req->rq_queue, &server->xmitq); - smb_rget(req); - - if (server->state != CONN_VALID) - smbiod_retry(server); - - smb_unlock_server(server); - - smbiod_wake_up(); - - timeleft = wait_event_interruptible_timeout(req->rq_wait, - req->rq_flags & SMB_REQ_RECEIVED, 30*HZ); - if (!timeleft || signal_pending(current)) { - /* - * On timeout or on interrupt we want to try and remove the - * request from the recvq/xmitq. - * First check if the request is still part of a queue. (May - * have been removed by some error condition) - */ - smb_lock_server(server); - if (!list_empty(&req->rq_queue)) { - list_del_init(&req->rq_queue); - smb_rput(req); - } - smb_unlock_server(server); - } - - if (!timeleft) { - PARANOIA("request [%p, mid=%d] timed out!\n", - req, req->rq_mid); - VERBOSE("smb_com: %02x\n", *(req->rq_header + smb_com)); - VERBOSE("smb_rcls: %02x\n", *(req->rq_header + smb_rcls)); - VERBOSE("smb_flg: %02x\n", *(req->rq_header + smb_flg)); - VERBOSE("smb_tid: %04x\n", WVAL(req->rq_header, smb_tid)); - VERBOSE("smb_pid: %04x\n", WVAL(req->rq_header, smb_pid)); - VERBOSE("smb_uid: %04x\n", WVAL(req->rq_header, smb_uid)); - VERBOSE("smb_mid: %04x\n", WVAL(req->rq_header, smb_mid)); - VERBOSE("smb_wct: %02x\n", *(req->rq_header + smb_wct)); - - req->rq_rcls = ERRSRV; - req->rq_err = ERRtimeout; - - /* Just in case it was "stuck" */ - smbiod_wake_up(); - } - VERBOSE("woke up, rcls=%d\n", req->rq_rcls); - - if (req->rq_rcls != 0) - req->rq_errno = smb_errno(req); - if (signal_pending(current)) - req->rq_errno = -ERESTARTSYS; - return req->rq_errno; -} - -/* - * Send a request and place it on the recvq if successfully sent. - * Must be called with the server lock held. - */ -static int smb_request_send_req(struct smb_request *req) -{ - struct smb_sb_info *server = req->rq_server; - int result; - - if (req->rq_bytes_sent == 0) { - WSET(req->rq_header, smb_tid, server->opt.tid); - WSET(req->rq_header, smb_pid, 1); - WSET(req->rq_header, smb_uid, server->opt.server_uid); - } - - result = smb_send_request(req); - if (result < 0 && result != -EAGAIN) - goto out; - - result = 0; - if (!(req->rq_flags & SMB_REQ_TRANSMITTED)) - goto out; - - list_move_tail(&req->rq_queue, &server->recvq); - result = 1; -out: - return result; -} - -/* - * Sends one request for this server. (smbiod) - * Must be called with the server lock held. - * Returns: <0 on error - * 0 if no request could be completely sent - * 1 if all data for one request was sent - */ -int smb_request_send_server(struct smb_sb_info *server) -{ - struct list_head *head; - struct smb_request *req; - int result; - - if (server->state != CONN_VALID) - return 0; - - /* dequeue first request, if any */ - req = NULL; - head = server->xmitq.next; - if (head != &server->xmitq) { - req = list_entry(head, struct smb_request, rq_queue); - } - if (!req) - return 0; - - result = smb_request_send_req(req); - if (result < 0) { - server->conn_error = result; - list_move(&req->rq_queue, &server->xmitq); - result = -EIO; - goto out; - } - -out: - return result; -} - -/* - * Try to find a request matching this "mid". Typically the first entry will - * be the matching one. - */ -static struct smb_request *find_request(struct smb_sb_info *server, int mid) -{ - struct list_head *tmp; - struct smb_request *req = NULL; - - list_for_each(tmp, &server->recvq) { - req = list_entry(tmp, struct smb_request, rq_queue); - if (req->rq_mid == mid) { - break; - } - req = NULL; - } - - if (!req) { - VERBOSE("received reply with mid %d but no request!\n", - WVAL(server->header, smb_mid)); - server->rstate = SMB_RECV_DROP; - } - - return req; -} - -/* - * Called when we have read the smb header and believe this is a response. - */ -static int smb_init_request(struct smb_sb_info *server, struct smb_request *req) -{ - int hdrlen, wct; - - memcpy(req->rq_header, server->header, SMB_HEADER_LEN); - - wct = *(req->rq_header + smb_wct); - if (wct > 20) { - PARANOIA("wct too large, %d > 20\n", wct); - server->rstate = SMB_RECV_DROP; - return 0; - } - - req->rq_resp_wct = wct; - hdrlen = SMB_HEADER_LEN + wct*2 + 2; - VERBOSE("header length: %d smb_wct: %2d\n", hdrlen, wct); - - req->rq_bytes_recvd = SMB_HEADER_LEN; - req->rq_rlen = hdrlen; - req->rq_iov[0].iov_base = req->rq_header; - req->rq_iov[0].iov_len = hdrlen; - req->rq_iovlen = 1; - server->rstate = SMB_RECV_PARAM; - -#ifdef SMB_DEBUG_PACKET_SIZE - add_recv_stats(smb_len(server->header)); -#endif - return 0; -} - -/* - * Reads the SMB parameters - */ -static int smb_recv_param(struct smb_sb_info *server, struct smb_request *req) -{ - int result; - - result = smb_receive(server, req); - if (result < 0) - return result; - if (req->rq_bytes_recvd < req->rq_rlen) - return 0; - - VERBOSE("result: %d smb_bcc: %04x\n", result, - WVAL(req->rq_header, SMB_HEADER_LEN + - (*(req->rq_header + smb_wct) * 2))); - - result = 0; - req->rq_iov[0].iov_base = NULL; - req->rq_rlen = 0; - if (req->rq_callback) - req->rq_callback(req); - else if (req->rq_setup_read) - result = req->rq_setup_read(req); - if (result < 0) { - server->rstate = SMB_RECV_DROP; - return result; - } - - server->rstate = req->rq_rlen > 0 ? SMB_RECV_DATA : SMB_RECV_END; - - req->rq_bytes_recvd = 0; // recvd out of the iov - - VERBOSE("rlen: %d\n", req->rq_rlen); - if (req->rq_rlen < 0) { - PARANOIA("Parameters read beyond end of packet!\n"); - server->rstate = SMB_RECV_END; - return -EIO; - } - return 0; -} - -/* - * Reads the SMB data - */ -static int smb_recv_data(struct smb_sb_info *server, struct smb_request *req) -{ - int result; - - result = smb_receive(server, req); - if (result < 0) - goto out; - if (req->rq_bytes_recvd < req->rq_rlen) - goto out; - server->rstate = SMB_RECV_END; -out: - VERBOSE("result: %d\n", result); - return result; -} - -/* - * Receive a transaction2 response - * Return: 0 if the response has been fully read - * 1 if there are further "fragments" to read - * <0 if there is an error - */ -static int smb_recv_trans2(struct smb_sb_info *server, struct smb_request *req) -{ - unsigned char *inbuf; - unsigned int parm_disp, parm_offset, parm_count, parm_tot; - unsigned int data_disp, data_offset, data_count, data_tot; - int hdrlen = SMB_HEADER_LEN + req->rq_resp_wct*2 - 2; - - VERBOSE("handling trans2\n"); - - inbuf = req->rq_header; - data_tot = WVAL(inbuf, smb_tdrcnt); - parm_tot = WVAL(inbuf, smb_tprcnt); - parm_disp = WVAL(inbuf, smb_prdisp); - parm_offset = WVAL(inbuf, smb_proff); - parm_count = WVAL(inbuf, smb_prcnt); - data_disp = WVAL(inbuf, smb_drdisp); - data_offset = WVAL(inbuf, smb_droff); - data_count = WVAL(inbuf, smb_drcnt); - - /* Modify offset for the split header/buffer we use */ - if (data_count || data_offset) { - if (unlikely(data_offset < hdrlen)) - goto out_bad_data; - else - data_offset -= hdrlen; - } - if (parm_count || parm_offset) { - if (unlikely(parm_offset < hdrlen)) - goto out_bad_parm; - else - parm_offset -= hdrlen; - } - - if (parm_count == parm_tot && data_count == data_tot) { - /* - * This packet has all the trans2 data. - * - * We setup the request so that this will be the common - * case. It may be a server error to not return a - * response that fits. - */ - VERBOSE("single trans2 response " - "dcnt=%u, pcnt=%u, doff=%u, poff=%u\n", - data_count, parm_count, - data_offset, parm_offset); - req->rq_ldata = data_count; - req->rq_lparm = parm_count; - req->rq_data = req->rq_buffer + data_offset; - req->rq_parm = req->rq_buffer + parm_offset; - if (unlikely(parm_offset + parm_count > req->rq_rlen)) - goto out_bad_parm; - if (unlikely(data_offset + data_count > req->rq_rlen)) - goto out_bad_data; - return 0; - } - - VERBOSE("multi trans2 response " - "frag=%d, dcnt=%u, pcnt=%u, doff=%u, poff=%u\n", - req->rq_fragment, - data_count, parm_count, - data_offset, parm_offset); - - if (!req->rq_fragment) { - int buf_len; - - /* We got the first trans2 fragment */ - req->rq_fragment = 1; - req->rq_total_data = data_tot; - req->rq_total_parm = parm_tot; - req->rq_ldata = 0; - req->rq_lparm = 0; - - buf_len = data_tot + parm_tot; - if (buf_len > SMB_MAX_PACKET_SIZE) - goto out_too_long; - - req->rq_trans2bufsize = buf_len; - req->rq_trans2buffer = kzalloc(buf_len, GFP_NOFS); - if (!req->rq_trans2buffer) - goto out_no_mem; - - req->rq_parm = req->rq_trans2buffer; - req->rq_data = req->rq_trans2buffer + parm_tot; - } else if (unlikely(req->rq_total_data < data_tot || - req->rq_total_parm < parm_tot)) - goto out_data_grew; - - if (unlikely(parm_disp + parm_count > req->rq_total_parm || - parm_offset + parm_count > req->rq_rlen)) - goto out_bad_parm; - if (unlikely(data_disp + data_count > req->rq_total_data || - data_offset + data_count > req->rq_rlen)) - goto out_bad_data; - - inbuf = req->rq_buffer; - memcpy(req->rq_parm + parm_disp, inbuf + parm_offset, parm_count); - memcpy(req->rq_data + data_disp, inbuf + data_offset, data_count); - - req->rq_ldata += data_count; - req->rq_lparm += parm_count; - - /* - * Check whether we've received all of the data. Note that - * we use the packet totals -- total lengths might shrink! - */ - if (req->rq_ldata >= data_tot && req->rq_lparm >= parm_tot) { - req->rq_ldata = data_tot; - req->rq_lparm = parm_tot; - return 0; - } - return 1; - -out_too_long: - printk(KERN_ERR "smb_trans2: data/param too long, data=%u, parm=%u\n", - data_tot, parm_tot); - goto out_EIO; -out_no_mem: - printk(KERN_ERR "smb_trans2: couldn't allocate data area of %d bytes\n", - req->rq_trans2bufsize); - req->rq_errno = -ENOMEM; - goto out; -out_data_grew: - printk(KERN_ERR "smb_trans2: data/params grew!\n"); - goto out_EIO; -out_bad_parm: - printk(KERN_ERR "smb_trans2: invalid parms, disp=%u, cnt=%u, tot=%u, ofs=%u\n", - parm_disp, parm_count, parm_tot, parm_offset); - goto out_EIO; -out_bad_data: - printk(KERN_ERR "smb_trans2: invalid data, disp=%u, cnt=%u, tot=%u, ofs=%u\n", - data_disp, data_count, data_tot, data_offset); -out_EIO: - req->rq_errno = -EIO; -out: - return req->rq_errno; -} - -/* - * State machine for receiving responses. We handle the fact that we can't - * read the full response in one try by having states telling us how much we - * have read. - * - * Must be called with the server lock held (only called from smbiod). - * - * Return: <0 on error - */ -int smb_request_recv(struct smb_sb_info *server) -{ - struct smb_request *req = NULL; - int result = 0; - - if (smb_recv_available(server) <= 0) - return 0; - - VERBOSE("state: %d\n", server->rstate); - switch (server->rstate) { - case SMB_RECV_DROP: - result = smb_receive_drop(server); - if (result < 0) - break; - if (server->rstate == SMB_RECV_DROP) - break; - server->rstate = SMB_RECV_START; - /* fallthrough */ - case SMB_RECV_START: - server->smb_read = 0; - server->rstate = SMB_RECV_HEADER; - /* fallthrough */ - case SMB_RECV_HEADER: - result = smb_receive_header(server); - if (result < 0) - break; - if (server->rstate == SMB_RECV_HEADER) - break; - if (! (*(server->header + smb_flg) & SMB_FLAGS_REPLY) ) { - server->rstate = SMB_RECV_REQUEST; - break; - } - if (server->rstate != SMB_RECV_HCOMPLETE) - break; - /* fallthrough */ - case SMB_RECV_HCOMPLETE: - req = find_request(server, WVAL(server->header, smb_mid)); - if (!req) - break; - smb_init_request(server, req); - req->rq_rcls = *(req->rq_header + smb_rcls); - req->rq_err = WVAL(req->rq_header, smb_err); - if (server->rstate != SMB_RECV_PARAM) - break; - /* fallthrough */ - case SMB_RECV_PARAM: - if (!req) - req = find_request(server,WVAL(server->header,smb_mid)); - if (!req) - break; - result = smb_recv_param(server, req); - if (result < 0) - break; - if (server->rstate != SMB_RECV_DATA) - break; - /* fallthrough */ - case SMB_RECV_DATA: - if (!req) - req = find_request(server,WVAL(server->header,smb_mid)); - if (!req) - break; - result = smb_recv_data(server, req); - if (result < 0) - break; - break; - - /* We should never be called with any of these states */ - case SMB_RECV_END: - case SMB_RECV_REQUEST: - BUG(); - } - - if (result < 0) { - /* We saw an error */ - return result; - } - - if (server->rstate != SMB_RECV_END) - return 0; - - result = 0; - if (req->rq_trans2_command && req->rq_rcls == SUCCESS) - result = smb_recv_trans2(server, req); - - /* - * Response completely read. Drop any extra bytes sent by the server. - * (Yes, servers sometimes add extra bytes to responses) - */ - VERBOSE("smb_len: %d smb_read: %d\n", - server->smb_len, server->smb_read); - if (server->smb_read < server->smb_len) - smb_receive_drop(server); - - server->rstate = SMB_RECV_START; - - if (!result) { - list_del_init(&req->rq_queue); - req->rq_flags |= SMB_REQ_RECEIVED; - smb_rput(req); - wake_up_interruptible(&req->rq_wait); - } - return 0; -} diff --git a/fs/smbfs/request.h b/fs/smbfs/request.h deleted file mode 100644 index efb21451e7c9..000000000000 --- a/fs/smbfs/request.h +++ /dev/null @@ -1,70 +0,0 @@ -#include -#include -#include -#include - -struct smb_request { - struct list_head rq_queue; /* recvq or xmitq for the server */ - - atomic_t rq_count; - - wait_queue_head_t rq_wait; - int rq_flags; - int rq_mid; /* multiplex ID, set by request.c */ - - struct smb_sb_info *rq_server; - - /* header + word count + parameter words + byte count */ - unsigned char rq_header[SMB_HEADER_LEN + 20*2 + 2]; - - int rq_bufsize; - unsigned char *rq_buffer; - - /* FIXME: this is not good enough for merging IO requests. */ - unsigned char *rq_page; - int rq_rsize; - - int rq_resp_wct; - int rq_resp_bcc; - - int rq_rlen; - int rq_bytes_recvd; - - int rq_slen; - int rq_bytes_sent; - - int rq_iovlen; - struct kvec rq_iov[4]; - - int (*rq_setup_read) (struct smb_request *); - void (*rq_callback) (struct smb_request *); - - /* ------ trans2 stuff ------ */ - - u16 rq_trans2_command; /* 0 if not a trans2 request */ - unsigned int rq_ldata; - unsigned char *rq_data; - unsigned int rq_lparm; - unsigned char *rq_parm; - - int rq_fragment; - u32 rq_total_data; - u32 rq_total_parm; - int rq_trans2bufsize; - unsigned char *rq_trans2buffer; - - /* ------ response ------ */ - - unsigned short rq_rcls; - unsigned short rq_err; - int rq_errno; -}; - -#define SMB_REQ_STATIC 0x0001 /* rq_buffer is static */ -#define SMB_REQ_NORETRY 0x0002 /* request is invalid after retry */ - -#define SMB_REQ_TRANSMITTED 0x4000 /* all data has been sent */ -#define SMB_REQ_RECEIVED 0x8000 /* reply received, smbiod is done */ - -#define xSMB_REQ_NOREPLY 0x0004 /* we don't want the reply (if any) */ -#define xSMB_REQ_NORECEIVER 0x0008 /* caller doesn't wait for response */ diff --git a/fs/smbfs/smb_debug.h b/fs/smbfs/smb_debug.h deleted file mode 100644 index fc4b1a5dd755..000000000000 --- a/fs/smbfs/smb_debug.h +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Defines some debug macros for smbfs. - */ - -/* This makes a dentry parent/child name pair. Useful for debugging printk's */ -#define DENTRY_PATH(dentry) \ - (dentry)->d_parent->d_name.name,(dentry)->d_name.name - -/* - * safety checks that should never happen ??? - * these are normally enabled. - */ -#ifdef SMBFS_PARANOIA -# define PARANOIA(f, a...) printk(KERN_NOTICE "%s: " f, __func__ , ## a) -#else -# define PARANOIA(f, a...) do { ; } while(0) -#endif - -/* lots of debug messages */ -#ifdef SMBFS_DEBUG_VERBOSE -# define VERBOSE(f, a...) printk(KERN_DEBUG "%s: " f, __func__ , ## a) -#else -# define VERBOSE(f, a...) do { ; } while(0) -#endif - -/* - * "normal" debug messages, but not with a normal DEBUG define ... way - * too common name. - */ -#ifdef SMBFS_DEBUG -#define DEBUG1(f, a...) printk(KERN_DEBUG "%s: " f, __func__ , ## a) -#else -#define DEBUG1(f, a...) do { ; } while(0) -#endif diff --git a/fs/smbfs/smbiod.c b/fs/smbfs/smbiod.c deleted file mode 100644 index 0e39a924f10a..000000000000 --- a/fs/smbfs/smbiod.c +++ /dev/null @@ -1,344 +0,0 @@ -/* - * smbiod.c - * - * Copyright (C) 2000, Charles Loep / Corel Corp. - * Copyright (C) 2001, Urban Widmark - */ - - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#include -#include - -#include "smb_debug.h" -#include "request.h" -#include "proto.h" - -enum smbiod_state { - SMBIOD_DEAD, - SMBIOD_STARTING, - SMBIOD_RUNNING, -}; - -static enum smbiod_state smbiod_state = SMBIOD_DEAD; -static struct task_struct *smbiod_thread; -static DECLARE_WAIT_QUEUE_HEAD(smbiod_wait); -static LIST_HEAD(smb_servers); -static DEFINE_SPINLOCK(servers_lock); - -#define SMBIOD_DATA_READY (1<<0) -static unsigned long smbiod_flags; - -static int smbiod(void *); -static int smbiod_start(void); - -/* - * called when there's work for us to do - */ -void smbiod_wake_up(void) -{ - if (smbiod_state == SMBIOD_DEAD) - return; - set_bit(SMBIOD_DATA_READY, &smbiod_flags); - wake_up_interruptible(&smbiod_wait); -} - -/* - * start smbiod if none is running - */ -static int smbiod_start(void) -{ - struct task_struct *tsk; - int err = 0; - - if (smbiod_state != SMBIOD_DEAD) - return 0; - smbiod_state = SMBIOD_STARTING; - __module_get(THIS_MODULE); - spin_unlock(&servers_lock); - tsk = kthread_run(smbiod, NULL, "smbiod"); - if (IS_ERR(tsk)) { - err = PTR_ERR(tsk); - module_put(THIS_MODULE); - } - - spin_lock(&servers_lock); - if (err < 0) { - smbiod_state = SMBIOD_DEAD; - smbiod_thread = NULL; - } else { - smbiod_state = SMBIOD_RUNNING; - smbiod_thread = tsk; - } - return err; -} - -/* - * register a server & start smbiod if necessary - */ -int smbiod_register_server(struct smb_sb_info *server) -{ - int ret; - spin_lock(&servers_lock); - list_add(&server->entry, &smb_servers); - VERBOSE("%p\n", server); - ret = smbiod_start(); - spin_unlock(&servers_lock); - return ret; -} - -/* - * Unregister a server - * Must be called with the server lock held. - */ -void smbiod_unregister_server(struct smb_sb_info *server) -{ - spin_lock(&servers_lock); - list_del_init(&server->entry); - VERBOSE("%p\n", server); - spin_unlock(&servers_lock); - - smbiod_wake_up(); - smbiod_flush(server); -} - -void smbiod_flush(struct smb_sb_info *server) -{ - struct list_head *tmp, *n; - struct smb_request *req; - - list_for_each_safe(tmp, n, &server->xmitq) { - req = list_entry(tmp, struct smb_request, rq_queue); - req->rq_errno = -EIO; - list_del_init(&req->rq_queue); - smb_rput(req); - wake_up_interruptible(&req->rq_wait); - } - list_for_each_safe(tmp, n, &server->recvq) { - req = list_entry(tmp, struct smb_request, rq_queue); - req->rq_errno = -EIO; - list_del_init(&req->rq_queue); - smb_rput(req); - wake_up_interruptible(&req->rq_wait); - } -} - -/* - * Wake up smbmount and make it reconnect to the server. - * This must be called with the server locked. - * - * FIXME: add smbconnect version to this - */ -int smbiod_retry(struct smb_sb_info *server) -{ - struct list_head *head; - struct smb_request *req; - struct pid *pid = get_pid(server->conn_pid); - int result = 0; - - VERBOSE("state: %d\n", server->state); - if (server->state == CONN_VALID || server->state == CONN_RETRYING) - goto out; - - smb_invalidate_inodes(server); - - /* - * Some requests are meaningless after a retry, so we abort them. - * One example are all requests using 'fileid' since the files are - * closed on retry. - */ - head = server->xmitq.next; - while (head != &server->xmitq) { - req = list_entry(head, struct smb_request, rq_queue); - head = head->next; - - req->rq_bytes_sent = 0; - if (req->rq_flags & SMB_REQ_NORETRY) { - VERBOSE("aborting request %p on xmitq\n", req); - req->rq_errno = -EIO; - list_del_init(&req->rq_queue); - smb_rput(req); - wake_up_interruptible(&req->rq_wait); - } - } - - /* - * FIXME: test the code for retrying request we already sent - */ - head = server->recvq.next; - while (head != &server->recvq) { - req = list_entry(head, struct smb_request, rq_queue); - head = head->next; -#if 0 - if (req->rq_flags & SMB_REQ_RETRY) { - /* must move the request to the xmitq */ - VERBOSE("retrying request %p on recvq\n", req); - list_move(&req->rq_queue, &server->xmitq); - continue; - } -#endif - - VERBOSE("aborting request %p on recvq\n", req); - /* req->rq_rcls = ???; */ /* FIXME: set smb error code too? */ - req->rq_errno = -EIO; - list_del_init(&req->rq_queue); - smb_rput(req); - wake_up_interruptible(&req->rq_wait); - } - - smb_close_socket(server); - - if (!pid) { - /* FIXME: this is fatal, umount? */ - printk(KERN_ERR "smb_retry: no connection process\n"); - server->state = CONN_RETRIED; - goto out; - } - - /* - * Change state so that only one retry per server will be started. - */ - server->state = CONN_RETRYING; - - /* - * Note: use the "priv" flag, as a user process may need to reconnect. - */ - result = kill_pid(pid, SIGUSR1, 1); - if (result) { - /* FIXME: this is most likely fatal, umount? */ - printk(KERN_ERR "smb_retry: signal failed [%d]\n", result); - goto out; - } - VERBOSE("signalled pid %d\n", pid_nr(pid)); - - /* FIXME: The retried requests should perhaps get a "time boost". */ - -out: - put_pid(pid); - return result; -} - -/* - * Currently handles lockingX packets. - */ -static void smbiod_handle_request(struct smb_sb_info *server) -{ - PARANOIA("smbiod got a request ... and we don't implement oplocks!\n"); - server->rstate = SMB_RECV_DROP; -} - -/* - * Do some IO for one server. - */ -static void smbiod_doio(struct smb_sb_info *server) -{ - int result; - int maxwork = 7; - - if (server->state != CONN_VALID) - goto out; - - do { - result = smb_request_recv(server); - if (result < 0) { - server->state = CONN_INVALID; - smbiod_retry(server); - goto out; /* reconnecting is slow */ - } else if (server->rstate == SMB_RECV_REQUEST) - smbiod_handle_request(server); - } while (result > 0 && maxwork-- > 0); - - /* - * If there is more to read then we want to be sure to wake up again. - */ - if (server->state != CONN_VALID) - goto out; - if (smb_recv_available(server) > 0) - set_bit(SMBIOD_DATA_READY, &smbiod_flags); - - do { - result = smb_request_send_server(server); - if (result < 0) { - server->state = CONN_INVALID; - smbiod_retry(server); - goto out; /* reconnecting is slow */ - } - } while (result > 0); - - /* - * If the last request was not sent out we want to wake up again. - */ - if (!list_empty(&server->xmitq)) - set_bit(SMBIOD_DATA_READY, &smbiod_flags); - -out: - return; -} - -/* - * smbiod kernel thread - */ -static int smbiod(void *unused) -{ - VERBOSE("SMB Kernel thread starting (%d) ...\n", current->pid); - - for (;;) { - struct smb_sb_info *server; - struct list_head *pos, *n; - - /* FIXME: Use poll? */ - wait_event_interruptible(smbiod_wait, - test_bit(SMBIOD_DATA_READY, &smbiod_flags)); - if (signal_pending(current)) { - spin_lock(&servers_lock); - smbiod_state = SMBIOD_DEAD; - spin_unlock(&servers_lock); - break; - } - - clear_bit(SMBIOD_DATA_READY, &smbiod_flags); - - spin_lock(&servers_lock); - if (list_empty(&smb_servers)) { - smbiod_state = SMBIOD_DEAD; - spin_unlock(&servers_lock); - break; - } - - list_for_each_safe(pos, n, &smb_servers) { - server = list_entry(pos, struct smb_sb_info, entry); - VERBOSE("checking server %p\n", server); - - if (server->state == CONN_VALID) { - spin_unlock(&servers_lock); - - smb_lock_server(server); - smbiod_doio(server); - smb_unlock_server(server); - - spin_lock(&servers_lock); - } - } - spin_unlock(&servers_lock); - } - - VERBOSE("SMB Kernel thread exiting (%d) ...\n", current->pid); - module_put_and_exit(0); -} diff --git a/fs/smbfs/sock.c b/fs/smbfs/sock.c deleted file mode 100644 index e37fe4deebd0..000000000000 --- a/fs/smbfs/sock.c +++ /dev/null @@ -1,386 +0,0 @@ -/* - * sock.c - * - * Copyright (C) 1995, 1996 by Paal-Kr. Engstad and Volker Lendecke - * Copyright (C) 1997 by Volker Lendecke - * - * 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 - -#include -#include -#include - -#include -#include - -#include "smb_debug.h" -#include "proto.h" -#include "request.h" - - -static int -_recvfrom(struct socket *socket, unsigned char *ubuf, int size, unsigned flags) -{ - struct kvec iov = {ubuf, size}; - struct msghdr msg = {.msg_flags = flags}; - msg.msg_flags |= MSG_DONTWAIT | MSG_NOSIGNAL; - return kernel_recvmsg(socket, &msg, &iov, 1, size, msg.msg_flags); -} - -/* - * Return the server this socket belongs to - */ -static struct smb_sb_info * -server_from_socket(struct socket *socket) -{ - return socket->sk->sk_user_data; -} - -/* - * Called when there is data on the socket. - */ -void -smb_data_ready(struct sock *sk, int len) -{ - struct smb_sb_info *server = server_from_socket(sk->sk_socket); - void (*data_ready)(struct sock *, int) = server->data_ready; - - data_ready(sk, len); - VERBOSE("(%p, %d)\n", sk, len); - smbiod_wake_up(); -} - -int -smb_valid_socket(struct inode * inode) -{ - return (inode && S_ISSOCK(inode->i_mode) && - SOCKET_I(inode)->type == SOCK_STREAM); -} - -static struct socket * -server_sock(struct smb_sb_info *server) -{ - struct file *file; - - if (server && (file = server->sock_file)) - { -#ifdef SMBFS_PARANOIA - if (!smb_valid_socket(file->f_path.dentry->d_inode)) - PARANOIA("bad socket!\n"); -#endif - return SOCKET_I(file->f_path.dentry->d_inode); - } - return NULL; -} - -void -smb_close_socket(struct smb_sb_info *server) -{ - struct file * file = server->sock_file; - - if (file) { - struct socket *sock = server_sock(server); - - VERBOSE("closing socket %p\n", sock); - sock->sk->sk_data_ready = server->data_ready; - server->sock_file = NULL; - fput(file); - } -} - -static int -smb_get_length(struct socket *socket, unsigned char *header) -{ - int result; - - result = _recvfrom(socket, header, 4, MSG_PEEK); - if (result == -EAGAIN) - return -ENODATA; - if (result < 0) { - PARANOIA("recv error = %d\n", -result); - return result; - } - if (result < 4) - return -ENODATA; - - switch (header[0]) { - case 0x00: - case 0x82: - break; - - case 0x85: - DEBUG1("Got SESSION KEEP ALIVE\n"); - _recvfrom(socket, header, 4, 0); /* read away */ - return -ENODATA; - - default: - PARANOIA("Invalid NBT packet, code=%x\n", header[0]); - return -EIO; - } - - /* The length in the RFC NB header is the raw data length */ - return smb_len(header); -} - -int -smb_recv_available(struct smb_sb_info *server) -{ - mm_segment_t oldfs; - int avail, err; - struct socket *sock = server_sock(server); - - oldfs = get_fs(); - set_fs(get_ds()); - err = sock->ops->ioctl(sock, SIOCINQ, (unsigned long) &avail); - set_fs(oldfs); - return (err >= 0) ? avail : err; -} - -/* - * Adjust the kvec to move on 'n' bytes (from nfs/sunrpc) - */ -static int -smb_move_iov(struct kvec **data, size_t *num, struct kvec *vec, unsigned amount) -{ - struct kvec *iv = *data; - int i; - int len; - - /* - * Eat any sent kvecs - */ - while (iv->iov_len <= amount) { - amount -= iv->iov_len; - iv++; - (*num)--; - } - - /* - * And chew down the partial one - */ - vec[0].iov_len = iv->iov_len-amount; - vec[0].iov_base =((unsigned char *)iv->iov_base)+amount; - iv++; - - len = vec[0].iov_len; - - /* - * And copy any others - */ - for (i = 1; i < *num; i++) { - vec[i] = *iv++; - len += vec[i].iov_len; - } - - *data = vec; - return len; -} - -/* - * smb_receive_header - * Only called by the smbiod thread. - */ -int -smb_receive_header(struct smb_sb_info *server) -{ - struct socket *sock; - int result = 0; - unsigned char peek_buf[4]; - - result = -EIO; - sock = server_sock(server); - if (!sock) - goto out; - if (sock->sk->sk_state != TCP_ESTABLISHED) - goto out; - - if (!server->smb_read) { - result = smb_get_length(sock, peek_buf); - if (result < 0) { - if (result == -ENODATA) - result = 0; - goto out; - } - server->smb_len = result + 4; - - if (server->smb_len < SMB_HEADER_LEN) { - PARANOIA("short packet: %d\n", result); - server->rstate = SMB_RECV_DROP; - result = -EIO; - goto out; - } - if (server->smb_len > SMB_MAX_PACKET_SIZE) { - PARANOIA("long packet: %d\n", result); - server->rstate = SMB_RECV_DROP; - result = -EIO; - goto out; - } - } - - result = _recvfrom(sock, server->header + server->smb_read, - SMB_HEADER_LEN - server->smb_read, 0); - VERBOSE("_recvfrom: %d\n", result); - if (result < 0) { - VERBOSE("receive error: %d\n", result); - goto out; - } - server->smb_read += result; - - if (server->smb_read == SMB_HEADER_LEN) - server->rstate = SMB_RECV_HCOMPLETE; -out: - return result; -} - -static char drop_buffer[PAGE_SIZE]; - -/* - * smb_receive_drop - read and throw away the data - * Only called by the smbiod thread. - * - * FIXME: we are in the kernel, could we just tell the socket that we want - * to drop stuff from the buffer? - */ -int -smb_receive_drop(struct smb_sb_info *server) -{ - struct socket *sock; - unsigned int flags; - struct kvec iov; - struct msghdr msg; - int rlen = smb_len(server->header) - server->smb_read + 4; - int result = -EIO; - - if (rlen > PAGE_SIZE) - rlen = PAGE_SIZE; - - sock = server_sock(server); - if (!sock) - goto out; - if (sock->sk->sk_state != TCP_ESTABLISHED) - goto out; - - flags = MSG_DONTWAIT | MSG_NOSIGNAL; - iov.iov_base = drop_buffer; - iov.iov_len = PAGE_SIZE; - msg.msg_flags = flags; - msg.msg_name = NULL; - msg.msg_namelen = 0; - msg.msg_control = NULL; - - result = kernel_recvmsg(sock, &msg, &iov, 1, rlen, flags); - - VERBOSE("read: %d\n", result); - if (result < 0) { - VERBOSE("receive error: %d\n", result); - goto out; - } - server->smb_read += result; - - if (server->smb_read >= server->smb_len) - server->rstate = SMB_RECV_END; - -out: - return result; -} - -/* - * smb_receive - * Only called by the smbiod thread. - */ -int -smb_receive(struct smb_sb_info *server, struct smb_request *req) -{ - struct socket *sock; - unsigned int flags; - struct kvec iov[4]; - struct kvec *p = req->rq_iov; - size_t num = req->rq_iovlen; - struct msghdr msg; - int rlen; - int result = -EIO; - - sock = server_sock(server); - if (!sock) - goto out; - if (sock->sk->sk_state != TCP_ESTABLISHED) - goto out; - - flags = MSG_DONTWAIT | MSG_NOSIGNAL; - msg.msg_flags = flags; - msg.msg_name = NULL; - msg.msg_namelen = 0; - msg.msg_control = NULL; - - /* Dont repeat bytes and count available bufferspace */ - rlen = min_t(int, smb_move_iov(&p, &num, iov, req->rq_bytes_recvd), - (req->rq_rlen - req->rq_bytes_recvd)); - - result = kernel_recvmsg(sock, &msg, p, num, rlen, flags); - - VERBOSE("read: %d\n", result); - if (result < 0) { - VERBOSE("receive error: %d\n", result); - goto out; - } - req->rq_bytes_recvd += result; - server->smb_read += result; - -out: - return result; -} - -/* - * Try to send a SMB request. This may return after sending only parts of the - * request. SMB_REQ_TRANSMITTED will be set if a request was fully sent. - * - * Parts of this was taken from xprt_sendmsg from net/sunrpc/xprt.c - */ -int -smb_send_request(struct smb_request *req) -{ - struct smb_sb_info *server = req->rq_server; - struct socket *sock; - struct msghdr msg = {.msg_flags = MSG_NOSIGNAL | MSG_DONTWAIT}; - int slen = req->rq_slen - req->rq_bytes_sent; - int result = -EIO; - struct kvec iov[4]; - struct kvec *p = req->rq_iov; - size_t num = req->rq_iovlen; - - sock = server_sock(server); - if (!sock) - goto out; - if (sock->sk->sk_state != TCP_ESTABLISHED) - goto out; - - /* Dont repeat bytes */ - if (req->rq_bytes_sent) - smb_move_iov(&p, &num, iov, req->rq_bytes_sent); - - result = kernel_sendmsg(sock, &msg, p, num, slen); - - if (result >= 0) { - req->rq_bytes_sent += result; - if (req->rq_bytes_sent >= req->rq_slen) - req->rq_flags |= SMB_REQ_TRANSMITTED; - } -out: - return result; -} diff --git a/fs/smbfs/symlink.c b/fs/smbfs/symlink.c deleted file mode 100644 index 00b2909bd469..000000000000 --- a/fs/smbfs/symlink.c +++ /dev/null @@ -1,68 +0,0 @@ -/* - * 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_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); -} - -static void *smb_follow_link(struct dentry *dentry, struct nameidata *nd) -{ - char *link = __getname(); - DEBUG1("followlink of %s/%s\n", DENTRY_PATH(dentry)); - - if (!link) { - link = ERR_PTR(-ENOMEM); - } else { - int len = smb_proc_read_link(server_from_dentry(dentry), - dentry, link, PATH_MAX - 1); - if (len < 0) { - __putname(link); - link = ERR_PTR(len); - } else { - link[len] = 0; - } - } - nd_set_link(nd, link); - return NULL; -} - -static void smb_put_link(struct dentry *dentry, struct nameidata *nd, void *p) -{ - char *s = nd_get_link(nd); - if (!IS_ERR(s)) - __putname(s); -} - -const struct inode_operations smb_link_inode_operations = -{ - .readlink = generic_readlink, - .follow_link = smb_follow_link, - .put_link = smb_put_link, -}; diff --git a/include/linux/Kbuild b/include/linux/Kbuild index 626b629429ff..98d520d371ed 100644 --- a/include/linux/Kbuild +++ b/include/linux/Kbuild @@ -326,10 +326,6 @@ header-y += serio.h header-y += shm.h header-y += signal.h header-y += signalfd.h -header-y += smb.h -header-y += smb_fs.h -header-y += smb_mount.h -header-y += smbno.h header-y += snmp.h header-y += socket.h header-y += sockios.h diff --git a/include/linux/smb.h b/include/linux/smb.h deleted file mode 100644 index 82fefddc5987..000000000000 --- a/include/linux/smb.h +++ /dev/null @@ -1,118 +0,0 @@ -/* - * smb.h - * - * Copyright (C) 1995, 1996 by Paal-Kr. Engstad and Volker Lendecke - * Copyright (C) 1997 by Volker Lendecke - * - */ - -#ifndef _LINUX_SMB_H -#define _LINUX_SMB_H - -#include -#include -#ifdef __KERNEL__ -#include -#endif - -enum smb_protocol { - SMB_PROTOCOL_NONE, - SMB_PROTOCOL_CORE, - SMB_PROTOCOL_COREPLUS, - SMB_PROTOCOL_LANMAN1, - SMB_PROTOCOL_LANMAN2, - SMB_PROTOCOL_NT1 -}; - -enum smb_case_hndl { - SMB_CASE_DEFAULT, - SMB_CASE_LOWER, - SMB_CASE_UPPER -}; - -struct smb_dskattr { - __u16 total; - __u16 allocblocks; - __u16 blocksize; - __u16 free; -}; - -struct smb_conn_opt { - - /* The socket */ - unsigned int fd; - - enum smb_protocol protocol; - enum smb_case_hndl case_handling; - - /* Connection-Options */ - - __u32 max_xmit; - __u16 server_uid; - __u16 tid; - - /* The following are LANMAN 1.0 options */ - __u16 secmode; - __u16 maxmux; - __u16 maxvcs; - __u16 rawmode; - __u32 sesskey; - - /* The following are NT LM 0.12 options */ - __u32 maxraw; - __u32 capabilities; - __s16 serverzone; -}; - -#ifdef __KERNEL__ - -#define SMB_NLS_MAXNAMELEN 20 -struct smb_nls_codepage { - char local_name[SMB_NLS_MAXNAMELEN]; - char remote_name[SMB_NLS_MAXNAMELEN]; -}; - - -#define SMB_MAXNAMELEN 255 -#define SMB_MAXPATHLEN 1024 - -/* - * Contains all relevant data on a SMB networked file. - */ -struct smb_fattr { - __u16 attr; - - unsigned long f_ino; - umode_t f_mode; - nlink_t f_nlink; - uid_t f_uid; - gid_t f_gid; - dev_t f_rdev; - loff_t f_size; - struct timespec f_atime; - struct timespec f_mtime; - struct timespec f_ctime; - unsigned long f_blocks; - int f_unix; -}; - -enum smb_conn_state { - CONN_VALID, /* everything's fine */ - CONN_INVALID, /* Something went wrong, but did not - try to reconnect yet. */ - CONN_RETRIED, /* Tried a reconnection, but was refused */ - CONN_RETRYING /* Currently trying to reconnect */ -}; - -#define SMB_HEADER_LEN 37 /* includes everything up to, but not - * including smb_bcc */ - -#define SMB_INITIAL_PACKET_SIZE 4000 -#define SMB_MAX_PACKET_SIZE 32768 - -/* reserve this much space for trans2 parameters. Shouldn't have to be more - than 10 or so, but OS/2 seems happier like this. */ -#define SMB_TRANS2_MAX_PARAM 64 - -#endif -#endif diff --git a/include/linux/smb_fs.h b/include/linux/smb_fs.h deleted file mode 100644 index 923cd8a247b1..000000000000 --- a/include/linux/smb_fs.h +++ /dev/null @@ -1,153 +0,0 @@ -/* - * smb_fs.h - * - * Copyright (C) 1995 by Paal-Kr. Engstad and Volker Lendecke - * Copyright (C) 1997 by Volker Lendecke - * - */ - -#ifndef _LINUX_SMB_FS_H -#define _LINUX_SMB_FS_H - -#include - -/* - * ioctl commands - */ -#define SMB_IOC_GETMOUNTUID _IOR('u', 1, __kernel_old_uid_t) -#define SMB_IOC_NEWCONN _IOW('u', 2, struct smb_conn_opt) - -/* __kernel_uid_t can never change, so we have to use __kernel_uid32_t */ -#define SMB_IOC_GETMOUNTUID32 _IOR('u', 3, __kernel_uid32_t) - - -#ifdef __KERNEL__ -#include -#include - -#include -#include -#include -#include -#include -#include - -static inline struct smb_sb_info *SMB_SB(struct super_block *sb) -{ - return sb->s_fs_info; -} - -static inline struct smb_inode_info *SMB_I(struct inode *inode) -{ - return container_of(inode, struct smb_inode_info, vfs_inode); -} - -/* macro names are short for word, double-word, long value (?) */ -#define WVAL(buf, pos) (get_unaligned_le16((u8 *)(buf) + (pos))) -#define DVAL(buf, pos) (get_unaligned_le32((u8 *)(buf) + (pos))) -#define LVAL(buf, pos) (get_unaligned_le64((u8 *)(buf) + (pos))) - -#define WSET(buf, pos, val) put_unaligned_le16((val), (u8 *)(buf) + (pos)) -#define DSET(buf, pos, val) put_unaligned_le32((val), (u8 *)(buf) + (pos)) -#define LSET(buf, pos, val) put_unaligned_le64((val), (u8 *)(buf) + (pos)) - -/* where to find the base of the SMB packet proper */ -#define smb_base(buf) ((u8 *)(((u8 *)(buf))+4)) - -/* - * Flags for the in-memory inode - */ -#define SMB_F_LOCALWRITE 0x02 /* file modified locally */ - - -/* NT1 protocol capability bits */ -#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 ... */ - - -/* - * This is the time we allow an inode, dentry or dir cache to live. It is bad - * for performance to have shorter ttl on an inode than on the cache. It can - * cause refresh on each inode for a dir listing ... one-by-one - */ -#define SMB_MAX_AGE(server) (((server)->mnt->ttl * HZ) / 1000) - -static inline void -smb_age_dentry(struct smb_sb_info *server, struct dentry *dentry) -{ - dentry->d_time = jiffies - SMB_MAX_AGE(server); -} - -struct smb_cache_head { - time_t mtime; /* unused */ - unsigned long time; /* cache age */ - unsigned long end; /* last valid fpos in cache */ - int eof; -}; - -#define SMB_DIRCACHE_SIZE ((int)(PAGE_CACHE_SIZE/sizeof(struct dentry *))) -union smb_dir_cache { - struct smb_cache_head head; - struct dentry *dentry[SMB_DIRCACHE_SIZE]; -}; - -#define SMB_FIRSTCACHE_SIZE ((int)((SMB_DIRCACHE_SIZE * \ - sizeof(struct dentry *) - sizeof(struct smb_cache_head)) / \ - sizeof(struct dentry *))) - -#define SMB_DIRCACHE_START (SMB_DIRCACHE_SIZE - SMB_FIRSTCACHE_SIZE) - -struct smb_cache_control { - struct smb_cache_head head; - struct page *page; - union smb_dir_cache *cache; - unsigned long fpos, ofs; - int filled, valid, idx; -}; - -#define SMB_OPS_NUM_STATIC 5 -struct smb_ops { - int (*read)(struct inode *inode, loff_t offset, int count, - char *data); - int (*write)(struct inode *inode, loff_t offset, int count, const - char *data); - int (*readdir)(struct file *filp, void *dirent, filldir_t filldir, - struct smb_cache_control *ctl); - - int (*getattr)(struct smb_sb_info *server, struct dentry *dir, - struct smb_fattr *fattr); - /* int (*setattr)(...); */ /* setattr is really icky! */ - - int (*truncate)(struct inode *inode, loff_t length); - - - /* --- --- --- end of "static" entries --- --- --- */ - - int (*convert)(unsigned char *output, int olen, - const unsigned char *input, int ilen, - struct nls_table *nls_from, - struct nls_table *nls_to); -}; - -static inline int -smb_is_open(struct inode *i) -{ - return (SMB_I(i)->open == server_from_inode(i)->generation); -} - -extern void smb_install_null_ops(struct smb_ops *); -#endif /* __KERNEL__ */ - -#endif /* _LINUX_SMB_FS_H */ diff --git a/include/linux/smb_fs_i.h b/include/linux/smb_fs_i.h deleted file mode 100644 index 8ccf4eca2c3d..000000000000 --- a/include/linux/smb_fs_i.h +++ /dev/null @@ -1,37 +0,0 @@ -/* - * smb_fs_i.h - * - * Copyright (C) 1995 by Paal-Kr. Engstad and Volker Lendecke - * Copyright (C) 1997 by Volker Lendecke - * - */ - -#ifndef _LINUX_SMB_FS_I -#define _LINUX_SMB_FS_I - -#include -#include - -/* - * smb fs inode data (in memory only) - */ -struct smb_inode_info { - - /* - * file handles are local to a connection. A file is open if - * (open == generation). - */ - unsigned int open; /* open generation */ - __u16 fileid; /* What id to handle a file with? */ - __u16 attr; /* Attribute fields, DOS value */ - - __u16 access; /* Access mode */ - __u16 flags; - unsigned long oldmtime; /* last time refreshed */ - unsigned long closed; /* timestamp when closed */ - unsigned openers; /* number of fileid users */ - - struct inode vfs_inode; /* must be at the end */ -}; - -#endif diff --git a/include/linux/smb_fs_sb.h b/include/linux/smb_fs_sb.h deleted file mode 100644 index bb947dd1fba9..000000000000 --- a/include/linux/smb_fs_sb.h +++ /dev/null @@ -1,100 +0,0 @@ -/* - * smb_fs_sb.h - * - * Copyright (C) 1995 by Paal-Kr. Engstad and Volker Lendecke - * Copyright (C) 1997 by Volker Lendecke - * - */ - -#ifndef _SMB_FS_SB -#define _SMB_FS_SB - -#include -#include -#include - -/* - * Upper limit on the total number of active smb_request structs. - */ -#define MAX_REQUEST_HARD 256 - -enum smb_receive_state { - SMB_RECV_START, /* No data read, looking for length + sig */ - SMB_RECV_HEADER, /* Reading the header data */ - SMB_RECV_HCOMPLETE, /* Done with the header */ - SMB_RECV_PARAM, /* Reading parameter words */ - SMB_RECV_DATA, /* Reading data bytes */ - SMB_RECV_END, /* End of request */ - SMB_RECV_DROP, /* Dropping this SMB */ - SMB_RECV_REQUEST, /* Received a request and not a reply */ -}; - -/* structure access macros */ -#define server_from_inode(inode) SMB_SB((inode)->i_sb) -#define server_from_dentry(dentry) SMB_SB((dentry)->d_sb) -#define SB_of(server) ((server)->super_block) - -struct smb_sb_info { - /* List of all smbfs superblocks */ - struct list_head entry; - - enum smb_conn_state state; - struct file * sock_file; - int conn_error; - enum smb_receive_state rstate; - - atomic_t nr_requests; - struct list_head xmitq; - struct list_head recvq; - u16 mid; - - struct smb_mount_data_kernel *mnt; - - /* Connections are counted. Each time a new socket arrives, - * generation is incremented. - */ - unsigned int generation; - struct pid *conn_pid; - struct smb_conn_opt opt; - wait_queue_head_t conn_wq; - int conn_complete; - struct semaphore sem; - - unsigned char header[SMB_HEADER_LEN + 20*2 + 2]; - u32 header_len; - u32 smb_len; - u32 smb_read; - - /* We use our own data_ready callback, but need the original one */ - void *data_ready; - - /* nls pointers for codepage conversions */ - struct nls_table *remote_nls; - struct nls_table *local_nls; - - struct smb_ops *ops; - - struct super_block *super_block; - - struct backing_dev_info bdi; -}; - -static inline int -smb_lock_server_interruptible(struct smb_sb_info *server) -{ - return down_interruptible(&(server->sem)); -} - -static inline void -smb_lock_server(struct smb_sb_info *server) -{ - down(&(server->sem)); -} - -static inline void -smb_unlock_server(struct smb_sb_info *server) -{ - up(&(server->sem)); -} - -#endif diff --git a/include/linux/smb_mount.h b/include/linux/smb_mount.h deleted file mode 100644 index d10f00cb5703..000000000000 --- a/include/linux/smb_mount.h +++ /dev/null @@ -1,65 +0,0 @@ -/* - * smb_mount.h - * - * Copyright (C) 1995, 1996 by Paal-Kr. Engstad and Volker Lendecke - * Copyright (C) 1997 by Volker Lendecke - * - */ - -#ifndef _LINUX_SMB_MOUNT_H -#define _LINUX_SMB_MOUNT_H - -#include - -#define SMB_MOUNT_VERSION 6 - -struct smb_mount_data { - int version; - __kernel_uid_t mounted_uid; /* Who may umount() this filesystem? */ - __kernel_uid_t uid; - __kernel_gid_t gid; - __kernel_mode_t file_mode; - __kernel_mode_t dir_mode; -}; - - -#ifdef __KERNEL__ - -/* "vers" in big-endian */ -#define SMB_MOUNT_ASCII 0x76657273 - -#define SMB_MOUNT_OLDVERSION 6 -#undef SMB_MOUNT_VERSION -#define SMB_MOUNT_VERSION 7 - -/* flags */ -#define SMB_MOUNT_WIN95 0x0001 /* Win 95 server */ -#define SMB_MOUNT_OLDATTR 0x0002 /* Use core getattr (Win 95 speedup) */ -#define SMB_MOUNT_DIRATTR 0x0004 /* Use find_first for getattr */ -#define SMB_MOUNT_CASE 0x0008 /* Be case sensitive */ -#define SMB_MOUNT_UNICODE 0x0010 /* Server talks unicode */ -#define SMB_MOUNT_UID 0x0020 /* Use user specified uid */ -#define SMB_MOUNT_GID 0x0040 /* Use user specified gid */ -#define SMB_MOUNT_FMODE 0x0080 /* Use user specified file mode */ -#define SMB_MOUNT_DMODE 0x0100 /* Use user specified dir mode */ - -struct smb_mount_data_kernel { - int version; - - uid_t mounted_uid; /* Who may umount() this filesystem? */ - uid_t uid; - gid_t gid; - mode_t file_mode; - mode_t dir_mode; - - u32 flags; - - /* maximum age in jiffies (inode, dentry and dircache) */ - int ttl; - - struct smb_nls_codepage codepage; -}; - -#endif - -#endif diff --git a/include/linux/smbno.h b/include/linux/smbno.h deleted file mode 100644 index f99e02d9ffe2..000000000000 --- a/include/linux/smbno.h +++ /dev/null @@ -1,363 +0,0 @@ -#ifndef _SMBNO_H_ -#define _SMBNO_H_ - -/* these define the attribute byte as seen by DOS */ -#define aRONLY (1L<<0) -#define aHIDDEN (1L<<1) -#define aSYSTEM (1L<<2) -#define aVOLID (1L<<3) -#define aDIR (1L<<4) -#define aARCH (1L<<5) - -/* error classes */ -#define SUCCESS 0 /* The request was successful. */ -#define ERRDOS 0x01 /* Error is from the core DOS operating system set. */ -#define ERRSRV 0x02 /* Error is generated by the server network file manager.*/ -#define ERRHRD 0x03 /* Error is an hardware error. */ -#define ERRCMD 0xFF /* Command was not in the "SMB" format. */ - -/* SMB X/Open error codes for the ERRdos error class */ - -#define ERRbadfunc 1 /* Invalid function (or system call) */ -#define ERRbadfile 2 /* File not found (pathname error) */ -#define ERRbadpath 3 /* Directory not found */ -#define ERRnofids 4 /* Too many open files */ -#define ERRnoaccess 5 /* Access denied */ -#define ERRbadfid 6 /* Invalid fid */ -#define ERRbadmcb 7 /* Memory control blocks destroyed */ -#define ERRnomem 8 /* Out of memory */ -#define ERRbadmem 9 /* Invalid memory block address */ -#define ERRbadenv 10 /* Invalid environment */ -#define ERRbadformat 11 /* Invalid format */ -#define ERRbadaccess 12 /* Invalid open mode */ -#define ERRbaddata 13 /* Invalid data (only from ioctl call) */ -#define ERRres 14 /* reserved */ -#define ERRbaddrive 15 /* Invalid drive */ -#define ERRremcd 16 /* Attempt to delete current directory */ -#define ERRdiffdevice 17 /* rename/move across different filesystems */ -#define ERRnofiles 18 /* no more files found in file search */ -#define ERRbadshare 32 /* Share mode on file conflict with open mode */ -#define ERRlock 33 /* Lock request conflicts with existing lock */ -#define ERRfilexists 80 /* File in operation already exists */ -#define ERRbadpipe 230 /* Named pipe invalid */ -#define ERRpipebusy 231 /* All instances of pipe are busy */ -#define ERRpipeclosing 232 /* named pipe close in progress */ -#define ERRnotconnected 233 /* No process on other end of named pipe */ -#define ERRmoredata 234 /* More data to be returned */ - -#define ERROR_INVALID_PARAMETER 87 -#define ERROR_DISK_FULL 112 -#define ERROR_INVALID_NAME 123 -#define ERROR_DIR_NOT_EMPTY 145 -#define ERROR_NOT_LOCKED 158 -#define ERROR_ALREADY_EXISTS 183 /* see also 80 ? */ -#define ERROR_EAS_DIDNT_FIT 275 /* Extended attributes didn't fit */ -#define ERROR_EAS_NOT_SUPPORTED 282 /* Extended attributes not supported */ - -/* Error codes for the ERRSRV class */ - -#define ERRerror 1 /* Non specific error code */ -#define ERRbadpw 2 /* Bad password */ -#define ERRbadtype 3 /* reserved */ -#define ERRaccess 4 /* No permissions to do the requested operation */ -#define ERRinvnid 5 /* tid invalid */ -#define ERRinvnetname 6 /* Invalid servername */ -#define ERRinvdevice 7 /* Invalid device */ -#define ERRqfull 49 /* Print queue full */ -#define ERRqtoobig 50 /* Queued item too big */ -#define ERRinvpfid 52 /* Invalid print file in smb_fid */ -#define ERRsmbcmd 64 /* Unrecognised command */ -#define ERRsrverror 65 /* smb server internal error */ -#define ERRfilespecs 67 /* fid and pathname invalid combination */ -#define ERRbadlink 68 /* reserved */ -#define ERRbadpermits 69 /* Access specified for a file is not valid */ -#define ERRbadpid 70 /* reserved */ -#define ERRsetattrmode 71 /* attribute mode invalid */ -#define ERRpaused 81 /* Message server paused */ -#define ERRmsgoff 82 /* Not receiving messages */ -#define ERRnoroom 83 /* No room for message */ -#define ERRrmuns 87 /* too many remote usernames */ -#define ERRtimeout 88 /* operation timed out */ -#define ERRnoresource 89 /* No resources currently available for request. */ -#define ERRtoomanyuids 90 /* too many userids */ -#define ERRbaduid 91 /* bad userid */ -#define ERRuseMPX 250 /* temporarily unable to use raw mode, use MPX mode */ -#define ERRuseSTD 251 /* temporarily unable to use raw mode, use std.mode */ -#define ERRcontMPX 252 /* resume MPX mode */ -#define ERRbadPW /* reserved */ -#define ERRnosupport 0xFFFF - -/* Error codes for the ERRHRD class */ - -#define ERRnowrite 19 /* read only media */ -#define ERRbadunit 20 /* Unknown device */ -#define ERRnotready 21 /* Drive not ready */ -#define ERRbadcmd 22 /* Unknown command */ -#define ERRdata 23 /* Data (CRC) error */ -#define ERRbadreq 24 /* Bad request structure length */ -#define ERRseek 25 -#define ERRbadmedia 26 -#define ERRbadsector 27 -#define ERRnopaper 28 -#define ERRwrite 29 /* write fault */ -#define ERRread 30 /* read fault */ -#define ERRgeneral 31 /* General hardware failure */ -#define ERRwrongdisk 34 -#define ERRFCBunavail 35 -#define ERRsharebufexc 36 /* share buffer exceeded */ -#define ERRdiskfull 39 - -/* - * Access modes when opening a file - */ -#define SMB_ACCMASK 0x0003 -#define SMB_O_RDONLY 0x0000 -#define SMB_O_WRONLY 0x0001 -#define SMB_O_RDWR 0x0002 - -/* offsets into message for common items */ -#define smb_com 8 -#define smb_rcls 9 -#define smb_reh 10 -#define smb_err 11 -#define smb_flg 13 -#define smb_flg2 14 -#define smb_reb 13 -#define smb_tid 28 -#define smb_pid 30 -#define smb_uid 32 -#define smb_mid 34 -#define smb_wct 36 -#define smb_vwv 37 -#define smb_vwv0 37 -#define smb_vwv1 39 -#define smb_vwv2 41 -#define smb_vwv3 43 -#define smb_vwv4 45 -#define smb_vwv5 47 -#define smb_vwv6 49 -#define smb_vwv7 51 -#define smb_vwv8 53 -#define smb_vwv9 55 -#define smb_vwv10 57 -#define smb_vwv11 59 -#define smb_vwv12 61 -#define smb_vwv13 63 -#define smb_vwv14 65 - -/* these are the trans2 sub fields for primary requests */ -#define smb_tpscnt smb_vwv0 -#define smb_tdscnt smb_vwv1 -#define smb_mprcnt smb_vwv2 -#define smb_mdrcnt smb_vwv3 -#define smb_msrcnt smb_vwv4 -#define smb_flags smb_vwv5 -#define smb_timeout smb_vwv6 -#define smb_pscnt smb_vwv9 -#define smb_psoff smb_vwv10 -#define smb_dscnt smb_vwv11 -#define smb_dsoff smb_vwv12 -#define smb_suwcnt smb_vwv13 -#define smb_setup smb_vwv14 -#define smb_setup0 smb_setup -#define smb_setup1 (smb_setup+2) -#define smb_setup2 (smb_setup+4) - -/* these are for the secondary requests */ -#define smb_spscnt smb_vwv2 -#define smb_spsoff smb_vwv3 -#define smb_spsdisp smb_vwv4 -#define smb_sdscnt smb_vwv5 -#define smb_sdsoff smb_vwv6 -#define smb_sdsdisp smb_vwv7 -#define smb_sfid smb_vwv8 - -/* and these for responses */ -#define smb_tprcnt smb_vwv0 -#define smb_tdrcnt smb_vwv1 -#define smb_prcnt smb_vwv3 -#define smb_proff smb_vwv4 -#define smb_prdisp smb_vwv5 -#define smb_drcnt smb_vwv6 -#define smb_droff smb_vwv7 -#define smb_drdisp smb_vwv8 - -/* the complete */ -#define SMBmkdir 0x00 /* create directory */ -#define SMBrmdir 0x01 /* delete directory */ -#define SMBopen 0x02 /* open file */ -#define SMBcreate 0x03 /* create file */ -#define SMBclose 0x04 /* close file */ -#define SMBflush 0x05 /* flush file */ -#define SMBunlink 0x06 /* delete file */ -#define SMBmv 0x07 /* rename file */ -#define SMBgetatr 0x08 /* get file attributes */ -#define SMBsetatr 0x09 /* set file attributes */ -#define SMBread 0x0A /* read from file */ -#define SMBwrite 0x0B /* write to file */ -#define SMBlock 0x0C /* lock byte range */ -#define SMBunlock 0x0D /* unlock byte range */ -#define SMBctemp 0x0E /* create temporary file */ -#define SMBmknew 0x0F /* make new file */ -#define SMBchkpth 0x10 /* check directory path */ -#define SMBexit 0x11 /* process exit */ -#define SMBlseek 0x12 /* seek */ -#define SMBtcon 0x70 /* tree connect */ -#define SMBtconX 0x75 /* tree connect and X*/ -#define SMBtdis 0x71 /* tree disconnect */ -#define SMBnegprot 0x72 /* negotiate protocol */ -#define SMBdskattr 0x80 /* get disk attributes */ -#define SMBsearch 0x81 /* search directory */ -#define SMBsplopen 0xC0 /* open print spool file */ -#define SMBsplwr 0xC1 /* write to print spool file */ -#define SMBsplclose 0xC2 /* close print spool file */ -#define SMBsplretq 0xC3 /* return print queue */ -#define SMBsends 0xD0 /* send single block message */ -#define SMBsendb 0xD1 /* send broadcast message */ -#define SMBfwdname 0xD2 /* forward user name */ -#define SMBcancelf 0xD3 /* cancel forward */ -#define SMBgetmac 0xD4 /* get machine name */ -#define SMBsendstrt 0xD5 /* send start of multi-block message */ -#define SMBsendend 0xD6 /* send end of multi-block message */ -#define SMBsendtxt 0xD7 /* send text of multi-block message */ - -/* Core+ protocol */ -#define SMBlockread 0x13 /* Lock a range and read */ -#define SMBwriteunlock 0x14 /* Unlock a range then write */ -#define SMBreadbraw 0x1a /* read a block of data with no smb header */ -#define SMBwritebraw 0x1d /* write a block of data with no smb header */ -#define SMBwritec 0x20 /* secondary write request */ -#define SMBwriteclose 0x2c /* write a file then close it */ - -/* dos extended protocol */ -#define SMBreadBraw 0x1A /* read block raw */ -#define SMBreadBmpx 0x1B /* read block multiplexed */ -#define SMBreadBs 0x1C /* read block (secondary response) */ -#define SMBwriteBraw 0x1D /* write block raw */ -#define SMBwriteBmpx 0x1E /* write block multiplexed */ -#define SMBwriteBs 0x1F /* write block (secondary request) */ -#define SMBwriteC 0x20 /* write complete response */ -#define SMBsetattrE 0x22 /* set file attributes expanded */ -#define SMBgetattrE 0x23 /* get file attributes expanded */ -#define SMBlockingX 0x24 /* lock/unlock byte ranges and X */ -#define SMBtrans 0x25 /* transaction - name, bytes in/out */ -#define SMBtranss 0x26 /* transaction (secondary request/response) */ -#define SMBioctl 0x27 /* IOCTL */ -#define SMBioctls 0x28 /* IOCTL (secondary request/response) */ -#define SMBcopy 0x29 /* copy */ -#define SMBmove 0x2A /* move */ -#define SMBecho 0x2B /* echo */ -#define SMBopenX 0x2D /* open and X */ -#define SMBreadX 0x2E /* read and X */ -#define SMBwriteX 0x2F /* write and X */ -#define SMBsesssetupX 0x73 /* Session Set Up & X (including User Logon) */ -#define SMBtconX 0x75 /* tree connect and X */ -#define SMBffirst 0x82 /* find first */ -#define SMBfunique 0x83 /* find unique */ -#define SMBfclose 0x84 /* find close */ -#define SMBinvalid 0xFE /* invalid command */ - - -/* Extended 2.0 protocol */ -#define SMBtrans2 0x32 /* TRANS2 protocol set */ -#define SMBtranss2 0x33 /* TRANS2 protocol set, secondary command */ -#define SMBfindclose 0x34 /* Terminate a TRANSACT2_FINDFIRST */ -#define SMBfindnclose 0x35 /* Terminate a TRANSACT2_FINDNOTIFYFIRST */ -#define SMBulogoffX 0x74 /* user logoff */ - -/* these are the TRANS2 sub commands */ -#define TRANSACT2_OPEN 0 -#define TRANSACT2_FINDFIRST 1 -#define TRANSACT2_FINDNEXT 2 -#define TRANSACT2_QFSINFO 3 -#define TRANSACT2_SETFSINFO 4 -#define TRANSACT2_QPATHINFO 5 -#define TRANSACT2_SETPATHINFO 6 -#define TRANSACT2_QFILEINFO 7 -#define TRANSACT2_SETFILEINFO 8 -#define TRANSACT2_FSCTL 9 -#define TRANSACT2_IOCTL 10 -#define TRANSACT2_FINDNOTIFYFIRST 11 -#define TRANSACT2_FINDNOTIFYNEXT 12 -#define TRANSACT2_MKDIR 13 - -/* Information Levels - Shared? */ -#define SMB_INFO_STANDARD 1 -#define SMB_INFO_QUERY_EA_SIZE 2 -#define SMB_INFO_QUERY_EAS_FROM_LIST 3 -#define SMB_INFO_QUERY_ALL_EAS 4 -#define SMB_INFO_IS_NAME_VALID 6 - -/* Information Levels - TRANSACT2_FINDFIRST */ -#define SMB_FIND_FILE_DIRECTORY_INFO 0x101 -#define SMB_FIND_FILE_FULL_DIRECTORY_INFO 0x102 -#define SMB_FIND_FILE_NAMES_INFO 0x103 -#define SMB_FIND_FILE_BOTH_DIRECTORY_INFO 0x104 - -/* Information Levels - TRANSACT2_QPATHINFO */ -#define SMB_QUERY_FILE_BASIC_INFO 0x101 -#define SMB_QUERY_FILE_STANDARD_INFO 0x102 -#define SMB_QUERY_FILE_EA_INFO 0x103 -#define SMB_QUERY_FILE_NAME_INFO 0x104 -#define SMB_QUERY_FILE_ALL_INFO 0x107 -#define SMB_QUERY_FILE_ALT_NAME_INFO 0x108 -#define SMB_QUERY_FILE_STREAM_INFO 0x109 -#define SMB_QUERY_FILE_COMPRESSION_INFO 0x10b - -/* Information Levels - TRANSACT2_SETFILEINFO */ -#define SMB_SET_FILE_BASIC_INFO 0x101 -#define SMB_SET_FILE_DISPOSITION_INFO 0x102 -#define SMB_SET_FILE_ALLOCATION_INFO 0x103 -#define SMB_SET_FILE_END_OF_FILE_INFO 0x104 - -/* smb_flg field flags */ -#define SMB_FLAGS_SUPPORT_LOCKREAD 0x01 -#define SMB_FLAGS_CLIENT_BUF_AVAIL 0x02 -#define SMB_FLAGS_RESERVED 0x04 -#define SMB_FLAGS_CASELESS_PATHNAMES 0x08 -#define SMB_FLAGS_CANONICAL_PATHNAMES 0x10 -#define SMB_FLAGS_REQUEST_OPLOCK 0x20 -#define SMB_FLAGS_REQUEST_BATCH_OPLOCK 0x40 -#define SMB_FLAGS_REPLY 0x80 - -/* smb_flg2 field flags (samba-2.2.0/source/include/smb.h) */ -#define SMB_FLAGS2_LONG_PATH_COMPONENTS 0x0001 -#define SMB_FLAGS2_EXTENDED_ATTRIBUTES 0x0002 -#define SMB_FLAGS2_DFS_PATHNAMES 0x1000 -#define SMB_FLAGS2_READ_PERMIT_NO_EXECUTE 0x2000 -#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 0xFFFFFFFFFFFFFFFFULL -#define SMB_SIZE_NO_CHANGE 0xFFFFFFFFFFFFFFFFULL - -/* 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_ */ -- cgit v1.2.3