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 |