void fscreate(Chan *c, char *name, int mode, ulong perm) { Dir *d; Cname *n; if(strcmp(name, ".") == 0 || strcmp(name, "..") == 0) error(Efilename); n = addelem(newcname(FS(c)->name->s), name); osenter(); FS(c)->fd = create(n->s, mode, perm); osleave(); if(FS(c)->fd < 0) { cnameclose(n); fserr(FS(c)); } d = dirfstat(FS(c)->fd); if(d == nil) { cnameclose(n); close(FS(c)->fd); FS(c)->fd = -1; fserr(FS(c)); } c->qid = d->qid; free(d); cnameclose(FS(c)->name); FS(c)->name = n; c->mode = openmode(mode); c->offset = 0; FS(c)->offset = 0; c->flag |= COPEN; }
/** * Copy thread code. This copies one segment in parallel with the other * segments. It is copied in reads and writes of several k-bytes at a time. * * @param[in] arg the file_seg_t pointer describing the segment to copy. */ static void * do_copy(void * arg) { file_seg_t * fseg = arg; size_t const readlim = pc_min(fseg->end - fseg->start, OPT_VALUE_MAX_CHUNK); copy_start(fseg); for (;;) { ssize_t rdlen = fseg->end - fseg->start; if (rdlen > readlim) rdlen = readlim; ssize_t rdct = read(fseg->fdin, fseg->buffer, rdlen); if (rdct <= 0) short_read(fseg, rdlen, rdct); rdlen = write(fseg->fdout, fseg->buffer, rdct); if (rdlen != rdct) fserr(PCOPY_EXIT_FS_ERR_OUT, "write", fseg->dname); fseg->start += rdlen; if (fseg->start >= fseg->end) break; copy_progress(fseg); } copy_wrap(fseg); free(arg); pthread_exit(0); }
/** * strdup or die. Do not return without memory. * * @param[in] istr input string * @results a newly allocated string */ static inline char * strdup_or_die(char const * istr) { char * res = strdup(istr); if (res == NULL) fserr(PCOPY_EXIT_NO_MEM, "strdup", istr); return res; }
static int afs_setcache(int lv, int hv, int lb, int hb) { int ret; ret = fs_setcache (lv, hv, lb, hb); if (ret) fserr(PROGNAME, ret, "."); return ret; }
/** * The input read was short. Formulate a somewhat more complex * "fserr()" exit call. * * @param[in,out] fseg The descriptor of the segment that was being copied. * @param[in] rdlen the length of the attempted read. * @param[in] rdct the count of bytes actually read. * @noreturn */ static void short_read(file_seg_t * fseg, ssize_t rdlen, ssize_t rdct) { static char const fmt[] = "th %2d read %d bytes not %d at 0x%08lX\n"; char msgbf[sizeof(fmt) + 64]; sprintf(msgbf, fmt, fseg->idx, (int)rdct, (int)rdlen, fseg->start); fserr(PCOPY_EXIT_FS_ERR_IN, msgbf, fseg->fname); /* NOTREACHED */ }
long fswrite(Chan *c, void *va, long n, vlong offset) { int r; osenter(); r = pwrite(FS(c)->fd, va, n, offset); osleave(); if(r < 0) fserr(FS(c)); return r; }
int fsstat(Chan *c, uchar *dp, int n) { if(FS(c)->fd >= 0) n = fstat(FS(c)->fd, dp, n); else n = stat(FS(c)->name->s, dp, n); if(n < 0) fserr(FS(c)); /* TO DO: change name to / if rootqid */ return n; }
int fswstat(Chan *c, uchar *dp, int n) { osenter(); if(FS(c)->fd >= 0) n = fwstat(FS(c)->fd, dp, n); else n = wstat(FS(c)->name->s, dp, n); osleave(); if(n < 0) fserr(FS(c)); return n; }
static void afs_wscell (void) { char buf[2048]; int ret; ret = fs_wscell (buf, sizeof(buf)); if (ret) { fserr (PROGNAME, ret, NULL); return; } printf ("This workstation belongs to cell '%s'\n", buf); }
Chan* fsopen(Chan *c, int mode) { osenter(); FS(c)->fd = open(FS(c)->name->s, mode); osleave(); if(FS(c)->fd < 0) fserr(FS(c)); c->mode = openmode(mode); c->offset = 0; FS(c)->offset = 0; c->flag |= COPEN; return c; }
/** * Allocate and initialize a file_seg_t. * * @param[in] sfile source file name * @param[in] dfile destination file name * @param[in] start start offset * @param[in] seg_len length of segment to process * * @returns the allocated and initialized result */ file_seg_t * new_file_seg(char const * sfile, char const * dfile, size_t start, size_t seg_len) { file_seg_t * res = malloc(sizeof(*res) + seg_len); if (res == NULL) fserr(PCOPY_EXIT_NO_MEM, "alloc copy space", sfile); memset(res, NUL, sizeof(*res)); res->start = start; res->end = start + seg_len; res->fname = sfile; res->dname = dfile; return res; }
void fsremove(Chan *c) { int r; if(waserror()){ fsfree(c); nexterror(); } osenter(); r = remove(FS(c)->name->s); osleave(); if(r < 0) fserr(FS(c)); poperror(); fsfree(c); }
long fsread(Chan *c, void *va, long n, vlong offset) { int r; if(c->qid.type & QTDIR){ /* need to maintain offset only for directories */ qlock(FS(c)); if(waserror()){ qunlock(FS(c)); nexterror(); } r = fsdirread(c, va, n, offset); poperror(); qunlock(FS(c)); }else{ osenter(); r = pread(FS(c)->fd, va, n, offset); osleave(); } if(r < 0) fserr(FS(c)); return r; }
Chan* fsattach(char *spec) { Chan *c; Dir *d; char *root; Qid rootqid; static int devno; static Lock l; if(!emptystr(spec)){ if(strcmp(spec, "*") != 0) error(Ebadspec); root = "/"; }else root = rootdir; d = dirstat(root); if(d == nil) fserr(nil); rootqid = d->qid; free(d); c = devattach('U', spec); lock(&l); c->dev = devno++; c->qid = rootqid; unlock(&l); c->aux = smalloc(sizeof(Fsinfo)); FS(c)->name = newcname(root); FS(c)->rootqid = rootqid; FS(c)->fd = -1; FS(c)->root = root; return c; }
static void afs_setacl(char *path, char *user, char *rights) { struct Acl *acl; struct AclEntry *position; struct ViceIoctl a_params; int i; int newrights=0; int foundit=0; char *ptr; char acltext[MAXSIZE]; char tmpstr[MAXSIZE]; if((acl=afs_getacl(path))==NULL) exit(1); if(!strcmp(rights,"read")) newrights=PRSFS_READ | PRSFS_LOOKUP; else if(!strcmp(rights,"write")) newrights=PRSFS_READ | PRSFS_LOOKUP | PRSFS_INSERT | PRSFS_DELETE | PRSFS_DELETE | PRSFS_WRITE | PRSFS_LOCK; else if(!strcmp(rights,"mail")) newrights=PRSFS_INSERT | PRSFS_LOCK | PRSFS_LOOKUP; else if(!strcmp(rights,"all")) newrights=PRSFS_READ | PRSFS_LOOKUP | PRSFS_INSERT | PRSFS_DELETE | PRSFS_WRITE | PRSFS_LOCK | PRSFS_ADMINISTER; else { ptr=rights; while(*ptr!=0) { if(*ptr=='r') newrights|=PRSFS_READ; if(*ptr=='l') newrights|=PRSFS_LOOKUP; if(*ptr=='i') newrights|=PRSFS_INSERT; if(*ptr=='d') newrights|=PRSFS_DELETE; if(*ptr=='w') newrights|=PRSFS_WRITE; if(*ptr=='k') newrights|=PRSFS_LOCK; if(*ptr=='a') newrights|=PRSFS_ADMINISTER; ptr++; } } position=acl->pos; for(i=0; i<acl->NumPositiveEntries; i++) { if(!strncmp(user, position->name, 100)) { position->RightsMask=newrights; foundit=1; } if(position->next) position=position->next; } if(!foundit) { if (position) { position->next=malloc(sizeof(struct AclEntry)); position=position->next; } else { acl->pos = malloc(sizeof(struct AclEntry)); position = acl->pos; } if(position==NULL) { printf("fs: Out of memory\n"); exit(1); } acl->NumPositiveEntries++; position->next=NULL; strlcpy(position->name, user, sizeof(position->name)); position->RightsMask=newrights; } acltext[0] = 0; for(position=acl->pos; position && acl->NumPositiveEntries; position = position->next) { if (position->RightsMask) { snprintf(tmpstr, sizeof(tmpstr), "%s %d\n", position->name, position->RightsMask); strlcat(acltext, tmpstr, sizeof(acltext)); } else acl->NumPositiveEntries--; } for(position=acl->neg; position && acl->NumNegativeEntries; position = position->next) { if (position->RightsMask) { snprintf(tmpstr, sizeof(tmpstr), "%s %d\n", position->name, position->RightsMask); strlcat(acltext, tmpstr, sizeof(acltext)); } else acl->NumNegativeEntries--; } strlcpy (tmpstr, acltext, sizeof(tmpstr)); snprintf(acltext, sizeof(acltext), "%d\n%d\n%s", acl->NumPositiveEntries, acl->NumNegativeEntries, tmpstr); a_params.in_size=strlen(acltext); a_params.out_size=0; a_params.in=acltext; a_params.out=0; if(k_pioctl(path,VIOCSETAL,&a_params,1)==-1) { fserr(PROGNAME, errno, path); return; } /* XXX free(oldacl); and its contents */ }
/** * Make a destination name. If the destination option is not provided, * the destination is the base name of the input file. If the destination * is a directory, the base name of the source is always appended. * If the destination is a file, then make certain we only copy one file * to this destination. * * @param[in] sname source file name. * @returns the name of the corresponding output file. */ static inline char const * make_dest_name(char const * sname) { static char const no_dir[] = "no directory component in source name '%s'\n" "and no destination directory was specified.\n"; if (! HAVE_OPT(DESTINATION)) { char const * p = strrchr(sname, '/'); if (p == NULL) usage_message(no_dir, sname); unlink(++p); return strdup_or_die(p); } { static bool been_here = false; char const * dname = OPT_ARG(DESTINATION); struct stat sb; if (stat(dname, &sb) == 0) { if (S_ISDIR(sb.st_mode)) { char const * bn = strrchr(sname, '/'); char * p; bn = bn ? (bn + 1) : sname; p = malloc(strlen(dname) + strlen(bn) + 2); if (p == NULL) fserr(PCOPY_EXIT_NO_MEM, "allocating", "destination name"); sprintf(p, "%s/%s", dname, bn); unlink(p); return p; } if (S_ISREG(sb.st_mode)) { if (been_here) die(PCOPY_EXIT_BAD_CONFIG, "destination for multiple sources is one file"); been_here = true; unlink(dname); return strdup_or_die(dname); } errno = EINVAL; fserr(PCOPY_EXIT_BAD_CONFIG, "invalid destination fs type (not dir or file)", dname); } /* * We could not stat "dname". It must be a file name. Make sure any * directory part exists. If we call this routine again, we should * successfully stat our destination file and trip over the * "been_here" flag. */ dname = strdup_or_die(dname); been_here = true; { static char const bad_stat[] = "stat-ing dir portion"; char * p = strrchr(dname, '/'); if (p == NULL) { /* * No directory part. Destination file is for current dir. */ return dname; } *p = NUL; if (stat(dname, &sb) != 0) fserr(PCOPY_EXIT_BAD_CONFIG, bad_stat, OPT_ARG(DESTINATION)); if (! S_ISDIR(sb.st_mode)) { errno = ENOTDIR; fserr(PCOPY_EXIT_BAD_CONFIG, bad_stat, OPT_ARG(DESTINATION)); } *p = '/'; } return dname; } }
/** * Return nanoseconds since start of copy. * * @returns nanoseconds since program start as a 64 bit unsigned integer. */ uint64_t get_time_delta(void) { static struct timeval stv = {.tv_sec = 0}; struct timeval ctv; if (gettimeofday(&ctv, NULL) < 0) fserr(PCOPY_EXIT_FAILURE, "gettimeofday", "current"); if (stv.tv_sec == 0) stv = ctv; if (stv.tv_sec == ctv.tv_sec) return (ctv.tv_usec - stv.tv_usec) * THOUSAND; uint64_t res = ctv.tv_sec - stv.tv_sec - 1; res *= MILLION; res += (MILLION - stv.tv_usec) + ctv.tv_usec; return res * THOUSAND; // micros to nanos } /** * Start up the copy of a segment. Once the open is complete and * the seek to the correct offset is done, let the main thread know * it can continue with the next thread. We start one at a time. * * @param[in,out] fseg The descriptor of the segment to copy. */ void copy_start(file_seg_t * fseg) { static char const st_fmt[] = "th %2d reading 0x%08lX"; if (! HAVE_OPT(QUIET)) { pthread_mutex_lock(&tty_mutex); if (fseg->idx == 0) { move(0,0); printw("copying %s", fseg->fname); move(1,0); printw("copy to %s", fseg->dname); } move(ENTRY_LINE, 0); printw(st_fmt, fseg->idx, fseg->start); refresh(); pthread_mutex_unlock(&tty_mutex); } fseg->fdin = open(fseg->fname, O_RDONLY); if (fseg->fdin < 0) fserr(PCOPY_EXIT_FS_ERR_IN, "open (r)", fseg->fname); fseg->fdout = open(fseg->dname, O_RDWR | O_CREAT, 0600); if (fseg->fdout < 0) fserr(PCOPY_EXIT_FS_ERR_OUT, "open (w)", fseg->dname); if (fseg->start > 0) { if (lseek(fseg->fdin, fseg->start, SEEK_SET) != fseg->start) fserr(PCOPY_EXIT_FS_ERR_IN, "seek (in)", fseg->fname); if (lseek(fseg->fdout, fseg->start, SEEK_SET) != fseg->start) fserr(PCOPY_EXIT_FS_ERR_OUT, "seek (out)", fseg->dname); } if (posix_fadvise(fseg->fdin, fseg->start, fseg->end - fseg->start, POSIX_FADV_SEQUENTIAL) != 0) fserr(PCOPY_EXIT_FS_ERR_IN, "fadvise(r)", fseg->fname); if (posix_fadvise(fseg->fdout, fseg->start, fseg->end - fseg->start, POSIX_FADV_SEQUENTIAL) != 0) fserr(PCOPY_EXIT_FS_ERR_OUT, "fadvise(w)", fseg->dname); pthread_mutex_lock(&th_start_mutex); pthread_cond_signal(&th_start_cond); pthread_mutex_unlock(&th_start_mutex); fseg->lastt = get_time_delta(); } /** * Show copy progress. The display happens if "quiet" has not been * specified. This function will also sleep, if "flow-rate" has * been specified and the time since the last segment read has been * too short. * * @param[in,out] fseg file segment descriptor. "lastt" may be updated. */ void copy_progress(file_seg_t * fseg) { static char const prg_fmt[] = " read to 0x%08lX togo 0x%08lX"; if (! HAVE_OPT(QUIET)) { pthread_mutex_lock(&tty_mutex); move(ENTRY_LINE + 1, 0); printw(prg_fmt, fseg->start, fseg->end - fseg->start); refresh(); pthread_mutex_unlock(&tty_mutex); } if (HAVE_OPT(FLOW_RATE)) { uint64_t cdelta = get_time_delta(); // delta from start uint64_t chunk_time = cdelta - fseg->lastt; // since chunk start /* * If the time used is more than 1/100 of a second less than * the time that is supposed to be consumed, then nanosleep. */ if (chunk_time < nsec_per_iteration - (10*MILLION)) { uint64_t slptm = nsec_per_iteration - chunk_time; struct timespec ts = { .tv_sec = (slptm > BILLION) ? (slptm / BILLION) : 0, .tv_nsec = (slptm > BILLION) ? (slptm % BILLION) : slptm }; (void)nanosleep(&ts, NULL); fseg->lastt = get_time_delta(); } else { fseg->lastt = cdelta; } } }
/** * kick off a thread. And do the cond-wait dance to ensure we don't have * a gazillion of these running at once. * * @param[in] pth The pointer to the pthread identifier * @param[in] arg The file_seg_t pointer specifying what is to be copied. */ static inline void start_copy(pthread_t * pth, void * arg) { pthread_create(pth, NULL, do_copy, arg); pthread_mutex_lock(&th_start_mutex); pthread_cond_wait(&th_start_cond, &th_start_mutex); pthread_mutex_unlock(&th_start_mutex); struct timespec ts = { .tv_sec = 0, .tv_nsec = BILLION / 10 }; (void)nanosleep(&ts, NULL); } /** * Copy parts of a file in parallel. They are copied into place in the output * file. Various options determine where to put the output file, how to split * it up, and how many bytes per second should be consumed with the process. * * @param[in] fn the input file name. */ void pcopy(char const * fn) { char const * dname = make_dest_name(fn); size_t base_size; size_t seg_size; size_t xfer_sz; int ix = 0; if (strcmp(fn, dname) == 0) die(PCOPY_EXIT_BAD_CONFIG, "source and destination must be distinct:\n\t%s", fn); { struct stat sb; if (stat(fn, &sb) < 0) fserr(PCOPY_EXIT_FAILURE, "cannot re-stat", fn); if (! S_ISREG(sb.st_mode)) { errno = EINVAL; fserr(PCOPY_EXIT_FS_ERR_IN, "regular file check", fn); } base_size = sb.st_size; xfer_sz = sb.st_blksize; } { size_t sz = base_size / OPT_VALUE_THREAD_CT; /* * Now make sure seg_size is an even multiple of xfer_sz * and that thread-ct * size >= base_size */ seg_size = xfer_sz * (sz / xfer_sz); if ((seg_size * OPT_VALUE_THREAD_CT) < base_size) seg_size += xfer_sz; } { size_t sz = (OPT_VALUE_MAX_CHUNK / xfer_sz) * xfer_sz; if (sz == 0) sz = xfer_sz; OPT_VALUE_MAX_CHUNK = sz; } if (HAVE_OPT(FLOW_RATE)) { /* bytes per second divided by thread count: */ float fr = (float)OPT_VALUE_FLOW_RATE; fr /= (float)OPT_VALUE_THREAD_CT; /* bytes/sec divided by byts/chunk -> chunks/second */ fr /= (float)OPT_VALUE_MAX_CHUNK; /* * nanoseconds per second div. by chunks per second * yields nanoseconds per chunk (iteration). */ nsec_per_iteration = BILLION / fr; } memset(pth_list, 0, OPT_VALUE_THREAD_CT * sizeof(*pth_list)); for (xfer_sz = 0; xfer_sz < base_size; ix++) { file_seg_t * fseg = new_file_seg(fn, dname, xfer_sz, seg_size); xfer_sz += seg_size; fseg->end = xfer_sz; if (fseg->end > base_size) fseg->end = base_size; fseg->idx = ix; start_copy(pth_list + ix, fseg); } for (int j = 0; j < ix; j++) pthread_join(pth_list[j], NULL); free((void *)dname); }