diff --git a/client/util.c b/client/util.c new file mode 100644 index 0000000..0cab9d8 --- /dev/null +++ b/client/util.c @@ -0,0 +1,716 @@ + /*********************************************************************\ + * Copyright (c) 1991 by Wen-King Su (wen-king@vlsi.cs.caltech.edu) * + * * + * You may copy or modify this file in any manner you wish, provided * + * that this notice is always included, and that you hold the author * + * harmless for any loss or damage resulting from the installation or * + * use of this software. * + \*********************************************************************/ + +#include "tweak.h" +#include "client_def.h" +#include "c_extern.h" +#ifdef STDC_HEADERS +#include +#endif +#include "my-string.h" + +static int env_dir_malloced = 0; +const char *env_dir = "/"; +const char *env_passwd = "\0"; +const char *env_myport; +const char *env_host; +const char *env_port; +const char *env_local_dir; +int env_timeout; +unsigned short client_buf_len; +unsigned short client_net_len; + +#define TWOGIGS 0x7fffffffUL +#define FOURGIGS 0xffffffffUL + +/* returns mallocated abs. path merged from s2 and env.dir */ +char *util_abs_path PROTO1(const char *, s2) +{ + char *path, *s, *d, *t; + + if(!env_dir) env_dir = ""; + if(!s2) s2 = ""; + + if(*s2 == '/') { + path = malloc(strlen(s2)+2+strlen(env_passwd)+1); + sprintf(path,"/%s",s2); + } else { + path = malloc(strlen(env_dir)+strlen(s2)+3+strlen(env_passwd)+1); + sprintf(path,"/%s/%s",env_dir,s2); + } + + for(t = path; *t; ) { + if(t[0] == '/') { + while(t[1] == '/') for(d = t, (s = t+1); (*d++ = *s++); ); + if(t != path && t[1] == 0) { t[0] = 0; return(path); } + } + if(t[0] == '.' && t[1] == '.') { + if(t-1 == path && t[2] == 0 ) { + *t = 0; + return(path); + } + if(t-1 == path && t[2] == '/') { + for(d = t, s = t + 3; (*d++ = *s++); ); + continue; + } + if(t[-1] == '/' && (t[2] == '/' || t[2] == 0)) { + s = t + 2; /* point to either slash or nul */ + t -= 2; /* guaranteed that t >= path here */ + while(t > path && t[0] != '/') t--; + if(t != path || *s == '/') for(d = t; (*d++ = *s++); ); + else { + t[1] = 0; + return(path); + } + continue; + } + } + if(t[0] == '.') { + if(t-1 == path && t[1] == 0 ) { + *t = 0; + return(path); + } + if(t-1 == path && t[1] == '/') { + for(d = t, s = t + 2; (*d++ = *s++); ); + continue; + } + if(t[-1] == '/' && (t[1] == '/' || t[1] == 0)) { + s = t + 1; /* point to either slash or nul */ + for(d = t-1; (*d++ = *s++); ); + t--; + continue; + } + } + t++; + } + return(path); +} + +static int util_split_path PROTO4(char *, path, char **, p1, char **, p2, + char **, p3) +{ + char *s; + static char junk; + + *p1 = "/"; + if(*path == '/') { + *p2 = path; + *p3 = path+1; + } else { + *p2 = &junk; + *p3 = path; + } + + for(s = *p3; *s; s++) { + if(*s == '/') { + *p1 = path; + *p2 = s; + *p3 = s+1; + } + } + + if (**p3 == '\0') *p3 = "."; + return(1); +} + +/* return current working directory (from environment) */ +char *util_getwd PROTO1(char *, p) +{ + if(p) strcpy(p,env_dir); + return(p); +} + +static RDIRENT **get_dir_blk PROTO1(char *, path) +{ + RDIRENT **dp; + char *p1, *p2, *fpath, buf[2*UBUF_SPACE]; + unsigned long pos; + int cnt, k, len, rem, acc, at_eof, rlen; + UBUF *ub; + + fpath = util_abs_path(path); + + for(pos = 0, at_eof = acc = cnt = 0; ; ) { + while((acc < UBUF_SPACE) && !at_eof) { + ub = client_interact(CC_GET_DIR,pos, strlen(fpath), + (unsigned char *)fpath+1, 2, + (unsigned char *)&client_net_len); + + if(ub->cmd == CC_ERR) { + fprintf(stderr,"%s: %s\n",path, ub->buf); + free(fpath); + errno = EACCES; + return((RDIRENT **) 0); + } + + rlen = BB_READ2(ub->bb_len); + if(rlen < RDHSIZE) at_eof = 1; + for(p1 = ub->buf, p2 = buf + acc, k = rlen; k--; ) *p2++ = *p1++; + acc += rlen; + pos += rlen; + } + + if(acc >= UBUF_SPACE) len = UBUF_SPACE; + else len = acc; + + for(p2 = buf, rem = len, k = 0; ; k++) { + if(rem < RDHSIZE) break; + if(((RDIRENT *) p2)->type == RDTYPE_SKIP) break; + if(((RDIRENT *) p2)->type == RDTYPE_END ) { k++; break; } + p2 += RDHSIZE; rem -= (RDHSIZE+1); + while(*p2++) rem--; + while((p2 - buf) & 3) { + p2++; + rem--; + } + } + + p1 = malloc(p2-buf); + if(cnt) dp = (RDIRENT **) realloc(dp,(cnt+k+1)*sizeof(RDIRENT *)); + else dp = (RDIRENT **) malloc((cnt+k+1)*sizeof(RDIRENT *)); + + if(!p1 || !dp) { + free(fpath); + fputs("directory reading out of memory\n",stderr); + return((RDIRENT **) 0); + } + + for(p2 = buf, rem = len; ; cnt++) { + if(rem < RDHSIZE) break; + if(((RDIRENT *) p2)->type == RDTYPE_SKIP) break; + if(((RDIRENT *) p2)->type == RDTYPE_END) { + dp[cnt] = 0; + return(dp); + } + dp[cnt] = (RDIRENT *) p1; + + for(k = RDHSIZE, rem -= (RDHSIZE+1); k--; *p1++ = *p2++); + while( (*p1++ = *p2++) ) rem--; + while((p2 - buf) & 3) { + p2++; + p1++; + rem--; + } + } + + if(acc < UBUF_SPACE) { + dp[cnt] = 0; + return(dp); + } + for(p1 = buf + UBUF_SPACE, p2 = buf, k = (acc -= UBUF_SPACE); k--;) + *p2++ = *p1++; + } +} + +static int util_download_main PROTO5(char *, path, char *, fpath, FILE *, fp, + unsigned long, start_from, int, cmd) +{ + unsigned long pos, started_from = start_from, downloaded; + unsigned tmax, wrote, sent_time, rlen; + UBUF *ub; + time_t t = time(NULL); + + for(tmax = 1, pos = start_from, sent_time = 0; ;) { + ub = client_interact(cmd,pos,strlen(fpath),(unsigned char *)fpath+1, 2, + (unsigned char *)&client_net_len); + + if(client_trace && (udp_sent_time != sent_time)) { + sent_time = udp_sent_time; + if(client_buf_len == UBUF_SPACE) fprintf(stderr,"\r%luk ",1+(pos>>10)); + else fprintf(stderr,"\r%lu ", pos); + fflush(stderr); + } + + if(ub->cmd == CC_ERR) { + fprintf(stderr,"downloading %s: %s\n",path,ub->buf); + return(-1); + } + + rlen = BB_READ2(ub->bb_len); + wrote = fwrite(ub->buf,1,rlen,fp); + /* check for long integer pos overflow */ +#if SIZEOF_LONG > 4 + if(pos+bytes>FOURGIGS) + break; +#else + if(pos+wrote>10), path, downloaded/t); + fflush(stderr); + } + + return(0); +} + +int util_download PROTO3(char *, path, FILE *, fp, unsigned long, start_from) +{ + int code, len; + char *fpath; + + fpath = util_abs_path(path); + if(*env_passwd) { + strcat(fpath, "\n"); + strcat(fpath, env_passwd); + len = strlen(fpath); + } + code = util_download_main(path, fpath, fp, start_from, CC_GET_FILE); + free(fpath); + return(code); +} + +int util_grab_file PROTO3(char *, path, FILE *, fp, unsigned long, start_from) +{ + int code, len; + char *fpath; + UBUF *ub; + + fpath = util_abs_path(path); + if(*env_passwd) { + strcat(fpath, "\n"); + strcat(fpath, env_passwd); + len = strlen(fpath); + } + code = util_download_main(path, fpath, fp, start_from, CC_GRAB_FILE); + if(code) { + free(fpath); + return(code); + } + + ub = client_interact(CC_GRAB_DONE, 0L, len, (unsigned char *)fpath+1, 0, + (unsigned char *)NULLP); + + if(ub->cmd == CC_ERR) { + fprintf(stderr,"Warning, unexpected grab error: %s\n",ub->buf); + } + + free(fpath); + return(code); +} + +int util_upload PROTO3(char *, path, FILE *, fp, time_t , stamp) +{ + unsigned long pos; + unsigned bytes, first, tmax, sent_time; + char *fpath, buf[UBUF_SPACE]; + UBUF *ub; + time_t t = time(NULL); + char *dpath,*p1,*p2; + unsigned char flags; + struct stat sb; + + fpath = util_abs_path(path); + util_split_path(fpath,&dpath,&p1,&p2); + *p1='\0'; + + /* check if we have enough rights to create file */ + ub = client_interact(CC_GET_PRO,0, strlen(dpath), (unsigned char *)dpath+1, 0, + (unsigned char *)NULLP); + free(fpath); + fpath = util_abs_path(path); + if(ub->cmd == CC_ERR) + { + fprintf(stderr,"uploading %s: %s\n",path,ub->buf); + free(fpath); + return(1); + } + /* extract flags from server reply */ + bytes = BB_READ2(ub->bb_len); + pos = BB_READ4(ub->bb_pos); + if(pos) + { + /* flags are in the reply */ + flags = *(ub->buf+bytes); + /* check for required flags */ + if(! (flags & DIR_OWNER)) + { + if( ! (flags & DIR_ADD)) + { + fprintf(stderr,"No permission for adding files.\n"); + free(fpath); + return(1); + } + } else + { + /* owner of directory */ + flags|=DIR_DEL|DIR_ADD; + } + /* check if we can overwrite an existing file */ + if(! (flags & DIR_DEL)) + { + if(util_stat(fpath,&sb)==0) + { + fprintf(stderr,"No permission for overwriting: %s\n",fpath); + free(fpath); + return(1); + } + } + } + + for(tmax = 1, sent_time = 0, pos = 0, first = 1; ; first = 0) + { + if((bytes = fread(buf,1,client_buf_len,fp)) || first) + { + ub = client_interact(CC_UP_LOAD,pos, bytes, (unsigned char *)buf, 0, + (unsigned char *)NULLP); + if(client_trace && (udp_sent_time != sent_time)) + { + sent_time = udp_sent_time; + if(client_buf_len == UBUF_SPACE) + fprintf(stderr,"\r%luk ",1+(pos>>10)); + else fprintf(stderr,"\r%lu ", pos ); + fflush(stderr); + } + } + else + { + BB_WRITE4(buf,stamp); + ub = client_interact(CC_INSTALL,pos,strlen(fpath), + (unsigned char *)fpath+1, stamp==0?0:4, + (unsigned char *)buf); + } + if(ub->cmd == CC_ERR) { + fprintf(stderr,"uploading %s: %s\n",path,ub->buf); + free(fpath); + return(1); + } + + if(!bytes && !first) break; +#if SIZEOF_LONG > 4 + if(pos+bytes>FOURGIGS) + break; +#else + if(pos+bytes>10), path, pos/t); + fflush(stderr); + } + free(fpath); + return(0); +} + +static void util_get_env PROTO0((void)) +{ + char *p; + + if(!(env_host = getenv("FSP_HOST"))) { + fputs("No FSP_HOST specified.\n",stderr); + exit(1); + } + if(!(env_port = getenv("FSP_PORT"))) { + fputs("No FSP_PORT specified.\n",stderr); + exit(1); + } + if(!(env_dir = getenv("FSP_DIR"))) { + fputs("No FSP_DIR specified.\n",stderr); + exit(1); + } + if(!(env_myport = getenv("FSP_LOCALPORT"))) env_myport = "0"; + if(!(env_passwd = getenv("FSP_PASSWORD"))) env_passwd = "\0"; + client_trace = !!getenv("FSP_TRACE"); + if( (p = getenv("FSP_BUF_SIZE")) ) client_buf_len = atoi(p); + else client_buf_len = UBUF_SPACE; + + if(client_buf_len > UBUF_SPACE) client_buf_len = UBUF_SPACE; + client_net_len = htons(client_buf_len); + + if( (p = getenv("FSP_DELAY")) ) target_delay = atol(p); + if(target_delay < MIN_DELAY) target_delay = MIN_DELAY; + + if(!(env_local_dir = getenv("FSP_LOCAL_DIR"))) env_local_dir="."; + + if(!(p = getenv("FSP_TIMEOUT"))) env_timeout = 180; + else env_timeout = atol(p); +} + + +void env_client PROTO0((void)) +{ + util_get_env(); + init_client(env_host,atoi(env_port),atoi(env_myport)); +} + +static DDLIST *ddroot = 0; + +RDIR *util_opendir PROTO1(char *, path) +{ + char *fpath; + RDIRENT **dep; + DDLIST *ddp; + RDIR *rdirp; + + fpath = util_abs_path(path); + + for(ddp = ddroot; ddp; ddp = ddp->next) + if(!strcmp(ddp->path,fpath)) break; + + if(!ddp) { + if(!(dep = get_dir_blk(fpath))) return((RDIR *) 0); + ddp = (DDLIST *) malloc(sizeof(DDLIST)); + ddp->dep_root = dep; + ddp->path = fpath; + ddp->ref_cnt = 0; + ddp->next = ddroot; + ddroot = ddp; + } else free(fpath); + + ddp->ref_cnt++; + + rdirp = (RDIR *) malloc(sizeof(RDIR)); + rdirp->ddp = ddp; + rdirp->dep = ddp->dep_root; + return(rdirp); +} + +void util_closedir PROTO1(RDIR *, rdirp) +{ + rdirp->ddp->ref_cnt--; + free(rdirp); +} + +rdirent *util_readdir PROTO1(RDIR *, rdirp) +{ + static rdirent rde; + RDIRENT **dep; + + dep = rdirp->dep; + + if(!*dep) return((rdirent *) 0); + + rde.d_fileno = 10; + rde.d_rcdlen = 10; + rde.d_namlen = strlen((*dep)->name); + rde.d_name = (*dep)->name; + rdirp->dep = dep+1; + + return(&rde); +} + + +int util_stat PROTO2(char *, path, struct stat *, sbuf) +{ + static int statworks=1; + RDIR *drp; + RDIRENT **dep; + DDLIST *ddp; + UBUF *ub; + char *fpath, *ppath, *p1, *pfile; + int cached=0; + + fpath = util_abs_path(path); + + if(!strcmp(fpath,env_dir)) { + ppath = fpath; + pfile = "."; + } else { + util_split_path(fpath,&ppath,&p1,&pfile); + *p1='\0'; + } + + /* check if we have ppath cached */ + /* printf("Finding `%s` in cache.\n",ppath); */ + for(ddp = ddroot; ddp; ddp = ddp->next) + { + /* printf(" we have %s in cache.\n",ddp->path); */ + if(!strcmp(ddp->path,ppath)) + { + cached=1; + break; + } + } + + /* free(fpath); */ /* TODO: memory leak is there, this should be freed! */ + fpath = util_abs_path(path); + + /* if(cached) printf("Record found in cache.\n",ppath); */ + if(statworks && !cached) + { + /* send a new FSP_STAT command to server */ + ub = client_interact(CC_STAT,0L, strlen(fpath), + (unsigned char *) fpath+1, 0, 0); + if(ub->cmd == CC_STAT) { + sbuf->st_uid = 0; + sbuf->st_gid = 0; + sbuf->st_atime = sbuf->st_mtime = + sbuf->st_ctime = BB_READ4((ub->buf)); + sbuf->st_size = BB_READ4((ub->buf+4)); + if((ub->buf[8]) == RDTYPE_DIR) + { + sbuf->st_mode = 0777 | S_IFDIR; + sbuf->st_nlink = 2; + } + else + { + sbuf->st_mode = 0666 | S_IFREG; + sbuf->st_nlink = 1; + } + + if(ub->buf[8]==0) + { + errno = ENOENT; + return -1; + } + return 0; + } + else + { + statworks=0; + } + } /* CC_STAT */ + + if( (drp = util_opendir(ppath)) ) { + for(dep = drp->dep; *dep; dep++) { + if(!strcmp((*dep)->name,pfile)) { + if((*dep)->type & RDTYPE_DIR) sbuf->st_mode = 0777 | S_IFDIR; + else sbuf->st_mode = 0666 | S_IFREG; + + if((*dep)->type & RDTYPE_DIR) sbuf->st_nlink = 2; + else sbuf->st_nlink = 1; + sbuf->st_uid = 0; + sbuf->st_gid = 0; + sbuf->st_size = BB_READ4((*dep)->bb_size); + sbuf->st_atime = sbuf->st_mtime = + sbuf->st_ctime = BB_READ4((*dep)->bb_time); + util_closedir(drp); + free(fpath); + return(0); + } + } + util_closedir(drp); + } + + free(fpath); + errno = ENOENT; + return(-1); +} + +int util_cd PROTO1(char *, p) +{ + char *fpath; + UBUF *ub; + DDLIST *ddp; + + fpath = util_abs_path(p); + for(ddp = ddroot; ddp; ddp = ddp->next) + if(!strcmp(ddp->path,fpath)) break; + + if(!ddp && strcmp(p,".") && strcmp(p,"..")) { + ub = client_interact(CC_GET_DIR,0L, strlen(fpath), + (unsigned char *) fpath+1, 2, + (unsigned char *)&client_net_len); + if(ub->cmd == CC_ERR) { + free(fpath); + fprintf(stderr,"%s: %s\n",p, ub->buf); + errno = EACCES; + return(-1); + } + } + + if(env_dir_malloced) free(env_dir); + env_dir_malloced = 1; + env_dir = fpath; + return(0); +} + +/* Perform a cd, but don't verify path. Assume the path has been + * pre-verified + */ +int util_cd2 PROTO1(char *, p) +{ + char *fpath; + + fpath = util_abs_path(p); + + if(env_dir_malloced) free(env_dir); + env_dir_malloced = 1; + env_dir = fpath; + return(0); +} + +#ifdef PROTOTYPES +void util_process_file(char *path, int mode, + void (*process_file)(char *,struct stat *, int, int), + int (*process_start_dir)(char *,struct stat *,u_long *), + void (*process_end_dir)(char *,int,u_long,int), + int level) +#else +void util_process_file(path, mode, process_file, process_start_dir, + process_end_dir, level) + char *path; + int mode, level; + void (*process_file)(), (*process_end_dir)(); + int (*process_start_dir)(); +#endif +{ + struct stat sbuf; + RDIR *rdir; + struct rdirent *rde; + int pathlen; + char *newname; + u_long sum; + + if (util_stat(path, &sbuf) < 0) { + perror(path); + return; + } + + if (S_ISREG(sbuf.st_mode)) { + if(process_file) (*process_file)(path, &sbuf, mode, level); + } else if (S_ISDIR(sbuf.st_mode)) { + sum = mode; + if (process_start_dir && (*process_start_dir)(path, &sbuf, &sum) < 0) + fprintf(stderr, "skipping remote directory `%s'\n", path); + else { + if ((rdir = util_opendir(path))) { + pathlen = strlen(path); + while ((rde = util_readdir(rdir))) { + /* skip over "." and ".." */ + if (rde->d_name[0] == '.' && + (rde->d_name[1] == '\0' || + (rde->d_name[1] == '.' && rde->d_name[2] == '\0'))) + continue; + newname = malloc(pathlen + rde->d_namlen + 2); + + strcpy(newname, path); + if(newname[pathlen-1] != '/') newname[pathlen] = '/'; + else pathlen--; + strcpy(newname + pathlen + 1, rde->d_name); + util_process_file(newname, mode, process_file, process_start_dir, + process_end_dir, level + 1); + free(newname); + } + util_closedir(rdir); + } + if(process_end_dir) (*process_end_dir)(path, mode, sum, level); + } + } else + fprintf(stderr, "remote file `%s' is not a file or directory!\n",path); +/* free(path); */ +}