OpenCores
URL https://opencores.org/ocsvn/or1k/or1k/trunk

Subversion Repositories or1k

Compare Revisions

  • This comparison shows the changes necessary to convert path
    /or1k/trunk/rc203soc/sw/uClinux/fs/smbfs
    from Rev 1628 to Rev 1765
    Reverse comparison

Rev 1628 → Rev 1765

/dir.c
0,0 → 1,860
/*
* dir.c
*
* Copyright (C) 1995, 1996 by Paal-Kr. Engstad and Volker Lendecke
*
*/
 
#include <linux/sched.h>
#include <linux/errno.h>
#include <linux/stat.h>
#include <linux/kernel.h>
#include <linux/malloc.h>
#include <linux/mm.h>
#include <linux/smb_fs.h>
#include <linux/smbno.h>
#include <asm/segment.h>
#include <asm/semaphore.h>
#include <linux/errno.h>
 
static int
smb_dir_read(struct inode *inode, struct file *filp, char *buf, int count);
 
static int
smb_readdir(struct inode *inode, struct file *filp,
void *dirent, filldir_t filldir);
 
static struct smb_inode_info *
smb_find_dir_inode(struct inode *parent, const char *name, int len);
 
static int
smb_lookup(struct inode *dir, const char *__name,
int len, struct inode **result);
 
static int
smb_create(struct inode *dir, const char *name, int len, int mode,
struct inode **result);
 
static int
smb_mkdir(struct inode *dir, const char *name, int len, int mode);
 
static int
smb_rmdir(struct inode *dir, const char *name, int len);
 
static int
smb_unlink(struct inode *dir, const char *name, int len);
 
static int
smb_rename(struct inode *old_dir, const char *old_name, int old_len,
struct inode *new_dir, const char *new_name, int new_len,
int must_be_dir);
 
static struct file_operations smb_dir_operations =
{
NULL, /* lseek - default */
smb_dir_read, /* read - bad */
NULL, /* write - bad */
smb_readdir, /* readdir */
NULL, /* select - default */
smb_ioctl, /* ioctl - default */
NULL, /* mmap */
NULL, /* no special open code */
NULL, /* no special release code */
NULL /* fsync */
};
 
struct inode_operations smb_dir_inode_operations =
{
&smb_dir_operations, /* default directory file ops */
smb_create, /* create */
smb_lookup, /* lookup */
NULL, /* link */
smb_unlink, /* unlink */
NULL, /* symlink */
smb_mkdir, /* mkdir */
smb_rmdir, /* rmdir */
NULL, /* mknod */
smb_rename, /* rename */
NULL, /* readlink */
NULL, /* follow_link */
NULL, /* readpage */
NULL, /* writepage */
NULL, /* bmap */
NULL, /* truncate */
NULL, /* permission */
NULL /* smap */
};
 
static int
strncasecmp(const char *s1, const char *s2, int len)
{
int result = 0;
 
for (; len > 0; len -= 1)
{
char c1, c2;
 
c1 = (*s1 >= 'a' && *s1 <= 'z') ? *s1 - ('a' - 'A') : *s1;
c2 = (*s2 >= 'a' && *s2 <= 'z') ? *s2 - ('a' - 'A') : *s2;
s1 += 1;
s2 += 1;
 
if ((result = c1 - c2) != 0 || c1 == 0)
{
return result;
}
}
return result;
}
 
struct smb_inode_info *
smb_find_inode(struct smb_server *server, ino_t ino)
{
struct smb_inode_info *root = &(server->root);
struct smb_inode_info *this = root;
 
do
{
if (ino == smb_info_ino(this))
{
return this;
}
this = this->next;
}
while (this != root);
 
return NULL;
}
 
static ino_t
smb_fresh_inodes(struct smb_server *server, int no)
{
static ino_t seed = 1;
struct smb_inode_info *root = &(server->root);
struct smb_inode_info *this;
 
retry:
if (seed + no <= no)
{
/* avoid inode number of 0 at wrap-around */
seed += no;
}
this = root;
do
{
/* We assume that ino_t is unsigned! */
if (this->finfo.f_ino - seed < no)
{
seed += no;
goto retry;
}
this = this->next;
}
while (this != root);
 
seed += no;
 
return seed - no;
}
 
static int
smb_dir_read(struct inode *inode, struct file *filp, char *buf, int count)
{
return -EISDIR;
}
 
 
static unsigned long c_ino = 0;
static kdev_t c_dev;
static int c_size;
static int c_seen_eof;
static int c_last_returned_index;
static struct smb_dirent *c_entry = NULL;
 
static struct smb_dirent *
smb_search_in_cache(struct inode *dir, unsigned long f_pos)
{
int i;
 
if ((dir->i_dev != c_dev) || (dir->i_ino != c_ino))
{
return NULL;
}
for (i = 0; i < c_size; i++)
{
if (f_pos == c_entry[i].f_pos)
{
c_last_returned_index = i;
return &(c_entry[i]);
}
}
return NULL;
}
 
static int
smb_refill_dir_cache(struct smb_server *server, struct inode *dir,
unsigned long f_pos)
{
int result;
static struct semaphore sem = MUTEX;
int i;
ino_t ino;
 
do
{
down(&sem);
result = smb_proc_readdir(server, dir, f_pos,
SMB_READDIR_CACHE_SIZE, c_entry);
 
if (result <= 0)
{
smb_invalid_dir_cache(dir->i_ino);
up(&sem);
return result;
}
c_seen_eof = (result < SMB_READDIR_CACHE_SIZE);
c_dev = dir->i_dev;
c_ino = dir->i_ino;
c_size = result;
c_last_returned_index = 0;
 
ino = smb_fresh_inodes(server, c_size);
for (i = 0; i < c_size; i++)
{
c_entry[i].f_ino = ino;
ino += 1;
}
 
up(&sem);
 
}
while ((c_dev != dir->i_dev) || (c_ino != dir->i_ino));
 
return result;
}
 
static int
smb_readdir(struct inode *dir, struct file *filp,
void *dirent, filldir_t filldir)
{
int result, i = 0;
struct smb_dirent *entry = NULL;
struct smb_server *server = SMB_SERVER(dir);
 
DPRINTK("smb_readdir: filp->f_pos = %d\n", (int) filp->f_pos);
DDPRINTK("smb_readdir: dir->i_ino = %ld, c_ino = %ld\n",
dir->i_ino, c_ino);
 
if ((dir == NULL) || !S_ISDIR(dir->i_mode))
{
printk("smb_readdir: dir is NULL or not a directory\n");
return -EBADF;
}
if (c_entry == NULL)
{
i = sizeof(struct smb_dirent) * SMB_READDIR_CACHE_SIZE;
c_entry = (struct smb_dirent *) smb_vmalloc(i);
if (c_entry == NULL)
{
printk("smb_readdir: no MEMORY for cache\n");
return -ENOMEM;
}
}
if (filp->f_pos == 0)
{
c_ino = 0;
c_dev = 0;
c_seen_eof = 0;
 
if (filldir(dirent, ".", 1, filp->f_pos,
smb_info_ino(SMB_INOP(dir))) < 0)
{
return 0;
}
filp->f_pos += 1;
}
if (filp->f_pos == 1)
{
if (filldir(dirent, "..", 2, filp->f_pos,
smb_info_ino(SMB_INOP(dir)->dir)) < 0)
{
return 0;
}
filp->f_pos += 1;
}
entry = smb_search_in_cache(dir, filp->f_pos);
 
if (entry == NULL)
{
if (c_seen_eof)
{
/* End of directory */
return 0;
}
result = smb_refill_dir_cache(server, dir, filp->f_pos);
if (result <= 0)
{
return result;
}
entry = c_entry;
}
while (entry < &(c_entry[c_size]))
{
/* We found it. For getwd(), we have to return the
correct inode in d_ino if the inode is currently in
use. Otherwise the inode number does not
matter. (You can argue a lot about this..) */
 
struct smb_inode_info *ino_info
= smb_find_dir_inode(dir, entry->name, entry->len);
 
ino_t ino = entry->f_ino;
 
if (ino_info != NULL)
{
ino = smb_info_ino(ino_info);
}
DDPRINTK("smb_readdir: entry->name = %s\n", entry->name);
DDPRINTK("smb_readdir: entry->f_pos = %ld\n", entry->f_pos);
 
if (filldir(dirent, entry->name, strlen(entry->name),
entry->f_pos, ino) < 0)
{
break;
}
if ((dir->i_dev != c_dev) || (dir->i_ino != c_ino)
|| (entry->f_pos != filp->f_pos))
{
/* Someone has destroyed the cache while we slept
in filldir */
break;
}
filp->f_pos += 1;
entry += 1;
}
return 0;
}
 
void
smb_init_dir_cache(void)
{
c_ino = 0;
c_dev = 0;
c_entry = NULL;
}
 
void
smb_invalid_dir_cache(unsigned long ino)
{
/* TODO: check for dev as well */
if (ino == c_ino)
{
c_ino = 0;
c_seen_eof = 0;
}
}
 
void
smb_free_dir_cache(void)
{
if (c_entry != NULL)
{
smb_vfree(c_entry);
}
c_entry = NULL;
}
 
/* Insert a NEW smb_inode_info into the inode tree of our filesystem,
under dir. The caller must assure that it's not already there. We
assume that path is allocated for us. */
 
static struct inode *
smb_iget(struct inode *dir, struct smb_inode_info *new_inode_info)
{
struct inode *inode;
struct smb_inode_info *root;
 
if ((dir == NULL) || (new_inode_info == NULL))
{
printk("smb_iget: parameter is NULL\n");
return NULL;
}
new_inode_info->state = SMB_INODE_LOOKED_UP;
new_inode_info->nused = 0;
new_inode_info->dir = SMB_INOP(dir);
 
SMB_INOP(dir)->nused += 1;
 
/*
* We have to link the new inode_info into the doubly linked
* list of inode_infos to make a complete linear search possible.
*/
root = &(SMB_SERVER(dir)->root);
 
new_inode_info->prev = root;
new_inode_info->next = root->next;
root->next->prev = new_inode_info;
root->next = new_inode_info;
 
if (!(inode = iget(dir->i_sb, smb_info_ino(new_inode_info))))
{
printk("smb_iget: iget failed!");
/*
* If we blocked in iget(), another task may have referenced
* the info structure ... clean up with smb_free_inode_info.
*/
smb_free_inode_info(new_inode_info);
return NULL;
}
 
return inode;
}
 
void
smb_free_inode_info(struct smb_inode_info *i)
{
if (i == NULL)
{
printk("smb_free_inode: i == NULL\n");
return;
}
i->state = SMB_INODE_CACHED;
while ((i->nused == 0) && (i->state == SMB_INODE_CACHED))
{
struct smb_inode_info *dir = i->dir;
 
i->next->prev = i->prev;
i->prev->next = i->next;
 
smb_kfree_s(i, sizeof(struct smb_inode_info));
 
if (dir == NULL)
{
return;
}
dir->nused -= 1;
i = dir;
}
}
 
void
smb_init_root(struct smb_server *server)
{
struct smb_inode_info *root = &(server->root);
 
root->state = SMB_INODE_LOOKED_UP;
root->nused = 1;
root->dir = NULL;
root->next = root->prev = root;
 
return;
}
 
void
smb_free_all_inodes(struct smb_server *server)
{
/* Here nothing should be to do. I do not know whether it's
better to leave some memory allocated or be stuck in an
endless loop */
#if 1
struct smb_inode_info *root = &(server->root);
 
if (root->next != root)
{
printk("smb_free_all_inodes: INODES LEFT!!!\n");
}
while (root->next != root)
{
printk("smb_free_all_inodes: freeing inode\n");
smb_free_inode_info(root->next);
/* In case we have an endless loop.. */
schedule();
}
#endif
 
return;
}
 
/* This has to be called when a connection has gone down, so that all
file-handles we got from the server are invalid */
void
smb_invalidate_all_inodes(struct smb_server *server)
{
struct smb_inode_info *ino = &(server->root);
 
do
{
ino->finfo.opened = 0;
ino = ino->next;
}
while (ino != &(server->root));
 
return;
}
 
static int
compare_filename(const struct smb_server *server,
const char *s1, int len, struct smb_dirent *entry)
{
if (len != entry->len)
{
#if 0
/* Check whether the entry is about to be removed */
if (!entry->len)
printk("SMBFS: dead entry %s\n", entry->name);
#endif
return 1;
}
if (server->case_handling == CASE_DEFAULT)
{
return strncasecmp(s1, entry->name, len);
}
return strncmp(s1, entry->name, len);
}
 
/*
* Search for the smb_inode_info that belongs to this name,
* currently by a complete linear search through the inodes
* belonging to this filesystem.
*
* Note that this returns files as well as directories.
*/
static struct smb_inode_info *
smb_find_dir_inode(struct inode *parent, const char *name, int len)
{
struct smb_server *server = SMB_SERVER(parent);
struct smb_inode_info *dir = SMB_INOP(parent);
struct smb_inode_info *result = &(server->root);
 
if (name == NULL)
{
return NULL;
}
if ((len == 1) && (name[0] == '.'))
{
return dir;
}
if ((len == 2) && (name[0] == '.') && (name[1] == '.'))
{
return dir->dir;
}
do
{
if (result->dir == dir)
{
if (compare_filename(server, name, len,
&(result->finfo)) == 0)
{
return result;
}
}
result = result->next;
}
while (result != &(server->root));
 
return NULL;
}
 
static int
smb_lookup(struct inode *dir, const char *name, int len,
struct inode **result)
{
struct smb_dirent finfo;
struct smb_inode_info *result_info;
int error;
int found_in_cache;
 
struct smb_inode_info *new_inode_info = NULL;
 
*result = NULL;
 
if (!dir || !S_ISDIR(dir->i_mode))
{
printk("smb_lookup: inode is NULL or not a directory.\n");
iput(dir);
return -ENOENT;
}
DDPRINTK("smb_lookup: %s\n", name);
 
/* Fast cheat for . */
if (len == 0 || (len == 1 && name[0] == '.'))
{
*result = dir;
return 0;
}
/* ..and for .. */
if (len == 2 && name[0] == '.' && name[1] == '.')
{
struct smb_inode_info *parent = SMB_INOP(dir)->dir;
 
if (parent->state == SMB_INODE_CACHED)
{
parent->state = SMB_INODE_LOOKED_UP;
}
*result = iget(dir->i_sb, smb_info_ino(parent));
iput(dir);
if (*result == 0)
{
return -EACCES;
}
return 0;
}
result_info = smb_find_dir_inode(dir, name, len);
 
in_tree:
if (result_info != NULL)
{
if (result_info->state == SMB_INODE_CACHED)
{
result_info->state = SMB_INODE_LOOKED_UP;
}
*result = iget(dir->i_sb, smb_info_ino(result_info));
iput(dir);
 
if (new_inode_info != NULL)
{
smb_kfree_s(new_inode_info,
sizeof(struct smb_inode_info));
}
if (*result == NULL)
{
return -EACCES;
}
return 0;
}
/* If the file is in the dir cache, we do not have to ask the
server. */
found_in_cache = 0;
 
if ((dir->i_dev == c_dev) && (dir->i_ino == c_ino) && (c_size != 0))
{
int first = c_last_returned_index;
int i;
 
i = first;
do
{
if (compare_filename(SMB_SERVER(dir), name, len,
&(c_entry[i])) == 0)
{
finfo = c_entry[i];
found_in_cache = 1;
break;
}
i = (i + 1) % c_size;
}
while (i != first);
}
if (found_in_cache == 0)
{
DPRINTK("smb_lookup: not found in cache: %s\n", name);
if (len > SMB_MAXNAMELEN)
{
iput(dir);
return -ENAMETOOLONG;
}
error = smb_proc_getattr(dir, name, len, &finfo);
if (error < 0)
{
iput(dir);
return error;
}
finfo.f_ino = smb_fresh_inodes(SMB_SERVER(dir), 1);
}
new_inode_info = smb_kmalloc(sizeof(struct smb_inode_info),
GFP_KERNEL);
 
/* Here somebody else might have inserted the inode */
 
result_info = smb_find_dir_inode(dir, name, len);
if (result_info != NULL)
{
goto in_tree;
}
 
if (new_inode_info == NULL)
{
iput(dir);
return -ENOMEM;
}
new_inode_info->finfo = finfo;
 
DPRINTK("attr: %x\n", finfo.attr);
 
if ((*result = smb_iget(dir, new_inode_info)) == NULL)
{
iput(dir);
return -EACCES;
}
DDPRINTK("smb_lookup: %s => %lu\n", name, (unsigned long) result_info);
iput(dir);
return 0;
}
 
static int
smb_create(struct inode *dir, const char *name, int len, int mode,
struct inode **result)
{
int error;
struct smb_dirent entry;
struct smb_inode_info *new_inode_info;
 
*result = NULL;
 
if (!dir || !S_ISDIR(dir->i_mode))
{
printk("smb_create: inode is NULL or not a directory\n");
iput(dir);
return -ENOENT;
}
new_inode_info = smb_kmalloc(sizeof(struct smb_inode_info),
GFP_KERNEL);
if (new_inode_info == NULL)
{
iput(dir);
return -ENOMEM;
}
error = smb_proc_create(dir, name, len, 0, CURRENT_TIME);
if (error < 0)
{
smb_kfree_s(new_inode_info, sizeof(struct smb_inode_info));
iput(dir);
return error;
}
smb_invalid_dir_cache(dir->i_ino);
 
if ((error = smb_proc_getattr(dir, name, len, &entry)) < 0)
{
smb_kfree_s(new_inode_info, sizeof(struct smb_inode_info));
iput(dir);
return error;
}
entry.f_ino = smb_fresh_inodes(SMB_SERVER(dir), 1);
 
new_inode_info->finfo = entry;
 
if ((*result = smb_iget(dir, new_inode_info)) == NULL)
{
iput(dir);
return error;
}
iput(dir);
return 0;
}
 
static int
smb_mkdir(struct inode *dir, const char *name, int len, int mode)
{
int error;
 
if (!dir || !S_ISDIR(dir->i_mode))
{
iput(dir);
return -EINVAL;
}
if ((error = smb_proc_mkdir(dir, name, len)) == 0)
{
smb_invalid_dir_cache(dir->i_ino);
}
iput(dir);
return error;
}
 
static int
smb_rmdir(struct inode *dir, const char *name, int len)
{
int error;
 
if (!dir || !S_ISDIR(dir->i_mode))
{
printk("smb_rmdir: inode is NULL or not a directory\n");
iput(dir);
return -ENOENT;
}
if (smb_find_dir_inode(dir, name, len) != NULL)
{
error = -EBUSY;
} else
{
if ((error = smb_proc_rmdir(dir, name, len)) == 0)
{
smb_invalid_dir_cache(dir->i_ino);
}
}
iput(dir);
return error;
}
 
static int
smb_unlink(struct inode *dir, const char *name, int len)
{
int error;
 
if (!dir || !S_ISDIR(dir->i_mode))
{
printk("smb_unlink: inode is NULL or not a directory\n");
iput(dir);
return -ENOENT;
}
if (smb_find_dir_inode(dir, name, len) != NULL)
{
error = -EBUSY;
} else
{
if ((error = smb_proc_unlink(dir, name, len)) == 0)
{
smb_invalid_dir_cache(dir->i_ino);
}
}
iput(dir);
return error;
}
 
static int
smb_rename(struct inode *old_dir, const char *old_name, int old_len,
struct inode *new_dir, const char *new_name, int new_len,
int must_be_dir)
{
int res;
 
if (!old_dir || !S_ISDIR(old_dir->i_mode))
{
printk("smb_rename: old inode is NULL or not a directory\n");
res = -ENOENT;
goto finished;
}
if (!new_dir || !S_ISDIR(new_dir->i_mode))
{
printk("smb_rename: new inode is NULL or not a directory\n");
res = -ENOENT;
goto finished;
}
if ((smb_find_dir_inode(old_dir, old_name, old_len) != NULL)
|| (smb_find_dir_inode(new_dir, new_name, new_len) != NULL))
{
res = -EBUSY;
goto finished;
}
res = smb_proc_mv(old_dir, old_name, old_len,
new_dir, new_name, new_len);
 
if (res == -EEXIST)
{
int res1 = smb_proc_unlink(old_dir, new_name, new_len);
 
if (res1 == 0)
{
res = smb_proc_mv(old_dir, old_name, old_len,
new_dir, new_name, new_len);
}
}
if (res == 0)
{
smb_invalid_dir_cache(old_dir->i_ino);
smb_invalid_dir_cache(new_dir->i_ino);
}
finished:
iput(old_dir);
iput(new_dir);
return res;
}
/inode.c
0,0 → 1,506
/*
* inode.c
*
* Copyright (C) 1995, 1996 by Paal-Kr. Engstad and Volker Lendecke
*
*/
 
#include <linux/module.h>
 
#include <asm/system.h>
#include <asm/segment.h>
 
#include <linux/sched.h>
#include <linux/smb_fs.h>
#include <linux/smbno.h>
#include <linux/kernel.h>
#include <linux/mm.h>
#include <linux/string.h>
#include <linux/stat.h>
#include <linux/errno.h>
#include <linux/locks.h>
#include <linux/fcntl.h>
#include <linux/malloc.h>
 
extern int close_fp(struct file *filp);
 
static void smb_put_inode(struct inode *);
static void smb_read_inode(struct inode *);
static void smb_put_super(struct super_block *);
static void smb_statfs(struct super_block *, struct statfs *, int bufsiz);
 
static struct super_operations smb_sops =
{
smb_read_inode, /* read inode */
smb_notify_change, /* notify change */
NULL, /* write inode */
smb_put_inode, /* put inode */
smb_put_super, /* put superblock */
NULL, /* write superblock */
smb_statfs, /* stat filesystem */
NULL
};
 
/* smb_read_inode: Called from iget, it only traverses the allocated
smb_inode_info's and initializes the inode from the data found
there. It does not allocate or deallocate anything. */
 
static void
smb_read_inode(struct inode *inode)
{
/* Our task should be extremely simple here. We only have to
look up the information somebody else (smb_iget) put into
the inode tree. */
struct smb_server *server = SMB_SERVER(inode);
struct smb_inode_info *inode_info
= smb_find_inode(server, inode->i_ino);
 
if (inode_info == NULL)
{
/* Ok, now we're in trouble. The inode info is not
there. What should we do now??? */
printk("smb_read_inode: inode %ld info not found\n",
inode->i_ino);
return;
}
inode_info->state = SMB_INODE_VALID;
 
SMB_INOP(inode) = inode_info;
inode->i_mode = inode_info->finfo.f_mode;
inode->i_nlink = inode_info->finfo.f_nlink;
inode->i_uid = inode_info->finfo.f_uid;
inode->i_gid = inode_info->finfo.f_gid;
inode->i_rdev = inode_info->finfo.f_rdev;
inode->i_size = inode_info->finfo.f_size;
inode->i_mtime = inode_info->finfo.f_mtime;
inode->i_ctime = inode_info->finfo.f_ctime;
inode->i_atime = inode_info->finfo.f_atime;
inode->i_blksize = inode_info->finfo.f_blksize;
inode->i_blocks = inode_info->finfo.f_blocks;
 
if (S_ISREG(inode->i_mode))
{
inode->i_op = &smb_file_inode_operations;
} else if (S_ISDIR(inode->i_mode))
{
inode->i_op = &smb_dir_inode_operations;
} else
{
inode->i_op = NULL;
}
}
 
static void
smb_put_inode(struct inode *inode)
{
struct smb_server *server = SMB_SERVER(inode);
struct smb_inode_info *info = SMB_INOP(inode);
__u32 mtime = inode->i_mtime;
 
if (inode->i_count > 1) {
printk("smb_put_inode: in use device %s, inode %ld count=%ld\n",
kdevname(inode->i_dev), inode->i_ino, inode->i_count);
return;
}
 
if (S_ISDIR(inode->i_mode))
{
smb_invalid_dir_cache(inode->i_ino);
} else
{
/*
* Clear the length so the info structure can't be found.
*/
info->finfo.len = 0;
}
clear_inode(inode);
 
/*
* We don't want the inode to be reused as free if we block here,
* so temporarily increment i_count.
*/
inode->i_count++;
if (info) {
if (info->finfo.opened != 0)
{
if (smb_proc_close(server, info->finfo.fileid, mtime))
{
/* We can't do anything but complain. */
printk("smb_put_inode: could not close %s\n",
info->finfo.name);
}
}
smb_free_inode_info(info);
} else
printk("smb_put_inode: no inode info??\n");
 
inode->i_count--;
}
 
static void
smb_put_super(struct super_block *sb)
{
struct smb_server *server = &(SMB_SBP(sb)->s_server);
 
smb_proc_disconnect(server);
smb_dont_catch_keepalive(server);
close_fp(server->sock_file);
 
lock_super(sb);
 
smb_free_all_inodes(server);
 
smb_vfree(server->packet);
server->packet = NULL;
 
sb->s_dev = 0;
smb_kfree_s(SMB_SBP(sb), sizeof(struct smb_sb_info));
 
unlock_super(sb);
 
MOD_DEC_USE_COUNT;
}
 
struct smb_mount_data_v4
{
int version;
unsigned int fd;
uid_t mounted_uid;
struct sockaddr_in addr;
 
char server_name[17];
char client_name[17];
char service[64];
char root_path[64];
 
char username[64];
char password[64];
 
unsigned short max_xmit;
 
uid_t uid;
gid_t gid;
mode_t file_mode;
mode_t dir_mode;
};
 
static int
smb_get_mount_data(struct smb_mount_data *target, void *source)
{
struct smb_mount_data_v4 *v4 = (struct smb_mount_data_v4 *) source;
struct smb_mount_data *cur = (struct smb_mount_data *) source;
 
if (source == NULL)
{
return 1;
}
if (cur->version == SMB_MOUNT_VERSION)
{
memcpy(target, cur, sizeof(struct smb_mount_data));
return 0;
}
if (v4->version == 4)
{
target->version = 5;
target->fd = v4->fd;
target->mounted_uid = v4->mounted_uid;
target->addr = v4->addr;
 
memcpy(target->server_name, v4->server_name, 17);
memcpy(target->client_name, v4->client_name, 17);
memcpy(target->service, v4->service, 64);
memcpy(target->root_path, v4->root_path, 64);
memcpy(target->username, v4->username, 64);
memcpy(target->password, v4->password, 64);
 
target->max_xmit = v4->max_xmit;
target->uid = v4->uid;
target->gid = v4->gid;
target->file_mode = v4->file_mode;
target->dir_mode = v4->dir_mode;
 
memset(target->domain, 0, 64);
strcpy(target->domain, "?");
return 0;
}
return 1;
}
 
struct super_block *
smb_read_super(struct super_block *sb, void *raw_data, int silent)
{
struct smb_mount_data data;
struct smb_server *server;
struct smb_sb_info *smb_sb;
unsigned int fd;
struct file *filp;
kdev_t dev = sb->s_dev;
int error;
 
MOD_INC_USE_COUNT;
 
if (smb_get_mount_data(&data, raw_data) != 0)
{
printk("smb_read_super: wrong data argument\n");
sb->s_dev = 0;
MOD_DEC_USE_COUNT;
return NULL;
}
fd = data.fd;
if (fd >= NR_OPEN || !(filp = current->files->fd[fd]))
{
printk("smb_read_super: invalid file descriptor\n");
sb->s_dev = 0;
MOD_DEC_USE_COUNT;
return NULL;
}
if (!S_ISSOCK(filp->f_inode->i_mode))
{
printk("smb_read_super: not a socket!\n");
sb->s_dev = 0;
MOD_DEC_USE_COUNT;
return NULL;
}
/* We must malloc our own super-block info */
smb_sb = (struct smb_sb_info *) smb_kmalloc(sizeof(struct smb_sb_info),
GFP_KERNEL);
 
if (smb_sb == NULL)
{
printk("smb_read_super: could not alloc smb_sb_info\n");
sb->s_dev = 0;
MOD_DEC_USE_COUNT;
return NULL;
}
filp->f_count += 1;
 
lock_super(sb);
 
SMB_SBP(sb) = smb_sb;
 
sb->s_blocksize = 1024; /* Eh... Is this correct? */
sb->s_blocksize_bits = 10;
sb->s_magic = SMB_SUPER_MAGIC;
sb->s_dev = dev;
sb->s_op = &smb_sops;
 
server = &(SMB_SBP(sb)->s_server);
server->sock_file = filp;
server->lock = 0;
server->wait = NULL;
server->packet = NULL;
server->max_xmit = data.max_xmit;
if (server->max_xmit <= 0)
{
server->max_xmit = SMB_DEF_MAX_XMIT;
}
server->tid = 0;
server->pid = current->pid;
server->mid = current->pid + 20;
 
server->m = data;
server->m.file_mode = (server->m.file_mode &
(S_IRWXU | S_IRWXG | S_IRWXO)) | S_IFREG;
server->m.dir_mode = (server->m.dir_mode &
(S_IRWXU | S_IRWXG | S_IRWXO)) | S_IFDIR;
 
smb_init_root(server);
 
error = smb_proc_connect(server);
 
unlock_super(sb);
 
if (error < 0)
{
sb->s_dev = 0;
DPRINTK("smb_read_super: Failed connection, bailing out "
"(error = %d).\n", -error);
goto fail;
}
if (server->protocol >= PROTOCOL_LANMAN2)
{
server->case_handling = CASE_DEFAULT;
} else
{
server->case_handling = CASE_LOWER;
}
 
if ((error = smb_proc_dskattr(sb, &(SMB_SBP(sb)->s_attr))) < 0)
{
sb->s_dev = 0;
printk("smb_read_super: could not get super block "
"attributes\n");
goto fail;
}
smb_init_root_dirent(server, &(server->root.finfo));
 
if (!(sb->s_mounted = iget(sb, smb_info_ino(&(server->root)))))
{
sb->s_dev = 0;
printk("smb_read_super: get root inode failed\n");
goto fail;
}
return sb;
 
fail:
if (server->packet != NULL)
{
smb_vfree(server->packet);
server->packet = NULL;
}
filp->f_count -= 1;
smb_dont_catch_keepalive(server);
smb_kfree_s(SMB_SBP(sb), sizeof(struct smb_sb_info));
MOD_DEC_USE_COUNT;
return NULL;
}
 
static void
smb_statfs(struct super_block *sb, struct statfs *buf, int bufsiz)
{
int error;
struct smb_dskattr attr;
struct statfs tmp;
 
error = smb_proc_dskattr(sb, &attr);
 
if (error)
{
printk("smb_statfs: dskattr error = %d\n", -error);
attr.total = attr.allocblocks = attr.blocksize =
attr.free = 0;
}
tmp.f_type = SMB_SUPER_MAGIC;
tmp.f_bsize = attr.blocksize * attr.allocblocks;
tmp.f_blocks = attr.total;
tmp.f_bfree = attr.free;
tmp.f_bavail = attr.free;
tmp.f_files = -1;
tmp.f_ffree = -1;
tmp.f_namelen = SMB_MAXPATHLEN;
memcpy_tofs(buf, &tmp, bufsiz);
}
 
int
smb_notify_change(struct inode *inode, struct iattr *attr)
{
int error = 0;
 
if ((error = inode_change_ok(inode, attr)) < 0)
return error;
 
if (((attr->ia_valid & ATTR_UID) &&
(attr->ia_uid != SMB_SERVER(inode)->m.uid)))
return -EPERM;
 
if (((attr->ia_valid & ATTR_GID) &&
(attr->ia_gid != SMB_SERVER(inode)->m.gid)))
return -EPERM;
 
if (attr->ia_valid & ATTR_MODE) {
struct smb_dirent *fold = SMB_FINFO(inode);
struct smb_dirent finfo;
 
if (attr->ia_mode & ~(S_IFREG | S_IFDIR |
S_IRWXU | S_IRWXG | S_IRWXO))
return -EPERM;
 
memset((char *)&finfo, 0, sizeof(finfo));
finfo.attr = fold->attr;
 
if((attr->ia_mode & 0200) == 0)
finfo.attr |= aRONLY;
else
finfo.attr &= ~aRONLY;
 
if ((error = smb_proc_setattr(SMB_SERVER(inode),
inode, &finfo)) >= 0)
{
fold->attr = finfo.attr;
if ((attr->ia_mode & 0200) == 0)
inode->i_mode &= ~0222;
else
inode->i_mode |= 0222;
}
}
 
if ((attr->ia_valid & ATTR_SIZE) != 0)
{
 
if ((error = smb_make_open(inode, O_WRONLY)) < 0)
goto fail;
 
if ((error = smb_proc_trunc(SMB_SERVER(inode),
SMB_FINFO(inode)->fileid,
attr->ia_size)) < 0)
goto fail;
 
}
 
/* ATTR_CTIME and ATTR_ATIME can not be set via SMB, so ignore it. */
 
if (attr->ia_valid & ATTR_MTIME)
{
if (smb_make_open(inode, O_WRONLY) != 0)
error = -EACCES;
else
inode->i_mtime = attr->ia_mtime;
}
fail:
smb_invalid_dir_cache(smb_info_ino(SMB_INOP(inode)->dir));
return error;
}
 
 
#ifdef DEBUG_SMB_MALLOC
int smb_malloced;
int smb_current_kmalloced;
int smb_current_vmalloced;
#endif
 
static struct file_system_type smb_fs_type =
{
smb_read_super, "smbfs", 0, NULL
};
 
int
init_smb_fs(void)
{
return register_filesystem(&smb_fs_type);
}
 
#ifdef MODULE
int
init_module(void)
{
int status;
 
DPRINTK("smbfs: init_module called\n");
 
#ifdef DEBUG_SMB_MALLOC
smb_malloced = 0;
smb_current_kmalloced = 0;
smb_current_vmalloced = 0;
#endif
 
smb_init_dir_cache();
 
if ((status = init_smb_fs()) == 0)
register_symtab(0);
return status;
}
 
void
cleanup_module(void)
{
DPRINTK("smbfs: cleanup_module called\n");
smb_free_dir_cache();
unregister_filesystem(&smb_fs_type);
#ifdef DEBUG_SMB_MALLOC
printk("smb_malloced: %d\n", smb_malloced);
printk("smb_current_kmalloced: %d\n", smb_current_kmalloced);
printk("smb_current_vmalloced: %d\n", smb_current_vmalloced);
#endif
}
 
#endif
/file.c
0,0 → 1,244
/*
* file.c
*
* Copyright (C) 1995, 1996 by Paal-Kr. Engstad and Volker Lendecke
*
*/
 
#include <asm/segment.h>
#include <asm/system.h>
 
#include <linux/sched.h>
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/fcntl.h>
#include <linux/stat.h>
#include <linux/mm.h>
#include <linux/smb_fs.h>
#include <linux/malloc.h>
 
static inline int
min(int a, int b)
{
return a < b ? a : b;
}
 
static int
smb_fsync(struct inode *inode, struct file *file)
{
return 0;
}
 
int
smb_make_open(struct inode *i, int right)
{
struct smb_dirent *dirent;
 
if (i == NULL)
{
printk("smb_make_open: got NULL inode\n");
return -EINVAL;
}
dirent = &(SMB_INOP(i)->finfo);
 
DDPRINTK("smb_make_open: dirent->opened = %d\n", dirent->opened);
 
if ((dirent->opened) == 0)
{
/* tries max. rights */
int open_result = smb_proc_open(SMB_SERVER(i),
SMB_INOP(i)->dir,
dirent->name, dirent->len,
dirent);
if (open_result)
{
return open_result;
}
}
if (((right == O_RDONLY) && ((dirent->access == O_RDONLY)
|| (dirent->access == O_RDWR)))
|| ((right == O_WRONLY) && ((dirent->access == O_WRONLY)
|| (dirent->access == O_RDWR)))
|| ((right == O_RDWR) && (dirent->access == O_RDWR)))
return 0;
 
return -EACCES;
}
 
static int
smb_file_read(struct inode *inode, struct file *file, char *buf, int count)
{
int result, bufsize, to_read, already_read;
off_t pos;
int errno;
 
DPRINTK("smb_file_read: enter %s\n", SMB_FINFO(inode)->name);
 
if (!inode)
{
DPRINTK("smb_file_read: inode = NULL\n");
return -EINVAL;
}
if (!S_ISREG(inode->i_mode))
{
DPRINTK("smb_file_read: read from non-file, mode %07o\n",
inode->i_mode);
return -EINVAL;
}
if ((errno = smb_make_open(inode, O_RDONLY)) != 0)
return errno;
 
pos = file->f_pos;
 
if (pos + count > inode->i_size)
{
count = inode->i_size - pos;
}
if (count <= 0)
{
return 0;
}
bufsize = SMB_SERVER(inode)->max_xmit - SMB_HEADER_LEN - 5 * 2 - 5;
 
already_read = 0;
 
/* First read in as much as possible for each bufsize. */
while (already_read < count)
{
to_read = min(bufsize, count - already_read);
result = smb_proc_read(SMB_SERVER(inode), SMB_FINFO(inode),
pos, to_read, buf, 1);
if (result < 0)
{
return result;
}
pos += result;
buf += result;
already_read += result;
 
if (result < to_read)
{
break;
}
}
 
file->f_pos = pos;
 
if (!IS_RDONLY(inode))
inode->i_atime = CURRENT_TIME;
inode->i_dirt = 1;
 
DPRINTK("smb_file_read: exit %s\n", SMB_FINFO(inode)->name);
 
return already_read;
}
 
static int
smb_file_write(struct inode *inode, struct file *file, const char *buf,
int count)
{
int result, bufsize, to_write, already_written;
off_t pos;
int errno;
 
if (!inode)
{
DPRINTK("smb_file_write: inode = NULL\n");
return -EINVAL;
}
if (!S_ISREG(inode->i_mode))
{
DPRINTK("smb_file_write: write to non-file, mode %07o\n",
inode->i_mode);
return -EINVAL;
}
DPRINTK("smb_file_write: enter %s\n", SMB_FINFO(inode)->name);
 
if (count <= 0)
{
return 0;
}
if ((errno = smb_make_open(inode, O_RDWR)) != 0)
{
return errno;
}
pos = file->f_pos;
 
if (file->f_flags & O_APPEND)
pos = inode->i_size;
 
bufsize = SMB_SERVER(inode)->max_xmit - SMB_HEADER_LEN - 5 * 2 - 5;
 
already_written = 0;
 
DPRINTK("smb_write_file: blkmode = %d, blkmode & 2 = %d\n",
SMB_SERVER(inode)->blkmode,
SMB_SERVER(inode)->blkmode & 2);
 
while (already_written < count)
{
to_write = min(bufsize, count - already_written);
result = smb_proc_write(SMB_SERVER(inode), SMB_FINFO(inode),
pos, to_write, buf);
 
if (result < 0)
{
return result;
}
pos += result;
buf += result;
already_written += result;
 
if (result < to_write)
{
break;
}
}
 
inode->i_mtime = inode->i_ctime = CURRENT_TIME;
inode->i_dirt = 1;
 
file->f_pos = pos;
 
if (pos > inode->i_size)
{
inode->i_size = pos;
}
DPRINTK("smb_file_write: exit %s\n", SMB_FINFO(inode)->name);
 
return already_written;
}
 
static struct file_operations smb_file_operations =
{
NULL, /* lseek - default */
smb_file_read, /* read */
smb_file_write, /* write */
NULL, /* readdir - bad */
NULL, /* select - default */
smb_ioctl, /* ioctl */
smb_mmap, /* mmap */
NULL, /* open */
NULL, /* release */
smb_fsync, /* fsync */
};
 
struct inode_operations smb_file_inode_operations =
{
&smb_file_operations, /* default file operations */
NULL, /* create */
NULL, /* lookup */
NULL, /* link */
NULL, /* unlink */
NULL, /* symlink */
NULL, /* mkdir */
NULL, /* rmdir */
NULL, /* mknod */
NULL, /* rename */
NULL, /* readlink */
NULL, /* follow_link */
NULL, /* readpage */
NULL, /* writepage */
NULL, /* bmap */
NULL /* truncate */
};
/sock.c
0,0 → 1,723
/*
* sock.c
*
* Copyright (C) 1995, 1996 by Paal-Kr. Engstad and Volker Lendecke
*
*/
 
#include <linux/sched.h>
#include <linux/smb_fs.h>
#include <linux/errno.h>
#include <linux/socket.h>
#include <linux/fcntl.h>
#include <linux/stat.h>
#include <asm/segment.h>
#include <linux/in.h>
#include <linux/net.h>
#include <linux/mm.h>
#include <linux/netdevice.h>
#include <net/ip.h>
 
#include <linux/smb.h>
#include <linux/smbno.h>
 
 
#define _S(nr) (1<<((nr)-1))
 
static int
_recvfrom(struct socket *sock, unsigned char *ubuf, int size,
int noblock, unsigned flags, struct sockaddr_in *sa, int *addr_len)
{
struct iovec iov;
struct msghdr msg;
 
iov.iov_base = ubuf;
iov.iov_len = size;
 
msg.msg_name = (void *) sa;
msg.msg_namelen = 0;
if (addr_len)
msg.msg_namelen = *addr_len;
msg.msg_control = NULL;
msg.msg_iov = &iov;
msg.msg_iovlen = 1;
 
return sock->ops->recvmsg(sock, &msg, size, noblock, flags, addr_len);
}
 
static int
_send(struct socket *sock, const void *buff, int len,
int nonblock, unsigned flags)
{
struct iovec iov;
struct msghdr msg;
 
iov.iov_base = (void *) buff;
iov.iov_len = len;
 
msg.msg_name = NULL;
msg.msg_namelen = 0;
msg.msg_control = NULL;
msg.msg_iov = &iov;
msg.msg_iovlen = 1;
 
return sock->ops->sendmsg(sock, &msg, len, nonblock, flags);
}
 
static void
smb_data_callback(struct sock *sk, int len)
{
struct socket *sock = sk->socket;
 
if (!sk->dead)
{
unsigned char peek_buf[4];
int result;
unsigned short fs;
 
fs = get_fs();
set_fs(get_ds());
 
result = _recvfrom(sock, (void *) peek_buf, 1, 1,
MSG_PEEK, NULL, NULL);
 
while ((result != -EAGAIN) && (peek_buf[0] == 0x85))
{
/* got SESSION KEEP ALIVE */
result = _recvfrom(sock, (void *) peek_buf,
4, 1, 0, NULL, NULL);
 
DDPRINTK("smb_data_callback:"
" got SESSION KEEP ALIVE\n");
 
if (result == -EAGAIN)
{
break;
}
result = _recvfrom(sock, (void *) peek_buf,
1, 1, MSG_PEEK,
NULL, NULL);
}
set_fs(fs);
 
if (result != -EAGAIN)
{
wake_up_interruptible(sk->sleep);
}
}
}
 
int
smb_catch_keepalive(struct smb_server *server)
{
struct file *file;
struct inode *inode;
struct socket *sock;
struct sock *sk;
 
if ((server == NULL)
|| ((file = server->sock_file) == NULL)
|| ((inode = file->f_inode) == NULL)
|| (!S_ISSOCK(inode->i_mode)))
{
printk("smb_catch_keepalive: did not get valid server!\n");
server->data_ready = NULL;
return -EINVAL;
}
sock = &(inode->u.socket_i);
 
if (sock->type != SOCK_STREAM)
{
printk("smb_catch_keepalive: did not get SOCK_STREAM\n");
server->data_ready = NULL;
return -EINVAL;
}
sk = (struct sock *) (sock->data);
 
if (sk == NULL)
{
printk("smb_catch_keepalive: sk == NULL");
server->data_ready = NULL;
return -EINVAL;
}
DDPRINTK("smb_catch_keepalive.: sk->d_r = %x, server->d_r = %x\n",
(unsigned int) (sk->data_ready),
(unsigned int) (server->data_ready));
 
if (sk->data_ready == smb_data_callback)
{
printk("smb_catch_keepalive: already done\n");
return -EINVAL;
}
server->data_ready = sk->data_ready;
sk->data_ready = smb_data_callback;
return 0;
}
 
int
smb_dont_catch_keepalive(struct smb_server *server)
{
struct file *file;
struct inode *inode;
struct socket *sock;
struct sock *sk;
 
if ((server == NULL)
|| ((file = server->sock_file) == NULL)
|| ((inode = file->f_inode) == NULL)
|| (!S_ISSOCK(inode->i_mode)))
{
printk("smb_dont_catch_keepalive: "
"did not get valid server!\n");
return -EINVAL;
}
sock = &(inode->u.socket_i);
 
if (sock->type != SOCK_STREAM)
{
printk("smb_dont_catch_keepalive: did not get SOCK_STREAM\n");
return -EINVAL;
}
sk = (struct sock *) (sock->data);
 
if (sk == NULL)
{
printk("smb_dont_catch_keepalive: sk == NULL");
return -EINVAL;
}
if (server->data_ready == NULL)
{
printk("smb_dont_catch_keepalive: "
"server->data_ready == NULL\n");
return -EINVAL;
}
if (sk->data_ready != smb_data_callback)
{
printk("smb_dont_catch_keepalive: "
"sk->data_callback != smb_data_callback\n");
return -EINVAL;
}
DDPRINTK("smb_dont_catch_keepalive: sk->d_r = %x, server->d_r = %x\n",
(unsigned int) (sk->data_ready),
(unsigned int) (server->data_ready));
 
sk->data_ready = server->data_ready;
server->data_ready = NULL;
return 0;
}
 
static int
smb_send_raw(struct socket *sock, unsigned char *source, int length)
{
int result;
int already_sent = 0;
 
while (already_sent < length)
{
result = _send(sock,
(void *) (source + already_sent),
length - already_sent, 0, 0);
 
if (result < 0)
{
DPRINTK("smb_send_raw: sendto error = %d\n",
-result);
return result;
}
already_sent += result;
}
return already_sent;
}
 
static int
smb_receive_raw(struct socket *sock, unsigned char *target, int length)
{
int result;
int already_read = 0;
 
while (already_read < length)
{
result = _recvfrom(sock,
(void *) (target + already_read),
length - already_read, 0, 0,
NULL, NULL);
 
if (result == 0)
{
return -EIO;
}
if (result < 0)
{
DPRINTK("smb_receive_raw: recvfrom error = %d\n",
-result);
return result;
}
already_read += result;
}
return already_read;
}
 
static int
smb_get_length(struct socket *sock, unsigned char *header)
{
int result;
unsigned char peek_buf[4];
unsigned short fs;
 
re_recv:
fs = get_fs();
set_fs(get_ds());
result = smb_receive_raw(sock, peek_buf, 4);
set_fs(fs);
 
if (result < 0)
{
DPRINTK("smb_get_length: recv error = %d\n", -result);
return result;
}
switch (peek_buf[0])
{
case 0x00:
case 0x82:
break;
 
case 0x85:
DPRINTK("smb_get_length: Got SESSION KEEP ALIVE\n");
goto re_recv;
 
default:
printk("smb_get_length: Invalid NBT packet\n");
return -EIO;
}
 
if (header != NULL)
{
memcpy(header, peek_buf, 4);
}
/* The length in the RFC NB header is the raw data length */
return smb_len(peek_buf);
}
 
static struct socket *
server_sock(struct smb_server *server)
{
struct file *file;
struct inode *inode;
 
if (server == NULL)
return NULL;
if ((file = server->sock_file) == NULL)
return NULL;
if ((inode = file->f_inode) == NULL)
return NULL;
return &(inode->u.socket_i);
}
 
/*
* smb_receive
* fs points to the correct segment
*/
static int
smb_receive(struct smb_server *server)
{
struct socket *sock = server_sock(server);
int len;
int result;
unsigned char peek_buf[4];
 
len = smb_get_length(sock, peek_buf);
 
if (len < 0)
{
return len;
}
if (len + 4 > server->packet_size)
{
/* Some servers do not care about our max_xmit. They
send larger packets */
DPRINTK("smb_receive: Increase packet size from %d to %d\n",
server->packet_size, len + 4);
smb_vfree(server->packet);
server->packet = NULL;
 
server->packet_size = 0;
server->packet = smb_vmalloc(len + 4);
if (server->packet == NULL)
{
return -ENOMEM;
}
server->packet_size = len + 4;
}
memcpy(server->packet, peek_buf, 4);
result = smb_receive_raw(sock, server->packet + 4, len);
 
if (result < 0)
{
printk("smb_receive: receive error: %d\n", result);
return result;
}
server->rcls = BVAL(server->packet, 9);
server->err = WVAL(server->packet, 11);
 
if (server->rcls != 0)
{
DPRINTK("smb_receive: rcls=%d, err=%d\n",
server->rcls, server->err);
}
return result;
}
 
static int
smb_receive_trans2(struct smb_server *server,
int *ldata, unsigned char **data,
int *lparam, unsigned char **param)
{
int total_data = 0;
int total_param = 0;
int result;
unsigned char *rcv_buf;
int buf_len;
int data_len = 0;
int param_len = 0;
 
if ((result = smb_receive(server)) < 0)
{
return result;
}
if (server->rcls != 0)
{
*param = *data = server->packet;
*ldata = *lparam = 0;
return 0;
}
total_data = WVAL(server->packet, smb_tdrcnt);
total_param = WVAL(server->packet, smb_tprcnt);
 
DDPRINTK("smb_receive_trans2: td=%d,tp=%d\n", total_data, total_param);
 
if ((total_data > TRANS2_MAX_TRANSFER)
|| (total_param > TRANS2_MAX_TRANSFER))
{
DPRINTK("smb_receive_trans2: data/param too long\n");
return -EIO;
}
buf_len = total_data + total_param;
if (server->packet_size > buf_len)
{
buf_len = server->packet_size;
}
if ((rcv_buf = smb_vmalloc(buf_len)) == NULL)
{
DPRINTK("smb_receive_trans2: could not alloc data area\n");
return -ENOMEM;
}
*param = rcv_buf;
*data = rcv_buf + total_param;
 
while (1)
{
unsigned char *inbuf = server->packet;
 
if (WVAL(inbuf, smb_prdisp) + WVAL(inbuf, smb_prcnt)
> total_param)
{
DPRINTK("smb_receive_trans2: invalid parameters\n");
result = -EIO;
goto fail;
}
memcpy(*param + WVAL(inbuf, smb_prdisp),
smb_base(inbuf) + WVAL(inbuf, smb_proff),
WVAL(inbuf, smb_prcnt));
param_len += WVAL(inbuf, smb_prcnt);
 
if (WVAL(inbuf, smb_drdisp) + WVAL(inbuf, smb_drcnt)
> total_data)
{
DPRINTK("smb_receive_trans2: invalid data block\n");
result = -EIO;
goto fail;
}
DDPRINTK("target: %X\n",
(unsigned int) *data + WVAL(inbuf, smb_drdisp));
DDPRINTK("source: %X\n",
(unsigned int)
smb_base(inbuf) + WVAL(inbuf, smb_droff));
DDPRINTK("disp: %d, off: %d, cnt: %d\n",
WVAL(inbuf, smb_drdisp), WVAL(inbuf, smb_droff),
WVAL(inbuf, smb_drcnt));
 
memcpy(*data + WVAL(inbuf, smb_drdisp),
smb_base(inbuf) + WVAL(inbuf, smb_droff),
WVAL(inbuf, smb_drcnt));
data_len += WVAL(inbuf, smb_drcnt);
 
if ((WVAL(inbuf, smb_tdrcnt) > total_data)
|| (WVAL(inbuf, smb_tprcnt) > total_param))
{
printk("smb_receive_trans2: data/params grew!\n");
result = -EIO;
goto fail;
}
/* the total lengths might shrink! */
total_data = WVAL(inbuf, smb_tdrcnt);
total_param = WVAL(inbuf, smb_tprcnt);
 
if ((data_len >= total_data) && (param_len >= total_param))
{
break;
}
if ((result = smb_receive(server)) < 0)
{
goto fail;
}
if (server->rcls != 0)
{
result = -EIO;
goto fail;
}
}
*ldata = data_len;
*lparam = param_len;
 
smb_vfree(server->packet);
server->packet = rcv_buf;
server->packet_size = buf_len;
return 0;
 
fail:
smb_vfree(rcv_buf);
return result;
}
 
int
smb_release(struct smb_server *server)
{
struct socket *sock = server_sock(server);
int result;
 
if (sock == NULL)
{
return -EINVAL;
}
result = sock->ops->release(sock, NULL);
DPRINTK("smb_release: sock->ops->release = %d\n", result);
 
/* inet_release does not set sock->state. Maybe someone is
confused about sock->state being SS_CONNECTED while there
is nothing behind it, so I set it to SS_UNCONNECTED. */
sock->state = SS_UNCONNECTED;
 
result = sock->ops->create(sock, 0);
DPRINTK("smb_release: sock->ops->create = %d\n", result);
return result;
}
 
int
smb_connect(struct smb_server *server)
{
struct socket *sock = server_sock(server);
if (sock == NULL)
{
return -EINVAL;
}
if (sock->state != SS_UNCONNECTED)
{
DPRINTK("smb_connect: socket is not unconnected: %d\n",
sock->state);
}
return sock->ops->connect(sock, (struct sockaddr *) &(server->m.addr),
sizeof(struct sockaddr_in), 0);
}
 
int
smb_request(struct smb_server *server)
{
unsigned long old_mask;
unsigned short fs;
int len, result;
 
unsigned char *buffer = (server == NULL) ? NULL : server->packet;
 
if (buffer == NULL)
{
printk("smb_request: Bad server!\n");
return -EBADF;
}
if (server->state != CONN_VALID)
{
return -EIO;
}
if ((result = smb_dont_catch_keepalive(server)) != 0)
{
server->state = CONN_INVALID;
smb_invalidate_all_inodes(server);
return result;
}
len = smb_len(buffer) + 4;
 
DPRINTK("smb_request: len = %d cmd = 0x%X\n", len, buffer[8]);
 
old_mask = current->blocked;
current->blocked |= ~(_S(SIGKILL) | _S(SIGSTOP));
fs = get_fs();
set_fs(get_ds());
 
result = smb_send_raw(server_sock(server), (void *) buffer, len);
if (result > 0)
{
result = smb_receive(server);
}
/* read/write errors are handled by errno */
current->signal &= ~_S(SIGPIPE);
current->blocked = old_mask;
set_fs(fs);
 
if (result >= 0)
{
int result2 = smb_catch_keepalive(server);
if (result2 < 0)
{
result = result2;
}
}
if (result < 0)
{
server->state = CONN_INVALID;
smb_invalidate_all_inodes(server);
}
DDPRINTK("smb_request: result = %d\n", result);
 
return result;
}
 
#define ROUND_UP(x) (((x)+3) & ~3)
static int
smb_send_trans2(struct smb_server *server, __u16 trans2_command,
int ldata, unsigned char *data,
int lparam, unsigned char *param)
{
struct socket *sock = server_sock(server);
 
/* 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 oparam =
ROUND_UP(SMB_HEADER_LEN + 2 * smb_parameters + 2 + 3);
const int odata =
ROUND_UP(oparam + lparam);
const int bcc =
odata + ldata - (SMB_HEADER_LEN + 2 * smb_parameters + 2);
const int packet_length =
SMB_HEADER_LEN + 2 * smb_parameters + bcc + 2;
 
unsigned char padding[4] =
{0,};
char *p;
 
struct iovec iov[4];
struct msghdr msg;
 
if ((bcc + oparam) > server->max_xmit)
{
return -ENOMEM;
}
p = smb_setup_header(server, SMBtrans2, smb_parameters, bcc);
 
WSET(server->packet, smb_tpscnt, lparam);
WSET(server->packet, smb_tdscnt, ldata);
WSET(server->packet, smb_mprcnt, TRANS2_MAX_TRANSFER);
WSET(server->packet, smb_mdrcnt, TRANS2_MAX_TRANSFER);
WSET(server->packet, smb_msrcnt, 0);
WSET(server->packet, smb_flags, 0);
DSET(server->packet, smb_timeout, 0);
WSET(server->packet, smb_pscnt, lparam);
WSET(server->packet, smb_psoff, oparam - 4);
WSET(server->packet, smb_dscnt, ldata);
WSET(server->packet, smb_dsoff, odata - 4);
WSET(server->packet, smb_suwcnt, 1);
WSET(server->packet, smb_setup0, trans2_command);
*p++ = 0; /* null smb_name for trans2 */
*p++ = 'D'; /* this was added because OS/2 does it */
*p++ = ' ';
 
iov[0].iov_base = (void *) server->packet;
iov[0].iov_len = oparam;
iov[1].iov_base = (param == NULL) ? padding : param;
iov[1].iov_len = lparam;
iov[2].iov_base = padding;
iov[2].iov_len = odata - oparam - lparam;
iov[3].iov_base = (data == NULL) ? padding : data;
iov[3].iov_len = ldata;
 
msg.msg_name = NULL;
msg.msg_namelen = 0;
msg.msg_control = NULL;
msg.msg_iov = iov;
msg.msg_iovlen = 4;
 
return sock->ops->sendmsg(sock, &msg, packet_length, 0, 0);
}
 
/*
* This is not really a trans2 request, we assume that you only have
* one packet to send.
*/
int
smb_trans2_request(struct smb_server *server, __u16 trans2_command,
int ldata, unsigned char *data,
int lparam, unsigned char *param,
int *lrdata, unsigned char **rdata,
int *lrparam, unsigned char **rparam)
{
unsigned long old_mask;
unsigned short fs;
int result;
 
DPRINTK("smb_trans2_request: com=%d, ld=%d, lp=%d\n",
trans2_command, ldata, lparam);
 
if (server->state != CONN_VALID)
{
return -EIO;
}
if ((result = smb_dont_catch_keepalive(server)) != 0)
{
server->state = CONN_INVALID;
smb_invalidate_all_inodes(server);
return result;
}
old_mask = current->blocked;
current->blocked |= ~(_S(SIGKILL) | _S(SIGSTOP));
fs = get_fs();
set_fs(get_ds());
 
result = smb_send_trans2(server, trans2_command,
ldata, data, lparam, param);
if (result >= 0)
{
result = smb_receive_trans2(server,
lrdata, rdata, lrparam, rparam);
}
/* read/write errors are handled by errno */
current->signal &= ~_S(SIGPIPE);
current->blocked = old_mask;
set_fs(fs);
 
if (result >= 0)
{
int result2 = smb_catch_keepalive(server);
if (result2 < 0)
{
result = result2;
}
}
if (result < 0)
{
server->state = CONN_INVALID;
smb_invalidate_all_inodes(server);
}
DDPRINTK("smb_trans2_request: result = %d\n", result);
 
return result;
}
/proc.c
0,0 → 1,1921
/*
* proc.c
*
* Copyright (C) 1995, 1996 by Paal-Kr. Engstad and Volker Lendecke
*
* 28/06/96 - Fixed long file name support (smb_proc_readdir_long) by Yuri Per
*/
 
#include <linux/config.h>
#include <linux/fs.h>
#include <linux/smbno.h>
#include <linux/smb_fs.h>
#include <linux/types.h>
#include <linux/errno.h>
#include <linux/malloc.h>
#include <linux/stat.h>
#include <linux/fcntl.h>
#include <asm/segment.h>
#include <asm/string.h>
 
#define SMB_VWV(packet) ((packet) + SMB_HEADER_LEN)
#define SMB_CMD(packet) (BVAL(packet,8))
#define SMB_WCT(packet) (BVAL(packet, SMB_HEADER_LEN - 1))
#define SMB_BCC(packet) smb_bcc(packet)
#define SMB_BUF(packet) ((packet) + SMB_HEADER_LEN + SMB_WCT(packet) * 2 + 2)
 
#define SMB_DIRINFO_SIZE 43
#define SMB_STATUS_SIZE 21
 
static int smb_request_ok(struct smb_server *s, int command, int wct, int bcc);
 
static inline int
min(int a, int b)
{
return a < b ? a : b;
}
 
static void
str_upper(char *name)
{
while (*name)
{
if (*name >= 'a' && *name <= 'z')
*name -= ('a' - 'A');
name++;
}
}
 
static void
str_lower(char *name)
{
while (*name)
{
if (*name >= 'A' && *name <= 'Z')
*name += ('a' - 'A');
name++;
}
}
 
/*****************************************************************************/
/* */
/* Encoding/Decoding section */
/* */
/*****************************************************************************/
 
static inline byte *
smb_decode_word(byte * p, word * data)
{
*data = WVAL(p, 0);
return p + 2;
}
 
byte *
smb_encode_smb_length(byte * p, dword len)
{
BSET(p, 0, 0);
BSET(p, 1, 0);
BSET(p, 2, (len & 0xFF00) >> 8);
BSET(p, 3, (len & 0xFF));
if (len > 0xFFFF)
{
BSET(p, 1, 1);
}
return p + 4;
}
 
static byte *
smb_encode_ascii(byte * p, const byte * name, int len)
{
*p++ = 4;
strcpy(p, name);
return p + len + 1;
}
 
static byte *
smb_encode_this_name(byte * p, const char *name, const int len)
{
*p++ = '\\';
strncpy(p, name, len);
return p + len;
}
 
/* I put smb_encode_parents into a separate function so that the
recursion only takes 16 bytes on the stack per path component on a
386. */
 
static byte *
smb_encode_parents(byte * p, struct smb_inode_info *ino)
{
byte *q;
 
if (ino->dir == NULL)
{
return p;
}
q = smb_encode_parents(p, ino->dir);
if (q - p + 1 + ino->finfo.len > SMB_MAXPATHLEN)
{
return p;
}
return smb_encode_this_name(q, ino->finfo.name, ino->finfo.len);
}
 
static byte *
smb_encode_path(struct smb_server *server,
byte * p, struct smb_inode_info *dir,
const char *name, const int len)
{
byte *start = p;
if (dir != NULL)
{
p = smb_encode_parents(p, dir);
}
p = smb_encode_this_name(p, name, len);
*p++ = 0;
if (server->protocol <= PROTOCOL_COREPLUS)
{
str_upper(start);
}
return p;
}
 
static byte *
smb_decode_data(byte * p, byte * data, word * data_len, int fs)
{
word len;
 
if (!(*p == 1 || *p == 5))
{
printk("smb_decode_data: Warning! Data block not starting "
"with 1 or 5\n");
}
len = WVAL(p, 1);
p += 3;
 
if (fs)
memcpy_tofs(data, p, len);
else
memcpy(data, p, len);
 
*data_len = len;
 
return p + len;
}
 
static byte *
smb_name_mangle(byte * p, const byte * name)
{
int len, pad = 0;
 
len = strlen(name);
 
if (len < 16)
pad = 16 - len;
 
*p++ = 2 * (len + pad);
 
while (*name)
{
*p++ = (*name >> 4) + 'A';
*p++ = (*name & 0x0F) + 'A';
name++;
}
while (pad--)
{
*p++ = 'C';
*p++ = 'A';
}
*p++ = '\0';
 
return p;
}
 
/* 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 */
 
 
extern struct timezone sys_tz;
 
static int
utc2local(int time)
{
return time - sys_tz.tz_minuteswest * 60 + sys_tz.tz_dsttime * 3600;
}
 
static int
local2utc(int time)
{
return time + sys_tz.tz_minuteswest * 60 - sys_tz.tz_dsttime * 3600;
}
 
/* Convert a MS-DOS time/date pair to a UNIX date (seconds since 1 1 70). */
 
static int
date_dos2unix(unsigned short time, unsigned short date)
{
int month, year, secs;
 
month = ((date >> 5) & 15) - 1;
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(secs);
}
 
/*****************************************************************************/
/* */
/* Support section. */
/* */
/*****************************************************************************/
 
dword
smb_len(byte * p)
{
return ((BVAL(p, 1) & 0x1) << 16L) | (BVAL(p, 2) << 8L) | (BVAL(p, 3));
}
 
static word
smb_bcc(byte * packet)
{
int pos = SMB_HEADER_LEN + SMB_WCT(packet) * sizeof(word);
return WVAL(packet, pos);
}
 
/* smb_valid_packet: We check if packet fulfills the basic
requirements of a smb packet */
 
static int
smb_valid_packet(byte * packet)
{
DDPRINTK("len: %d, wct: %d, bcc: %d\n",
smb_len(packet), SMB_WCT(packet), SMB_BCC(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(byte * packet, int command, int wct, int bcc)
{
return (SMB_CMD(packet) == command &&
SMB_WCT(packet) >= wct &&
(bcc == -1 || SMB_BCC(packet) >= bcc)) ? 0 : -EIO;
}
 
static int
smb_errno(int errcls, int error)
{
if (errcls == ERRDOS)
switch (error)
{
case ERRbadfunc:
return EINVAL;
case ERRbadfile:
return ENOENT;
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:
return EREMOTEIO;
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 0;
case ERRbadshare:
return ETXTBSY;
case ERRlock:
return EDEADLK;
case ERRfilexists:
return EEXIST;
case 87:
return 0; /* Unknown error!! */
/* This next error seems to occur on an mv when
* the destination exists */
case 183:
return EEXIST;
default:
return EIO;
} else if (errcls == ERRSRV)
switch (error)
{
case ERRerror:
return ENFILE;
case ERRbadpw:
return EINVAL;
case ERRbadtype:
return EIO;
case ERRaccess:
return EACCES;
default:
return EIO;
} else if (errcls == ERRHRD)
switch (error)
{
case ERRnowrite:
return EROFS;
case ERRbadunit:
return ENODEV;
case ERRnotready:
return EUCLEAN;
case ERRbadcmd:
return EIO;
case ERRdata:
return EIO;
case ERRbadreq:
return ERANGE;
case ERRbadshare:
return ETXTBSY;
case ERRlock:
return EDEADLK;
default:
return EIO;
} else if (errcls == ERRCMD)
return EIO;
return 0;
}
 
static void
smb_lock_server(struct smb_server *server)
{
while (server->lock)
sleep_on(&server->wait);
server->lock = 1;
}
 
static void
smb_unlock_server(struct smb_server *server)
{
if (server->lock != 1)
{
printk("smb_unlock_server: was not locked!\n");
}
server->lock = 0;
wake_up(&server->wait);
}
 
/* 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_server *s, int command, int wct, int bcc)
{
int result = 0;
s->rcls = 0;
s->err = 0;
 
if (smb_request(s) < 0)
{
DPRINTK("smb_request failed\n");
result = -EIO;
} else if (smb_valid_packet(s->packet) != 0)
{
DPRINTK("not a valid packet!\n");
result = -EIO;
} else if (s->rcls != 0)
{
result = -smb_errno(s->rcls, s->err);
} else if (smb_verify(s->packet, command, wct, bcc) != 0)
{
DPRINTK("smb_verify failed\n");
result = -EIO;
}
return result;
}
 
/* smb_retry: This function should be called when smb_request_ok has
indicated an error. If the error was indicated because the
connection was killed, we try to reconnect. If smb_retry returns 0,
the error was indicated for another reason, so a retry would not be
of any use. */
 
static int
smb_retry(struct smb_server *server)
{
if (server->state != CONN_INVALID)
{
return 0;
}
if (smb_release(server) < 0)
{
DPRINTK("smb_retry: smb_release failed\n");
server->state = CONN_RETRIED;
return 0;
}
if (smb_proc_reconnect(server) < 0)
{
DPRINTK("smb_proc_reconnect failed\n");
server->state = CONN_RETRIED;
return 0;
}
server->state = CONN_VALID;
return 1;
}
 
static int
smb_request_ok_unlock(struct smb_server *s, int command, int wct, int bcc)
{
int result = smb_request_ok(s, command, wct, bcc);
 
smb_unlock_server(s);
 
return result;
}
 
/* smb_setup_header: We completely set up the packet. You only have to
insert the command-specific fields */
 
__u8 *
smb_setup_header(struct smb_server * server, byte command, word wct, word bcc)
{
dword xmit_len = SMB_HEADER_LEN + wct * sizeof(word) + bcc + 2;
byte *p = server->packet;
byte *buf = server->packet;
 
p = smb_encode_smb_length(p, xmit_len - 4);
 
BSET(p, 0, 0xff);
BSET(p, 1, 'S');
BSET(p, 2, 'M');
BSET(p, 3, 'B');
BSET(p, 4, command);
 
p += 5;
memset(p, '\0', 19);
p += 19;
p += 8;
 
WSET(buf, smb_tid, server->tid);
WSET(buf, smb_pid, server->pid);
WSET(buf, smb_uid, server->server_uid);
WSET(buf, smb_mid, server->mid);
 
if (server->protocol > PROTOCOL_CORE)
{
BSET(buf, smb_flg, 0x8);
WSET(buf, smb_flg2, 0x3);
}
*p++ = wct; /* wct */
p += 2 * wct;
WSET(p, 0, bcc);
return p + 2;
}
 
/* smb_setup_header_exclusive waits on server->lock and locks the
server, when it's free. You have to unlock it manually when you're
finished with server->packet! */
 
static byte *
smb_setup_header_exclusive(struct smb_server *server,
byte command, word wct, word bcc)
{
smb_lock_server(server);
return smb_setup_header(server, command, wct, bcc);
}
 
static void
smb_setup_bcc(struct smb_server *server, byte * p)
{
__u8 *packet = server->packet;
__u8 *pbcc = packet + SMB_HEADER_LEN + 2 * SMB_WCT(packet);
__u16 bcc = p - (pbcc + 2);
 
WSET(pbcc, 0, bcc);
smb_encode_smb_length(packet,
SMB_HEADER_LEN + 2 * SMB_WCT(packet) - 2 + bcc);
}
 
 
/*****************************************************************************/
/* */
/* File operation section. */
/* */
/*****************************************************************************/
 
int
smb_proc_open(struct smb_server *server,
struct smb_inode_info *dir, const char *name, int len,
struct smb_dirent *entry)
{
int error;
char *p;
char *buf;
const word o_attr = aSYSTEM | aHIDDEN | aDIR;
 
DPRINTK("smb_proc_open: name=%s\n", name);
 
smb_lock_server(server);
 
if (entry->opened != 0)
{
/* Somebody else opened the file while we slept */
smb_unlock_server(server);
return 0;
}
retry:
buf = server->packet;
p = smb_setup_header(server, SMBopen, 2, 0);
WSET(buf, smb_vwv0, 0x42); /* read/write */
WSET(buf, smb_vwv1, o_attr);
*p++ = 4;
p = smb_encode_path(server, p, dir, name, len);
smb_setup_bcc(server, p);
 
if ((error = smb_request_ok(server, SMBopen, 7, 0)) != 0)
{
 
if (smb_retry(server))
{
goto retry;
}
if ((error != -EACCES) && (error != -ETXTBSY)
&& (error != -EROFS))
{
smb_unlock_server(server);
return error;
}
/* N.B. Packet may change after request */
buf = server->packet;
p = smb_setup_header(server, SMBopen, 2, 0);
WSET(buf, smb_vwv0, 0x40); /* read only */
WSET(buf, smb_vwv1, o_attr);
*p++ = 4;
p = smb_encode_path(server, p, dir, name, len);
smb_setup_bcc(server, p);
 
if ((error = smb_request_ok(server, SMBopen, 7, 0)) != 0)
{
if (smb_retry(server))
{
goto retry;
}
smb_unlock_server(server);
return error;
}
}
/* We should now have data in vwv[0..6]. */
 
/* N.B. Packet may change after request */
buf = server->packet;
entry->fileid = WVAL(buf, smb_vwv0);
entry->attr = WVAL(buf, smb_vwv1);
entry->f_ctime = entry->f_atime =
entry->f_mtime = local2utc(DVAL(buf, smb_vwv2));
entry->f_size = DVAL(buf, smb_vwv4);
entry->access = WVAL(buf, smb_vwv6);
 
entry->opened = 1;
entry->access &= 3;
 
smb_unlock_server(server);
 
DPRINTK("smb_proc_open: entry->access = %d\n", entry->access);
return 0;
}
 
int
smb_proc_close(struct smb_server *server,
__u16 fileid, __u32 mtime)
{
char *buf;
 
smb_setup_header_exclusive(server, SMBclose, 3, 0);
buf = server->packet;
WSET(buf, smb_vwv0, fileid);
DSET(buf, smb_vwv1, utc2local(mtime));
 
return smb_request_ok_unlock(server, SMBclose, 0, 0);
}
 
/* In smb_proc_read and smb_proc_write we do not retry, because the
file-id would not be valid after a reconnection. */
 
/* smb_proc_read: fs indicates if it should be copied with
memcpy_tofs. */
 
int
smb_proc_read(struct smb_server *server, struct smb_dirent *finfo,
off_t offset, long count, char *data, int fs)
{
word returned_count, data_len;
char *buf;
int error;
 
smb_setup_header_exclusive(server, SMBread, 5, 0);
buf = server->packet;
 
WSET(buf, smb_vwv0, finfo->fileid);
WSET(buf, smb_vwv1, count);
DSET(buf, smb_vwv2, offset);
WSET(buf, smb_vwv4, 0);
 
if ((error = smb_request_ok(server, SMBread, 5, -1)) < 0)
{
smb_unlock_server(server);
return error;
}
returned_count = WVAL(server->packet, smb_vwv0);
 
smb_decode_data(SMB_BUF(server->packet), data, &data_len, fs);
 
smb_unlock_server(server);
 
if (returned_count != data_len)
{
printk("smb_proc_read: Warning, returned_count != data_len\n");
printk("smb_proc_read: ret_c=%d, data_len=%d\n",
returned_count, data_len);
}
return data_len;
}
 
int
smb_proc_write(struct smb_server *server, struct smb_dirent *finfo,
off_t offset, int count, const char *data)
{
int res = 0;
char *buf;
byte *p;
 
p = smb_setup_header_exclusive(server, SMBwrite, 5, count + 3);
buf = server->packet;
WSET(buf, smb_vwv0, finfo->fileid);
WSET(buf, smb_vwv1, count);
DSET(buf, smb_vwv2, offset);
WSET(buf, smb_vwv4, 0);
 
*p++ = 1;
WSET(p, 0, count);
memcpy_fromfs(p + 2, data, count);
 
if ((res = smb_request_ok(server, SMBwrite, 1, 0)) >= 0)
{
res = WVAL(server->packet, smb_vwv0);
}
smb_unlock_server(server);
 
return res;
}
 
int
smb_proc_create(struct inode *dir, const char *name, int len,
word attr, time_t ctime)
{
int error;
char *p;
struct smb_server *server = SMB_SERVER(dir);
char *buf;
__u16 fileid;
 
smb_lock_server(server);
retry:
buf = server->packet;
p = smb_setup_header(server, SMBcreate, 3, 0);
WSET(buf, smb_vwv0, attr);
DSET(buf, smb_vwv1, utc2local(ctime));
*p++ = 4;
p = smb_encode_path(server, p, SMB_INOP(dir), name, len);
smb_setup_bcc(server, p);
 
if ((error = smb_request_ok(server, SMBcreate, 1, 0)) < 0)
{
if (smb_retry(server))
{
goto retry;
}
smb_unlock_server(server);
return error;
}
fileid = WVAL(server->packet, smb_vwv0);
smb_unlock_server(server);
 
smb_proc_close(server, fileid, CURRENT_TIME);
 
return 0;
}
 
int
smb_proc_mv(struct inode *odir, const char *oname, const int olen,
struct inode *ndir, const char *nname, const int nlen)
{
char *p;
struct smb_server *server = SMB_SERVER(odir);
int result;
 
smb_lock_server(server);
 
retry:
p = smb_setup_header(server, SMBmv, 1, 0);
WSET(server->packet, smb_vwv0, aSYSTEM | aHIDDEN);
*p++ = 4;
p = smb_encode_path(server, p, SMB_INOP(odir), oname, olen);
*p++ = 4;
p = smb_encode_path(server, p, SMB_INOP(ndir), nname, nlen);
smb_setup_bcc(server, p);
 
if ((result = smb_request_ok(server, SMBmv, 0, 0)) < 0)
{
if (smb_retry(server))
{
goto retry;
}
}
smb_unlock_server(server);
return result;
}
 
int
smb_proc_mkdir(struct inode *dir, const char *name, const int len)
{
char *p;
int result;
struct smb_server *server = SMB_SERVER(dir);
 
smb_lock_server(server);
 
retry:
p = smb_setup_header(server, SMBmkdir, 0, 0);
*p++ = 4;
p = smb_encode_path(server, p, SMB_INOP(dir), name, len);
smb_setup_bcc(server, p);
 
if ((result = smb_request_ok(server, SMBmkdir, 0, 0)) < 0)
{
if (smb_retry(server))
{
goto retry;
}
}
smb_unlock_server(server);
return result;
}
 
int
smb_proc_rmdir(struct inode *dir, const char *name, const int len)
{
char *p;
int result;
struct smb_server *server = SMB_SERVER(dir);
 
smb_lock_server(server);
 
retry:
p = smb_setup_header(server, SMBrmdir, 0, 0);
*p++ = 4;
p = smb_encode_path(server, p, SMB_INOP(dir), name, len);
smb_setup_bcc(server, p);
 
if ((result = smb_request_ok(server, SMBrmdir, 0, 0)) < 0)
{
if (smb_retry(server))
{
goto retry;
}
}
smb_unlock_server(server);
return result;
}
 
int
smb_proc_unlink(struct inode *dir, const char *name, const int len)
{
char *p;
struct smb_server *server = SMB_SERVER(dir);
int result;
 
smb_lock_server(server);
 
retry:
p = smb_setup_header(server, SMBunlink, 1, 0);
WSET(server->packet, smb_vwv0, aSYSTEM | aHIDDEN);
*p++ = 4;
p = smb_encode_path(server, p, SMB_INOP(dir), name, len);
smb_setup_bcc(server, p);
 
if ((result = smb_request_ok(server, SMBunlink, 0, 0)) < 0)
{
if (smb_retry(server))
{
goto retry;
}
}
smb_unlock_server(server);
return result;
}
 
int
smb_proc_trunc(struct smb_server *server, word fid, dword length)
{
char *p;
char *buf;
int result;
 
smb_lock_server(server);
 
retry:
buf = server->packet;
p = smb_setup_header(server, SMBwrite, 5, 0);
WSET(buf, smb_vwv0, fid);
WSET(buf, smb_vwv1, 0);
DSET(buf, smb_vwv2, length);
WSET(buf, smb_vwv4, 0);
p = smb_encode_ascii(p, "", 0);
smb_setup_bcc(server, p);
 
if ((result = smb_request_ok(server, SMBwrite, 1, 0)) < 0)
{
if (smb_retry(server))
{
goto retry;
}
}
smb_unlock_server(server);
return result;
}
 
static void
smb_init_dirent(struct smb_server *server, struct smb_dirent *entry)
{
memset(entry, 0, sizeof(struct smb_dirent));
 
entry->f_nlink = 1;
entry->f_uid = server->m.uid;
entry->f_gid = server->m.gid;
entry->f_blksize = 512;
}
 
static void
smb_finish_dirent(struct smb_server *server, struct smb_dirent *entry)
{
if ((entry->attr & aDIR) != 0)
{
entry->f_mode = server->m.dir_mode;
entry->f_size = 512;
} else
{
entry->f_mode = server->m.file_mode;
}
 
if (entry->attr & aRONLY)
entry->f_mode &= ~0222;
 
if ((entry->f_blksize != 0) && (entry->f_size != 0))
{
entry->f_blocks =
(entry->f_size - 1) / entry->f_blksize + 1;
} else
{
entry->f_blocks = 0;
}
return;
}
 
void
smb_init_root_dirent(struct smb_server *server, struct smb_dirent *entry)
{
smb_init_dirent(server, entry);
entry->attr = aDIR;
entry->f_ino = 1;
smb_finish_dirent(server, entry);
}
 
 
static char *
smb_decode_dirent(struct smb_server *server, char *p, struct smb_dirent *entry)
{
smb_init_dirent(server, entry);
 
p += SMB_STATUS_SIZE; /* reserved (search_status) */
entry->attr = BVAL(p, 0);
entry->f_mtime = entry->f_atime = entry->f_ctime =
date_dos2unix(WVAL(p, 1), WVAL(p, 3));
entry->f_size = DVAL(p, 5);
entry->len = strlen(p + 9);
if (entry->len > 12)
{
entry->len = 12;
}
memcpy(entry->name, p + 9, entry->len);
entry->name[entry->len] = '\0';
while (entry->len > 2)
{
/* Pathworks fills names with spaces */
entry->len -= 1;
if (entry->name[entry->len] == ' ')
{
entry->name[entry->len] = '\0';
}
}
switch (server->case_handling)
{
case CASE_UPPER:
str_upper(entry->name);
break;
case CASE_LOWER:
str_lower(entry->name);
break;
default:
break;
}
DPRINTK("smb_decode_dirent: name = %s\n", entry->name);
smb_finish_dirent(server, entry);
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 <
PROTOCOL_LANMAN2 */
 
static int
smb_proc_readdir_short(struct smb_server *server, struct inode *dir, int fpos,
int cache_size, struct smb_dirent *entry)
{
char *p;
char *buf;
int error;
int result;
int i;
int first, total_count;
struct smb_dirent *current_entry;
word bcc;
word count;
char status[SMB_STATUS_SIZE];
int entries_asked = (server->max_xmit - 100) / SMB_DIRINFO_SIZE;
 
DPRINTK("SMB call readdir %d @ %d\n", cache_size, fpos);
 
smb_lock_server(server);
 
retry:
buf = server->packet;
first = 1;
total_count = 0;
current_entry = entry;
 
while (1)
{
if (first == 1)
{
p = smb_setup_header(server, SMBsearch, 2, 0);
WSET(buf, smb_vwv0, entries_asked);
WSET(buf, smb_vwv1, aDIR);
*p++ = 4;
p = smb_encode_path(server, p, SMB_INOP(dir), "*.*", 3);
*p++ = 5;
WSET(p, 0, 0);
p += 2;
} else
{
p = smb_setup_header(server, SMBsearch, 2, 0);
WSET(buf, smb_vwv0, entries_asked);
WSET(buf, smb_vwv1, aDIR);
p = smb_encode_ascii(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(server, p);
 
if ((error = smb_request_ok(server, SMBsearch, 1, -1)) < 0)
{
if ((server->rcls == ERRDOS)
&& (server->err == ERRnofiles))
{
result = total_count - fpos;
goto unlock_return;
} else
{
if (smb_retry(server))
{
goto retry;
}
result = error;
goto unlock_return;
}
}
p = SMB_VWV(server->packet);
p = smb_decode_word(p, &count);
p = smb_decode_word(p, &bcc);
 
first = 0;
 
if (count <= 0)
{
result = total_count - fpos;
goto unlock_return;
}
if (bcc != count * SMB_DIRINFO_SIZE + 3)
{
result = -EIO;
goto unlock_return;
}
p += 3; /* Skipping VBLOCK header
(5, length lo, length hi). */
 
/* Read the last entry into the status field. */
memcpy(status,
SMB_BUF(server->packet) + 3 +
(count - 1) * SMB_DIRINFO_SIZE,
SMB_STATUS_SIZE);
 
/* Now we are ready to parse smb directory entries. */
 
for (i = 0; i < count; i++)
{
if (total_count < fpos)
{
p += SMB_DIRINFO_SIZE;
DDPRINTK("smb_proc_readdir: skipped entry.\n");
DDPRINTK(" total_count = %d\n"
" i = %d, fpos = %d\n",
total_count, i, fpos);
} else if (total_count >= fpos + cache_size)
{
result = total_count - fpos;
goto unlock_return;
} else
{
p = smb_decode_dirent(server, p,
current_entry);
current_entry->f_pos = total_count;
DDPRINTK("smb_proc_readdir: entry->f_pos = "
"%lu\n", entry->f_pos);
current_entry += 1;
}
total_count += 1;
}
}
unlock_return:
smb_unlock_server(server);
return result;
}
 
/* interpret a long filename structure - this is mostly guesses at the
moment. The length of the structure is returned. The structure of
a long filename depends on the info level. 260 is used by NT and 2
is used by OS/2. */
 
static char *
smb_decode_long_dirent(struct smb_server *server, char *p,
struct smb_dirent *entry, int level)
{
char *result;
 
smb_init_dirent(server, entry);
 
switch (level)
{
/* We might add more levels later... */
case 1:
entry->len = BVAL(p, 26);
strncpy(entry->name, p + 27, entry->len);
entry->name[entry->len] = '\0';
entry->f_size = DVAL(p, 16);
entry->attr = BVAL(p, 24);
 
entry->f_ctime = date_dos2unix(WVAL(p, 6), WVAL(p, 4));
entry->f_atime = date_dos2unix(WVAL(p, 10), WVAL(p, 8));
entry->f_mtime = date_dos2unix(WVAL(p, 14), WVAL(p, 12));
result = p + 28 + BVAL(p, 26);
break;
 
default:
DPRINTK("Unknown long filename format %d\n", level);
result = p + WVAL(p, 0);
}
 
switch (server->case_handling)
{
case CASE_UPPER:
str_upper(entry->name);
break;
case CASE_LOWER:
str_lower(entry->name);
break;
default:
break;
}
 
smb_finish_dirent(server, entry);
return result;
}
 
int
smb_proc_readdir_long(struct smb_server *server, struct inode *dir, int fpos,
int cache_size, struct smb_dirent *cache)
{
/* NT uses 260, OS/2 uses 2. Both accept 1. */
const int info_level = 1;
const int max_matches = 512;
 
char *p;
char *lastname;
int lastname_len;
int i;
int first, entries, entries_seen;
 
unsigned char *resp_data = NULL;
unsigned char *resp_param = NULL;
int resp_data_len = 0;
int resp_param_len = 0;
 
__u16 command;
 
int result;
 
int ff_resume_key = 0;
int ff_searchcount = 0;
int ff_eos = 0;
int ff_lastname = 0;
int ff_dir_handle = 0;
int loop_count = 0;
 
char param[SMB_MAXPATHLEN + 2 + 12];
int mask_len;
unsigned char *mask = &(param[12]);
 
mask_len = smb_encode_path(server, mask,
SMB_INOP(dir), "*", 1) - mask;
 
mask[mask_len] = 0;
mask[mask_len + 1] = 0;
 
DPRINTK("smb_readdir_long cache=%d, fpos=%d, mask=%s\n",
cache_size, fpos, mask);
 
smb_lock_server(server);
 
retry:
 
first = 1;
entries = 0;
entries_seen = 2;
 
while (ff_eos == 0)
{
loop_count += 1;
if (loop_count > 200)
{
printk("smb_proc_readdir_long: "
"Looping in FIND_NEXT??\n");
break;
}
if (first != 0)
{
command = TRANSACT2_FINDFIRST;
WSET(param, 0, aSYSTEM | aHIDDEN | aDIR);
WSET(param, 2, max_matches); /* max count */
WSET(param, 4, 8 + 4 + 2); /* resume required +
close on end +
continue */
WSET(param, 6, info_level);
DSET(param, 8, 0);
} else
{
command = TRANSACT2_FINDNEXT;
DPRINTK("hand=0x%X resume=%d ff_lastname=%d mask=%s\n",
ff_dir_handle, ff_resume_key, ff_lastname, mask);
WSET(param, 0, ff_dir_handle);
WSET(param, 2, max_matches); /* max count */
WSET(param, 4, info_level);
DSET(param, 6, ff_resume_key); /* ff_resume_key */
WSET(param, 10, 8 + 4 + 2); /* resume required +
close on end +
continue */
}
#ifdef CONFIG_SMB_WIN95
/* Windows 95 is not able to deliver answers
to FIND_NEXT fast enough, so sleep 0.2 seconds */
current->timeout = jiffies + HZ / 5;
current->state = TASK_INTERRUPTIBLE;
schedule();
current->timeout = 0;
#endif
 
result = smb_trans2_request(server, command,
0, NULL, 12 + mask_len + 2, param,
&resp_data_len, &resp_data,
&resp_param_len, &resp_param);
 
if (result < 0)
{
if (smb_retry(server))
{
goto retry;
}
DPRINTK("smb_proc_readdir_long: "
"got error from trans2_request\n");
break;
}
if (server->rcls != 0)
{
result = -EIO;
break;
}
/* parse out some important return info */
if (first != 0)
{
ff_dir_handle = WVAL(resp_param, 0);
ff_searchcount = WVAL(resp_param, 2);
ff_eos = WVAL(resp_param, 4);
ff_lastname = WVAL(resp_param, 8);
} else
{
ff_searchcount = WVAL(resp_param, 0);
ff_eos = WVAL(resp_param, 2);
ff_lastname = WVAL(resp_param, 6);
}
 
if (ff_searchcount == 0)
{
break;
}
/* point to the data bytes */
p = resp_data;
 
/* we might need the lastname for continuations */
lastname = "";
lastname_len = 0;
if (ff_lastname > 0)
{
switch (info_level)
{
case 260:
lastname = p + ff_lastname;
lastname_len = resp_data_len - ff_lastname;
ff_resume_key = 0;
break;
case 1:
lastname = p + ff_lastname + 1;
lastname_len = BVAL(p, ff_lastname);
ff_resume_key = 0;
break;
}
}
lastname_len = min(lastname_len, 256);
strncpy(mask, lastname, lastname_len);
mask[lastname_len] = '\0';
 
/* Now we are ready to parse smb directory entries. */
 
for (i = 0; i < ff_searchcount; i++)
{
struct smb_dirent *entry = &(cache[entries]);
 
p = smb_decode_long_dirent(server, p,
entry, info_level);
 
DDPRINTK("smb_readdir_long: got %s\n", entry->name);
 
if ((entry->name[0] == '.')
&& ((entry->name[1] == '\0')
|| ((entry->name[1] == '.')
&& (entry->name[2] == '\0'))))
{
/* ignore . and .. from the server */
continue;
}
if (entries_seen >= fpos)
{
entry->f_pos = entries_seen;
entries += 1;
}
if (entries >= cache_size)
{
goto finished;
}
entries_seen += 1;
}
 
DPRINTK("received %d entries (eos=%d resume=%d)\n",
ff_searchcount, ff_eos, ff_resume_key);
 
first = 0;
}
 
finished:
smb_unlock_server(server);
return entries;
}
 
int
smb_proc_readdir(struct smb_server *server, struct inode *dir, int fpos,
int cache_size, struct smb_dirent *entry)
{
if (server->protocol >= PROTOCOL_LANMAN2)
return smb_proc_readdir_long(server, dir, fpos, cache_size,
entry);
else
return smb_proc_readdir_short(server, dir, fpos, cache_size,
entry);
}
 
/*
* This version uses the core protocol to get the attribute info.
* It works OK with Win 3.11, 95 and NT 3.51, but NOT with NT 4 (bad mtime).
*/
static int
smb_proc_getattr_core(struct inode *dir, const char *name, int len,
struct smb_dirent *entry)
{
int result;
char *p;
struct smb_server *server = SMB_SERVER(dir);
char *buf;
 
smb_lock_server(server);
 
DDPRINTK("smb_proc_getattr_core: %s\n", name);
 
retry:
buf = server->packet;
p = smb_setup_header(server, SMBgetatr, 0, 0);
*p++ = 4;
p = smb_encode_path(server, p, SMB_INOP(dir), name, len);
smb_setup_bcc(server, p);
 
if ((result = smb_request_ok(server, SMBgetatr, 10, 0)) < 0)
{
if (smb_retry(server))
{
goto retry;
}
smb_unlock_server(server);
return result;
}
/* N.B. Packet may change after request */
buf = server->packet;
entry->attr = WVAL(buf, smb_vwv0);
entry->f_ctime = entry->f_atime =
entry->f_mtime = local2utc(DVAL(buf, smb_vwv1));
 
DDPRINTK("smb_proc_getattr_core: mtime=%ld\n", entry->f_mtime);
 
entry->f_size = DVAL(buf, smb_vwv3);
smb_unlock_server(server);
return 0;
}
 
/*
* This version uses the trans2 findfirst to get the attribute info.
* It works fine with NT 3.51 and NT 4 (any SP), but not with Win95 (ERRerror).
*/
static int
smb_proc_getattr_ff(struct inode *dir, const char *name, int len,
struct smb_dirent *entry)
{
unsigned char *resp_data = NULL;
unsigned char *resp_param = NULL;
int resp_data_len = 0;
int resp_param_len = 0;
 
char param[SMB_MAXPATHLEN + 1 + 12];
int mask_len;
unsigned char *mask = &(param[12]);
 
int result;
char *p;
struct smb_server *server = SMB_SERVER(dir);
 
mask_len = smb_encode_path(server, mask,
SMB_INOP(dir), name, len) - mask;
 
mask[mask_len] = 0;
 
DDPRINTK("smb_proc_getattr_ff: mask=%s\n", mask);
 
smb_lock_server(server);
 
retry:
 
WSET(param, 0, aSYSTEM | aHIDDEN | aDIR);
WSET(param, 2, 1); /* max count */
WSET(param, 4, 2 + 1); /* close on end + close after this call */
WSET(param, 6, 1); /* info level */
DSET(param, 8, 0);
 
result = smb_trans2_request(server, TRANSACT2_FINDFIRST,
0, NULL, 12 + mask_len + 1, param,
&resp_data_len, &resp_data,
&resp_param_len, &resp_param);
 
if (result < 0)
{
if (smb_retry(server))
{
DPRINTK("smb_proc_getattr_ff: error=%d, retrying\n",
result);
goto retry;
}
goto out;
}
if (server->rcls != 0)
{
result = -smb_errno(server->rcls, server->err);
if (result != -ENOENT)
DPRINTK("smb_proc_getattr_ff: rcls=%d, err=%d\n",
server->rcls, server->err);
goto out;
}
/* Make sure we got enough data ... */
result = -EINVAL; /* WVAL(resp_param, 2) is ff_searchcount */
if (resp_data_len < 22 || WVAL(resp_param, 2) != 1)
{
DPRINTK("smb_proc_getattr_ff: bad result, len=%d, count=%d\n",
resp_data_len, WVAL(resp_param, 2));
goto out;
}
/* Decode the response (info level 1, as in smb_decode_long_dirent) */
p = resp_data;
entry->f_ctime = date_dos2unix(WVAL(p, 2), WVAL(p, 0));
entry->f_atime = date_dos2unix(WVAL(p, 6), WVAL(p, 4));
entry->f_mtime = date_dos2unix(WVAL(p, 10), WVAL(p, 8));
entry->f_size = DVAL(p, 12);
entry->attr = WVAL(p, 20);
 
DDPRINTK("smb_proc_getattr_ff: attr=%x\n", entry->attr);
 
result = 0;
 
out:
smb_unlock_server(server);
return result;
}
 
int
smb_proc_getattr(struct inode *dir, const char *name, int len,
struct smb_dirent *entry)
{
struct smb_server *server = SMB_SERVER(dir);
int result;
 
smb_init_dirent(server, entry);
/* Use trans2 for NT, use core protocol for others (Win95/3.11/...).
* We distinguish NT from Win95 by looking at the capabilities,
* in the same way as in Samba 1.9.18p2's reply.c.
*/
if ((server->protocol >= PROTOCOL_LANMAN2)
&& (server->blkmode & (CAP_NT_SMBS | CAP_STATUS32)))
result = smb_proc_getattr_ff(dir, name, len, entry);
else
result = smb_proc_getattr_core(dir, name, len, entry);
 
smb_finish_dirent(server, entry);
 
entry->len = len;
memcpy(entry->name, name, len);
/* entry->name is null terminated from smb_init_dirent */
 
return result;
}
 
int
smb_proc_setattr(struct smb_server *server,
struct inode *i, struct smb_dirent *new_finfo)
{
char *p;
char *buf;
int result;
 
smb_lock_server(server);
 
retry:
buf = server->packet;
p = smb_setup_header(server, SMBsetatr, 8, 0);
WSET(buf, smb_vwv0, new_finfo->attr);
DSET(buf, smb_vwv1, 0);
DSET(buf, smb_vwv3, 0);
DSET(buf, smb_vwv5, 0);
WSET(buf, smb_vwv7, 0);
*p++ = 4;
p = smb_encode_path(server, p,
SMB_INOP(i)->dir, SMB_INOP(i)->finfo.name,
SMB_INOP(i)->finfo.len);
 
smb_setup_bcc(server, p);
if ((result = smb_request_ok(server, SMBsetatr, 0, 0)) < 0)
{
if (smb_retry(server))
{
goto retry;
}
}
smb_unlock_server(server);
return result;
}
 
int
smb_proc_dskattr(struct super_block *super, struct smb_dskattr *attr)
{
int error;
char *p;
struct smb_server *server = &(SMB_SBP(super)->s_server);
 
smb_lock_server(server);
 
retry:
smb_setup_header(server, SMBdskattr, 0, 0);
 
if ((error = smb_request_ok(server, SMBdskattr, 5, 0)) < 0)
{
if (smb_retry(server))
{
goto retry;
}
smb_unlock_server(server);
return error;
}
p = SMB_VWV(server->packet);
p = smb_decode_word(p, &attr->total);
p = smb_decode_word(p, &attr->allocblocks);
p = smb_decode_word(p, &attr->blocksize);
p = smb_decode_word(p, &attr->free);
smb_unlock_server(server);
return 0;
}
 
/*****************************************************************************/
/* */
/* Mount/umount operations. */
/* */
/*****************************************************************************/
 
struct smb_prots
{
enum smb_protocol prot;
const char *name;
};
 
/* smb_proc_reconnect: We expect the server to be locked, so that you
can call the routine from within smb_retry. The socket must be
created, like after a user-level socket()-call. It may not be
connected. */
 
int
smb_proc_reconnect(struct smb_server *server)
{
struct smb_prots prots[] =
{
{PROTOCOL_CORE, "PC NETWORK PROGRAM 1.0"},
{PROTOCOL_COREPLUS, "MICROSOFT NETWORKS 1.03"},
#ifdef LANMAN1
{PROTOCOL_LANMAN1, "MICROSOFT NETWORKS 3.0"},
{PROTOCOL_LANMAN1, "LANMAN1.0"},
#endif
#ifdef LANMAN2
{PROTOCOL_LANMAN2, "LM1.2X002"},
#endif
#ifdef NT1
{PROTOCOL_NT1, "NT LM 0.12"},
{PROTOCOL_NT1, "NT LANMAN 1.0"},
#endif
{-1, NULL}};
char dev[] = "A:";
int i, plength;
int max_xmit = 1024; /* Space needed for first request. */
int given_max_xmit = server->m.max_xmit;
int result;
byte *p;
 
if ((result = smb_connect(server)) < 0)
{
DPRINTK("smb_proc_reconnect: could not smb_connect\n");
goto fail;
}
/* Here we assume that the connection is valid */
server->state = CONN_VALID;
 
if (server->packet != NULL)
{
smb_vfree(server->packet);
server->packet = NULL;
server->packet_size = 0;
}
server->packet = smb_vmalloc(max_xmit);
 
if (server->packet == NULL)
{
printk("smb_proc_connect: No memory! Bailing out.\n");
result = -ENOMEM;
goto fail;
}
server->packet_size = server->max_xmit = max_xmit;
 
/*
* Start with an RFC1002 session request packet.
*/
p = server->packet + 4;
 
p = smb_name_mangle(p, server->m.server_name);
p = smb_name_mangle(p, server->m.client_name);
 
smb_encode_smb_length(server->packet,
(void *) p - (void *) (server->packet));
 
server->packet[0] = 0x81; /* SESSION REQUEST */
 
if (smb_catch_keepalive(server) < 0)
{
printk("smb_proc_connect: could not catch_keepalives\n");
}
if ((result = smb_request(server)) < 0)
{
DPRINTK("smb_proc_connect: Failed to send SESSION REQUEST.\n");
smb_dont_catch_keepalive(server);
goto fail;
}
if (server->packet[0] != 0x82)
{
printk("smb_proc_connect: Did not receive positive response "
"(err = %x)\n",
server->packet[0]);
smb_dont_catch_keepalive(server);
result = -EIO;
goto fail;
}
DPRINTK("smb_proc_connect: Passed SESSION REQUEST.\n");
 
/* Now we are ready to send a SMB Negotiate Protocol packet. */
memset(server->packet, 0, SMB_HEADER_LEN);
 
plength = 0;
for (i = 0; prots[i].name != NULL; i++)
{
plength += strlen(prots[i].name) + 2;
}
 
smb_setup_header(server, SMBnegprot, 0, plength);
 
p = SMB_BUF(server->packet);
 
for (i = 0; prots[i].name != NULL; i++)
{
*p++ = 2;
strcpy(p, prots[i].name);
p += strlen(prots[i].name) + 1;
}
 
if ((result = smb_request_ok(server, SMBnegprot, 1, -1)) < 0)
{
DPRINTK("smb_proc_connect: Failure requesting SMBnegprot\n");
smb_dont_catch_keepalive(server);
goto fail;
} else
{
DDPRINTK("smb_proc_connect: Request SMBnegprot..");
}
 
DDPRINTK("Verified!\n");
 
p = SMB_VWV(server->packet);
p = smb_decode_word(p, (word *) & i);
#ifdef CONFIG_COLDFIRE
/*
* Not quite sure why this is needed???
* Maybe the byte swapping code is broken, but the result
* byte is certainly in the wrong position within the word.
*/
i = i >> 16;
#endif
server->protocol = prots[i].prot;
 
DPRINTK("smb_proc_connect: Server wants %s protocol.\n",
prots[i].name);
 
if (server->protocol >= PROTOCOL_LANMAN1)
{
 
word passlen = strlen(server->m.password);
word userlen = strlen(server->m.username);
 
#ifdef DEBUG_SMB_PASSWORD
DPRINTK("smb_proc_connect: password = %s\n",
server->m.password);
#endif
DPRINTK("smb_proc_connect: usernam = %s\n",
server->m.username);
 
if (server->protocol >= PROTOCOL_NT1)
{
server->max_xmit = DVAL(server->packet, smb_vwv3 + 1);
server->maxmux = WVAL(server->packet, smb_vwv1 + 1);
server->maxvcs = WVAL(server->packet, smb_vwv2 + 1);
server->blkmode = DVAL(server->packet, smb_vwv9 + 1);
server->sesskey = DVAL(server->packet, smb_vwv7 + 1);
} else
{
server->max_xmit = WVAL(server->packet, smb_vwv2);
server->maxmux = WVAL(server->packet, smb_vwv3);
server->maxvcs = WVAL(server->packet, smb_vwv4);
server->blkmode = WVAL(server->packet, smb_vwv5);
server->sesskey = DVAL(server->packet, smb_vwv6);
}
DPRINTK("smb_proc_connect: blkmode (capabilities) = %x\n",
server->blkmode);
 
if (server->max_xmit < given_max_xmit)
{
/* We do not distinguish between the client
requests and the server response. */
given_max_xmit = server->max_xmit;
}
if (server->protocol >= PROTOCOL_NT1)
{
char *workgroup = server->m.domain;
char *OS_id = "Unix";
char *client_id = "ksmbfs";
 
smb_setup_header(server, SMBsesssetupX, 13,
5 + userlen + passlen +
strlen(workgroup) + strlen(OS_id) +
strlen(client_id));
 
WSET(server->packet, smb_vwv0, 0x00ff);
WSET(server->packet, smb_vwv1, 0);
WSET(server->packet, smb_vwv2, given_max_xmit);
WSET(server->packet, smb_vwv3, 2);
WSET(server->packet, smb_vwv4, server->pid);
DSET(server->packet, smb_vwv5, server->sesskey);
WSET(server->packet, smb_vwv7, passlen + 1);
WSET(server->packet, smb_vwv8, 0);
WSET(server->packet, smb_vwv9, 0);
 
p = SMB_BUF(server->packet);
strcpy(p, server->m.password);
p += passlen + 1;
strcpy(p, server->m.username);
p += userlen + 1;
strcpy(p, workgroup);
p += strlen(p) + 1;
strcpy(p, OS_id);
p += strlen(p) + 1;
strcpy(p, client_id);
} else
{
smb_setup_header(server, SMBsesssetupX, 10,
2 + userlen + passlen);
 
WSET(server->packet, smb_vwv0, 0x00ff);
WSET(server->packet, smb_vwv1, 0);
WSET(server->packet, smb_vwv2, given_max_xmit);
WSET(server->packet, smb_vwv3, 2);
WSET(server->packet, smb_vwv4, server->pid);
DSET(server->packet, smb_vwv5, server->sesskey);
WSET(server->packet, smb_vwv7, passlen + 1);
WSET(server->packet, smb_vwv8, 0);
WSET(server->packet, smb_vwv9, 0);
 
p = SMB_BUF(server->packet);
strcpy(p, server->m.password);
p += passlen + 1;
strcpy(p, server->m.username);
}
 
if ((result = smb_request_ok(server, SMBsesssetupX, 3, 0)) < 0)
{
DPRINTK("smb_proc_connect: SMBsessetupX failed\n");
smb_dont_catch_keepalive(server);
goto fail;
}
smb_decode_word(server->packet + 32, &(server->server_uid));
} else
{
server->max_xmit = 0;
server->maxmux = 0;
server->maxvcs = 0;
server->blkmode = 0;
server->sesskey = 0;
}
 
/* Fine! We have a connection, send a tcon message. */
 
smb_setup_header(server, SMBtcon, 0,
6 + strlen(server->m.service) +
strlen(server->m.password) + strlen(dev));
 
p = SMB_BUF(server->packet);
p = smb_encode_ascii(p, server->m.service, strlen(server->m.service));
p = smb_encode_ascii(p, server->m.password, strlen(server->m.password));
p = smb_encode_ascii(p, dev, strlen(dev));
 
if ((result = smb_request_ok(server, SMBtcon, 2, 0)) < 0)
{
DPRINTK("smb_proc_connect: SMBtcon not verified.\n");
smb_dont_catch_keepalive(server);
goto fail;
}
DDPRINTK("OK! Managed to set up SMBtcon!\n");
 
p = SMB_VWV(server->packet);
 
if (server->protocol <= PROTOCOL_COREPLUS)
{
word max_xmit;
 
p = smb_decode_word(p, &max_xmit);
server->max_xmit = max_xmit;
 
if (server->max_xmit > given_max_xmit)
{
server->max_xmit = given_max_xmit;
}
} else
{
p += 2;
}
 
p = smb_decode_word(p, &server->tid);
 
/* Ok, everything is fine. max_xmit does not include */
/* the TCP-SMB header of 4 bytes. */
server->max_xmit += 4;
 
DPRINTK("max_xmit = %d, tid = %d\n", server->max_xmit, server->tid);
 
/* Now make a new packet with the correct size. */
smb_vfree(server->packet);
server->packet = NULL;
 
server->packet = smb_vmalloc(server->max_xmit);
if (server->packet == NULL)
{
printk("smb_proc_connect: No memory left in end of "
"connection phase :-(\n");
smb_dont_catch_keepalive(server);
goto fail;
}
server->packet_size = server->max_xmit;
 
DPRINTK("smb_proc_connect: Normal exit\n");
return 0;
 
fail:
server->state = CONN_INVALID;
return result;
}
 
/* smb_proc_reconnect: server->packet is allocated with
server->max_xmit bytes if and only if we return >= 0 */
int
smb_proc_connect(struct smb_server *server)
{
int result;
smb_lock_server(server);
 
result = smb_proc_reconnect(server);
 
if ((result < 0) && (server->packet != NULL))
{
smb_vfree(server->packet);
server->packet = NULL;
}
smb_unlock_server(server);
return result;
}
 
int
smb_proc_disconnect(struct smb_server *server)
{
smb_setup_header_exclusive(server, SMBtdis, 0, 0);
return smb_request_ok_unlock(server, SMBtdis, 0, 0);
}
/ioctl.c
0,0 → 1,35
/*
* ioctl.c
*
* Copyright (C) 1995, 1996 by Volker Lendecke
*
*/
 
#include <asm/segment.h>
#include <linux/errno.h>
#include <linux/fs.h>
#include <linux/smb_fs.h>
#include <linux/ioctl.h>
#include <linux/sched.h>
#include <linux/mm.h>
 
int
smb_ioctl(struct inode *inode, struct file *filp,
unsigned int cmd, unsigned long arg)
{
int result;
 
switch (cmd)
{
case SMB_IOC_GETMOUNTUID:
if ((result = verify_area(VERIFY_WRITE, (uid_t *) arg,
sizeof(uid_t))) != 0)
{
return result;
}
put_fs_word(SMB_SERVER(inode)->m.mounted_uid, (uid_t *) arg);
return 0;
default:
return -EINVAL;
}
}
/mmap.c
0,0 → 1,144
/*
* mmap.c
*
* Copyright (C) 1995, 1996 by Paal-Kr. Engstad and Volker Lendecke
*
*/
 
/*
* uClinux revisions for NO_MM
* Copyright (C) 1998 Kenneth Albanowski <kjahds@kjahds.com>,
* The Silver Hammer Group, Ltd.
*/
#include <linux/stat.h>
#include <linux/sched.h>
#include <linux/kernel.h>
#include <linux/mm.h>
#include <linux/shm.h>
#include <linux/errno.h>
#include <linux/mman.h>
#include <linux/string.h>
#include <linux/malloc.h>
#include <linux/smb_fs.h>
#include <linux/fcntl.h>
 
#include <asm/segment.h>
#include <asm/system.h>
 
#ifndef NO_MM
 
/*
* Fill in the supplied page for mmap
*/
static unsigned long
smb_file_mmap_nopage(struct vm_area_struct *area,
unsigned long address, int no_share)
{
struct inode *inode = area->vm_inode;
unsigned long page;
unsigned int clear;
unsigned long tmp;
int n;
int i;
int pos;
 
page = __get_free_page(GFP_KERNEL);
if (!page)
return 0;
address &= PAGE_MASK;
pos = address - area->vm_start + area->vm_offset;
 
clear = 0;
if (address + PAGE_SIZE > area->vm_end)
{
clear = address + PAGE_SIZE - area->vm_end;
}
/* what we can read in one go */
n = SMB_SERVER(inode)->max_xmit - SMB_HEADER_LEN - 5 * 2 - 3 - 10;
 
if (smb_make_open(inode, O_RDONLY) < 0)
{
clear = PAGE_SIZE;
} else
{
 
for (i = 0; i < (PAGE_SIZE - clear); i += n)
{
int hunk, result;
 
hunk = PAGE_SIZE - i;
if (hunk > n)
hunk = n;
 
DDPRINTK("smb_file_mmap_nopage: reading\n");
DDPRINTK("smb_file_mmap_nopage: pos = %d\n", pos);
result = smb_proc_read(SMB_SERVER(inode),
SMB_FINFO(inode), pos, hunk,
(char *) (page + i), 0);
DDPRINTK("smb_file_mmap_nopage: result= %d\n", result);
if (result < 0)
break;
pos += result;
if (result < n)
{
i += result;
break;
}
}
}
 
tmp = page + PAGE_SIZE;
while (clear--)
{
*(char *) --tmp = 0;
}
return page;
}
 
struct vm_operations_struct smb_file_mmap =
{
NULL, /* open */
NULL, /* close */
NULL, /* unmap */
NULL, /* protect */
NULL, /* sync */
NULL, /* advise */
smb_file_mmap_nopage, /* nopage */
NULL, /* wppage */
NULL, /* swapout */
NULL, /* swapin */
};
 
 
/* This is used for a general mmap of a smb file */
int
smb_mmap(struct inode *inode, struct file *file, struct vm_area_struct *vma)
{
DPRINTK("smb_mmap: called\n");
 
/* only PAGE_COW or read-only supported now */
if (vma->vm_flags & VM_SHARED)
return -EINVAL;
if (!inode->i_sb || !S_ISREG(inode->i_mode))
return -EACCES;
if (!IS_RDONLY(inode))
{
inode->i_atime = CURRENT_TIME;
inode->i_dirt = 1;
}
vma->vm_inode = inode;
inode->i_count++;
vma->vm_ops = &smb_file_mmap;
return 0;
}
 
#else /* NO_MM */
 
int
smb_mmap(struct inode *inode, struct file *file, struct vm_area_struct *vma)\
{
return -ENOSYS;
}
 
#endif /* NO_MM */
/.depend
0,0 → 1,87
dir.o: \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/sched.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/errno.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/stat.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/kernel.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/malloc.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/mm.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/smb_fs.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/smbno.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/asm/segment.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/asm/semaphore.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/errno.h
file.o: \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/asm/segment.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/asm/system.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/sched.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/kernel.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/errno.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/fcntl.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/stat.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/mm.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/smb_fs.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/malloc.h
inode.o: \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/module.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/asm/system.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/asm/segment.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/sched.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/smb_fs.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/smbno.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/kernel.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/mm.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/string.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/stat.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/errno.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/locks.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/fcntl.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/malloc.h
ioctl.o: \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/asm/segment.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/errno.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/fs.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/smb_fs.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/ioctl.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/sched.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/mm.h
mmap.o: \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/stat.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/sched.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/kernel.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/mm.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/shm.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/errno.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/mman.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/string.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/malloc.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/smb_fs.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/fcntl.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/asm/segment.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/asm/system.h
proc.o: \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/config.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/fs.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/smbno.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/smb_fs.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/types.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/errno.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/malloc.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/stat.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/fcntl.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/asm/segment.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/asm/string.h
sock.o: \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/sched.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/smb_fs.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/errno.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/socket.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/fcntl.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/stat.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/asm/segment.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/in.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/net.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/mm.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/netdevice.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/net/ip.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/smb.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/smbno.h
/Makefile
0,0 → 1,18
#
# Makefile for the linux smb-filesystem routines.
#
# Note! Dependencies are done automagically by 'make dep', which also
# removes any old dependencies. DON'T put your own dependencies here
# unless it's something special (ie not a .c file).
#
# Note 2! The CFLAGS definitions are now in the main makefile...
 
O_TARGET := smbfs.o
O_OBJS := proc.o sock.o inode.o file.o dir.o ioctl.o mmap.o
M_OBJS := $(O_TARGET)
 
# If you want debugging output, please uncomment the following line
 
# EXTRA_CFLAGS += -DDEBUG_SMB=1 -DDEBUG_SMB_MALLOC=1
 
include $(TOPDIR)/Rules.make

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.