This is smb_abstraction.c in view mode; [Download] [Up]
/*
* Name: smb_abstraction.c
* Description: Smb abstraction layer.
* Author: Christian Starkjohann <cs@hal.kph.tuwien.ac.at>
* Date: 1996-12-31
* Copyright: GNU-GPL
* Tabsize: 4
*/
#include "syshdr.h"
#include <sys/stat.h>
#include <sys/socket.h>
#include <ctype.h>
#include <netdb.h>
#include <linux/fs.h>
#include <linux/smb.h>
#include <linux/smb_fs.h>
#include <linux/smbno.h>
#include "psinode.h"
#include "my_defines.h"
/*#define DPRINTF(arg) dprintf arg*/
#define DPRINTF(arg)
/* ------------------------------------------------------------------------- */
#define ATTR_CACHE_TIME 5 /* cache attributes for this time */
#define DIR_CACHE_TIME 5 /* cache directories for this time */
#define DIRCACHE_SIZE 64
#define DOS_PATHSEP '\\'
/* ------------------------------------------------------------------------- */
extern int smb_proc_setattrE(struct smb_server *server, word fid,
struct smb_dirent *new_entry);
extern int smb_proc_setattr_core(struct smb_server *server, const char *path,
int len, struct smb_dirent *new_finfo);
extern int smb_proc_getattrE(struct smb_server *server,
struct smb_dirent *entry);
extern int smb_proc_getattr_core(struct smb_server *server, const char *path,
int len, struct smb_dirent *entry);
/* ------------------------------------------------------------------------- */
typedef struct dircache{
struct smb_dirent cache[DIRCACHE_SIZE];
int base;
int len;
int eof; /* cache end is eof */
long created_at; /* for invalidation */
struct smba_file *cache_for; /* owner of this cache */
}dircache_t;
/* opaque structures for server and files: */
struct smba_server{
struct smba_server *next;
struct smba_server **prevnext; /* doubly linked list */
struct smb_server server;
list_t open_files;
unsigned supports_E :1;
unsigned supports_E_known :1;
};
struct smba_file{
struct smba_file *next; /* doubly linked list of open files */
struct smba_file **prevnext;
struct smba_server *server;
struct smb_dirent dirent;
long attr_time; /* time when dirent was read */
char attr_dirty; /* attribute cache is dirty */
char is_valid; /* server was down, entry removed, ... */
dircache_t *dircache; /* content cache for directories */
};
#include "smb_abstraction.h"
static dircache_t the_dircache;
list_t servers;
/* ------------------------------------------------------------------------- */
static inline void str_upper(char *name)
{
while(*name){
*name = toupper(*name);
name ++;
}
}
static inline char *my_strdup(const char *s)
{
return strcpy(malloc(strlen(s) + 1), s);
}
static inline int uncase_strcmp(const char *s1, const char *s2)
{
for(;;){
if(*s1 == 0 && *s2 == 0)
return 0;
if(tolower(*s1) != tolower(*s2))
return 1;
s1++; s2++;
}
}
/* ------------------------------------------------------------------------- */
int smba_connect(smba_connect_parameters_t *p,int use_E,smba_server_t **result)
{
smba_server_t *res = calloc(1, sizeof(*res));
struct smb_mount_data data;
int errnum = -1;
char hostname[MAXHOSTNAMELEN + 1];
struct hostent *h;
*result = res;
memset(&data, 0, sizeof(struct smb_mount_data));
memset(hostname, 0, sizeof(hostname));
gethostname(hostname, MAXHOSTNAMELEN);
if((h = gethostbyname(p->server_ipname)) == NULL){
eprintf("%s: unknown host\n", p->server_ipname);
goto error_occured;
}
data.addr.sin_family = AF_INET;
data.addr.sin_addr.s_addr = ((struct in_addr *)(h->h_addr))->s_addr;
data.addr.sin_port = htons(p->port > 0 ? p->port : SMB_PORT);
data.fd = socket(AF_INET, SOCK_STREAM, 0);
if (data.fd == -1) {
eprintf("smba_connect: socket: [%d] %s\n", errno, strerror(errno));
goto error_occured;
}
data.version = SMB_MOUNT_VERSION;
strncpy(data.service, p->service, sizeof(data.service));
strncpy(data.root_path, p->root_path, sizeof(data.root_path));
strncpy(data.username, p->username, sizeof(data.username));
strncpy(data.password, p->password, sizeof(data.password));
if(p->max_xmit > 0)
data.max_xmit = p->max_xmit;
else
data.max_xmit = 4070;
strncpy(data.server_name, p->server_name, sizeof(data.server_name));
strncpy(data.client_name, p->client_name, sizeof(data.client_name));
if(data.server_name[0] == 0){
if(strlen(p->server_ipname) > 16){
eprintf("server name too long as a netbios name: %s\n",
p->server_ipname);
goto error_occured;
}
strcpy(data.server_name, p->server_ipname);
str_upper(data.server_name);
}
if(data.client_name[0] == 0) {
if(strlen(hostname) > 16) {
eprintf("my hostname name too long for netbios: %s\n", hostname);
goto error_occured;
}
strcpy(data.client_name, hostname);
str_upper(data.client_name);
}
res->server.m = data;
list_init(&res->open_files);
if((errnum = smb_proc_connect(&res->server)) < 0){
eprintf("error connecting to server: [%d] %s\n",
-errnum, strerror(-errnum));
goto error_occured;
}
res->server.case_handling = res->server.protocol >= PROTOCOL_LANMAN2 ?
CASE_DEFAULT : CASE_LOWER;
list_append(&servers, res);
if(!use_E){
res->supports_E_known = 1;
}
return 0;
error_occured:
free(res);
return errnum;
}
/* ------------------------------------------------------------------------- */
int smba_disconnect(smba_server_t *server)
{
if(server->open_files.head != NULL){
eprintf("smba_disconnect: still files open\n");
return -1;
}
list_remove_element(&servers, server);
close(server->server.m.fd);
free(server);
return 0;
}
/* ------------------------------------------------------------------------- */
static inline int make_open(smba_file_t *f, int need_fid)
{
int errnum = 0;
smba_server_t *s;
if(!f->is_valid || (need_fid && !f->dirent.opened)){
s = f->server;
if(!f->is_valid || f->attr_time == -1
|| time(NULL) - f->attr_time > ATTR_CACHE_TIME){
if((errnum = smb_proc_getattr_core(&s->server, f->dirent.path,
f->dirent.len, &f->dirent)) < 0)
goto error_occured;
}
if((f->dirent.attr & aDIR) == 0){ /* a regular file */
if(need_fid || !s->supports_E_known || s->supports_E){
DPRINTF(("make_open(): opening file %s\n", f->dirent.path));
if((errnum = smb_proc_open(&s->server, f->dirent.path,
f->dirent.len, &f->dirent)) < 0)
goto error_occured;
if(s->supports_E || !s->supports_E_known){
if(smb_proc_getattrE(&s->server, &f->dirent) < 0){
if(!s->supports_E_known){
s->supports_E_known = 1;
s->supports_E = 0;
} /* ignore errors here */
}else{
s->supports_E_known = 1;
s->supports_E = 1;
}
}
}
}else{ /* don't open directory, initialize directory cache */
if(f->dircache != NULL){
f->dircache->cache_for = NULL;
f->dircache->len = 0;
f->dircache = NULL;
}
}
f->attr_time = time(NULL);
f->is_valid = 1;
}
error_occured:
return errnum;
}
/* ------------------------------------------------------------------------- */
int smba_open(smba_server_t *s, const char *name, smba_file_t **file)
{
smba_file_t *f;
int errnum;
*file = f = calloc(1, sizeof(*f));
f->dirent.path = my_strdup(name);
f->dirent.len = strlen(name);
f->server = s;
if((errnum = make_open(f, 0)) < 0)
goto error_occured;
list_append(&s->open_files, f);
return 0;
error_occured:
free(f->dirent.path);
free(f);
return errnum;
}
/* ------------------------------------------------------------------------- */
int smba_file_importance(smba_file_t *f)
{
DPRINTF(("smba_file_importance(): file ->%s<-\n", f->dirent.path));
if(!f->is_valid)
return 0;
/* entries with directory cache are most important */
if(f->dircache != NULL)
return 2;
/* open entries are important, too */
if(f->dirent.opened)
return 1;
return 0;
}
/* ------------------------------------------------------------------------- */
static int write_attr(smba_file_t *f)
{
int errnum;
DPRINTF(("write_attr(): file %s\n", f->dirent.path));
if((errnum = make_open(f, 0)) < 0)
return errnum;
if(f->dirent.opened && f->server->supports_E){
errnum = smb_proc_setattrE(&f->server->server, f->dirent.fileid,
&f->dirent);
}else{
errnum = smb_proc_setattr_core(&f->server->server, f->dirent.path,
f->dirent.len, &f->dirent);
}
if(errnum < 0){
f->attr_time = -1;
}else{
f->attr_dirty = 0;
}
return errnum;
}
/* ------------------------------------------------------------------------- */
int smba_close(smba_file_t *f)
{
int errnum = 0;
list_remove_element(&f->server->open_files, f);
/* should not be necessary because smba_setattr() writes through
* if(f->attr_dirty){
* write_attr(f);
* }
*/
if(f->dirent.opened){
DPRINTF(("smba_close(): closing file %s\n", f->dirent.path));
errnum = smb_proc_close(&f->server->server, f->dirent.fileid,
f->dirent.mtime);
}
if(f->dirent.path != NULL)
free(f->dirent.path);
if(f->dircache != NULL){
f->dircache->cache_for = NULL;
f->dircache->len = 0;
f->dircache = NULL;
}
free(f);
return errnum;
}
/* ------------------------------------------------------------------------- */
int smba_read(smba_file_t *f, char *data, long len, long offset)
{
int maxsize, count, totalcount, result = 0;
char *rpos;
if((result = make_open(f, 1)) < 0)
return result;
if(f->server->server.blkmode & 1){
result = smb_proc_read_raw(&f->server->server, &f->dirent, offset,
len, data);
DPRINTF(("smb_proc_read_raw(%s)->%d\n", f->dirent.path, result));
}
if (result <= 0) {
totalcount = len;
rpos = data;
maxsize = f->server->server.max_xmit - SMB_HEADER_LEN - 5 * 2 - 5;
do{
count = totalcount > maxsize ? maxsize : totalcount;
result = smb_proc_read(&f->server->server, &f->dirent,
offset, count, rpos, 0);
if(result < 0)
break;
totalcount -= count;
offset += count;
rpos += count;
}while(totalcount > 0);
}
if(result < 0)
return result;
return len;
}
/* ------------------------------------------------------------------------- */
int smba_write(smba_file_t *f, char *data, long len, long offset)
{
int newlen, maxsize, totalcount, count, result = 0;
if((result = make_open(f, 1)) < 0)
return result;
newlen = f->dirent.size;
if(offset + len > newlen)
newlen = offset + len;
if(f->server->server.blkmode & 2){
result = smb_proc_write_raw(&f->server->server, &f->dirent, offset,
len, data);
}
if (result <= 0){
maxsize = f->server->server.max_xmit - SMB_HEADER_LEN - 5 * 2 - 5;
totalcount = len;
do{
count = totalcount > maxsize ? maxsize : totalcount;
result = smb_proc_write(&f->server->server, &f->dirent, offset,
count, data);
if(result < 0)
break;
totalcount -= count;
offset += count;
data += count;
}while(totalcount > 0);
}
f->dirent.mtime = time(NULL);
if(result < 0){
f->attr_time = -1;
return result;
}
f->dirent.size = newlen;
return len;
}
/* ------------------------------------------------------------------------- */
int smba_getattr(smba_file_t *f, smba_stat_t *data)
{
long now = time(NULL);
int errnum = 0;
if((errnum = make_open(f, 0)) < 0)
return errnum;
if(f->attr_time == -1 || (now - f->attr_time) > ATTR_CACHE_TIME){
DPRINTF(("smba_getattr(): file %s\n", f->dirent.path));
if(f->dirent.opened && f->server->supports_E){
errnum = smb_proc_getattrE(&f->server->server, &f->dirent);
}else{
errnum = smb_proc_getattr_core(&f->server->server,
f->dirent.path, f->dirent.len, &f->dirent);
}
if(errnum >= 0)
f->attr_time = now;
}
data->is_dir = (f->dirent.attr & aDIR) != 0;
data->is_wp = (f->dirent.attr & aRONLY) != 0;
data->is_hidden = (f->dirent.attr & aHIDDEN) != 0;
data->is_system = (f->dirent.attr & aSYSTEM) != 0;
data->is_volid = (f->dirent.attr & aVOLID) != 0;
data->size = f->dirent.size;
data->atime = f->dirent.atime;
data->ctime = f->dirent.ctime;
data->mtime = f->dirent.mtime;
return errnum;
}
/* ------------------------------------------------------------------------- */
int smb_make_open(struct inode *i, int right)
{
eprintf("dummy function smb_make_open() called\n");
return -1;
}
/* ------------------------------------------------------------------------- */
int smba_setattr(smba_file_t *f, smba_stat_t *data)
{
if(data->atime != -1)
f->dirent.atime = data->atime;
if(data->ctime != -1)
f->dirent.ctime = data->ctime;
if(data->mtime != -1)
f->dirent.mtime = data->mtime;
f->dirent.attr &= ~aRONLY;
if(data->is_wp)
f->dirent.attr |= aRONLY;
f->attr_dirty = 1;
return write_attr(f);
}
/* ------------------------------------------------------------------------- */
int smba_touch(smba_file_t *f)
{
int errnum = 0;
f->dirent.mtime = time(NULL);
f->attr_dirty = 1;
return errnum;
}
/* ------------------------------------------------------------------------- */
int smba_name(smba_file_t *file, char **name)
{
*name = file->dirent.path;
return 0;
}
/* ------------------------------------------------------------------------- */
int smba_readdir(smba_file_t *f, long offs, void *d, smba_callback_t callback)
{
struct inode dir_ino;
struct smb_inode_info dir_info;
int index, rval = 0, o, eof, count = 0;
long now = time(NULL);
if((rval = make_open(f, 0)) < 0)
return rval;
if(f->dircache == NULL){ /* get a cache */
if(the_dircache.cache_for != NULL){
the_dircache.cache_for->dircache = NULL; /* steal it */
}
the_dircache.eof = the_dircache.len = the_dircache.base = 0;
the_dircache.cache_for = f;
f->dircache = &the_dircache;
DPRINTF(("smba_readdir(): stealing cache\n"));
}else{
if((now - f->dircache->created_at) >= DIR_CACHE_TIME){
f->dircache->eof = f->dircache->len = f->dircache->base = 0;
DPRINTF(("smba_readdir(): cache outdated\n"));
}
}
for(index = offs;;index++){
if(index < f->dircache->base /* fill cache if necessary */
|| index >= f->dircache->base + f->dircache->len){
if(index >= f->dircache->base + f->dircache->len
&& f->dircache->eof)
break; /* nothing more to read */
DPRINTF(("smba_readdir(): cachefill for %s\n", f->dirent.path));
DPRINTF(("\tbase was: %d, len was: %d, newbase=%d\n",
f->dircache->base, f->dircache->len, index));
f->dircache->len = 0;
f->dircache->base = index;
/* the following line is an evil hack!!! */
dir_ino.u.generic_ip = (char *)&f->dirent -
(char *)&dir_info.finfo + (char *)&dir_info;
rval = smb_proc_readdir(&f->server->server, &dir_ino, index,
DIRCACHE_SIZE, f->dircache->cache);
if(rval <= 0)
break;
f->dircache->len = rval;
f->dircache->eof = rval < DIRCACHE_SIZE;
f->dircache->created_at = now;
DPRINTF(("smba_readdir(): cachefill with %d entries\n", rval));
}
o = index - f->dircache->base;
eof = o >= (f->dircache->len - 1) && f->dircache->eof;
count++;
DPRINTF(("smba_readdir(): delivering ->%s<-, index=%d, eof=%d\n",
f->dircache->cache[o].path, index, eof));
if((*callback)(d, index, index+1, f->dircache->cache[o].path, eof))
break;
}
if(rval < 0)
return rval;
else
return count;
}
/* ------------------------------------------------------------------------- */
static void invalidate_dircache(void)
{
the_dircache.eof = the_dircache.len = the_dircache.base = 0;
}
/* ------------------------------------------------------------------------- */
int smba_create(smba_file_t *dir, const char *name, smba_stat_t *attr)
{
struct smb_dirent entry;
char *path;
int errnum;
if((errnum = make_open(dir, 0)) < 0)
return errnum;
memset(&entry, 0, sizeof(entry));
if(attr->is_wp)
entry.attr |= aRONLY;
entry.atime = entry.mtime = entry.ctime = time(NULL);
path = malloc(strlen(name) + dir->dirent.len + 2);
memcpy(path, dir->dirent.path, dir->dirent.len);
path[dir->dirent.len] = DOS_PATHSEP;
strcpy(&path[dir->dirent.len + 1], name);
errnum = smb_proc_create(&dir->server->server, path, strlen(path), &entry);
free(path);
invalidate_dircache();
return errnum;
}
/* ------------------------------------------------------------------------- */
int smba_mkdir(smba_file_t *dir, const char *name, smba_stat_t *attr)
{
char *path;
int errnum;
if((errnum = make_open(dir, 0)) < 0)
return errnum;
path = malloc(strlen(name) + dir->dirent.len + 2);
memcpy(path, dir->dirent.path, dir->dirent.len);
path[dir->dirent.len] = DOS_PATHSEP;
strcpy(&path[dir->dirent.len + 1], name);
errnum = smb_proc_mkdir(&dir->server->server, path, strlen(path));
free(path);
invalidate_dircache();
return errnum;
}
/* ------------------------------------------------------------------------- */
static void close_path(smba_server_t *s, char *path)
{
smba_file_t *p;
for(p=s->open_files.head;p!=NULL;p=p->next){
if(p->is_valid && uncase_strcmp(p->dirent.path, path) == 0){
if(p->dirent.opened)
smb_proc_close(&s->server, p->dirent.fileid, p->dirent.mtime);
p->dirent.opened = 0;
p->is_valid = 0;
}
}
}
/* ------------------------------------------------------------------------- */
int smba_remove(smba_server_t *s, char *path)
{
close_path(s, path);
invalidate_dircache();
return smb_proc_unlink(&s->server, path, strlen(path));
}
/* ------------------------------------------------------------------------- */
int smba_rmdir(smba_server_t *s, char *path)
{
close_path(s, path);
invalidate_dircache();
return smb_proc_rmdir(&s->server, path, strlen(path));
}
/* ------------------------------------------------------------------------- */
int smba_rename(smba_server_t *s, char *from, char *to)
{
close_path(s, from);
invalidate_dircache();
smba_remove(s, to); /* ignore the expected error */
return smb_proc_mv(&s->server, from, strlen(from), to, strlen(to));
}
/* ------------------------------------------------------------------------- */
int smba_statfs(smba_server_t *s, long *bsize, long *blocks, long *bfree)
{
struct smb_dskattr dskattr;
struct super_block super;
struct smb_sb_info sb;
int errnum = 0;
super.u.generic_sbp = &sb;
sb.s_server = s->server;
errnum = smb_proc_dskattr(&super, &dskattr);
if(errnum)
return errnum;
*bsize = dskattr.blocksize * dskattr.allocblocks;
*blocks = dskattr.total;
*bfree = dskattr.free;
return 0;
}
/* ------------------------------------------------------------------------- */
void smb_invalidate_all_inodes(struct smb_server *server)
{
smba_server_t *p;
smba_file_t *f;
invalidate_dircache();
for(p=servers.head;p!=NULL;p=p->next){
if(&p->server == server){
for(f=p->open_files.head;f!=NULL;f=f->next){
f->dirent.opened = 0;
f->is_valid = 0;
}
}
}
}
/* ------------------------------------------------------------------------- */
void smba_regular(void) /* write back all dirty attributes */
{
smba_server_t *s;
smba_file_t *f;
for(s=servers.head;s!=NULL;s=s->next){
for(f=s->open_files.head;f!=NULL;f=f->next){
if(f->attr_dirty)
write_attr(f);
}
}
}
/* ------------------------------------------------------------------------- */
void smba_init(void)
{
int i;
list_init(&servers);
for(i=0;i<DIRCACHE_SIZE;i++){
the_dircache.cache[i].path = malloc(SMB_MAXNAMELEN + 1);
}
}
/* ------------------------------------------------------------------------- */
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.