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.