int sftp_cmd_rmdir(struct sftp_command *cmd) { char *dir; int result; if (back == NULL) { printf("psftp: not connected to a host; use \"open host.name\"\n"); return 0; } if (cmd->nwords < 2) { printf("rmdir: expects a directory\n"); return 0; } dir = canonify(cmd->words[1]); if (!dir) { printf("%s: %s\n", dir, fxp_error()); return 0; } result = fxp_rmdir(dir); if (!result) { printf("rmdir %s: %s\n", dir, fxp_error()); sfree(dir); return 0; } sfree(dir); return 1; }
int sftp_cmd_rm(struct sftp_command *cmd) { char *fname; int result; if (back == NULL) { printf("psftp: not connected to a host; use \"open host.name\"\n"); return 0; } if (cmd->nwords < 2) { printf("rm: expects a filename\n"); return 0; } fname = canonify(cmd->words[1]); if (!fname) { printf("%s: %s\n", fname, fxp_error()); return 0; } result = fxp_remove(fname); if (!result) { printf("rm %s: %s\n", fname, fxp_error()); sfree(fname); return 0; } sfree(fname); return 1; }
/* * Change directories. We do this by canonifying the new name, then * trying to OPENDIR it. Only if that succeeds do we set the new pwd. */ int sftp_cmd_cd(struct sftp_command *cmd) { struct fxp_handle *dirh; char *dir; if (back == NULL) { printf("psftp: not connected to a host; use \"open host.name\"\n"); return 0; } if (cmd->nwords < 2) dir = dupstr(homedir); else dir = canonify(cmd->words[1]); if (!dir) { printf("%s: %s\n", dir, fxp_error()); return 0; } dirh = fxp_opendir(dir); if (!dirh) { printf("Directory %s: %s\n", dir, fxp_error()); sfree(dir); return 0; } fxp_close(dirh); sfree(pwd); pwd = dir; printf("Remote directory is now %s\n", pwd); return 1; }
int main(int argc, char **argv) { int ch; int rc = 0; struct rule *r; fn_list *pre_input = NULL; #ifdef YYDEBUG extern int yydebug; yydebug = 0; #endif hcreate(MAXRULE); while ((ch = getopt(argc, argv, "cdi:kntqS:")) != -1) { switch (ch) { case 'c': cflag++; break; case 'd': #ifdef YYDEBUG yydebug = 1; #else fprintf(stderr, "Rebuild with -DYYDEBUG to use -d.\n"); #endif break; case 'k': c2flag++; break; case 'n': canon = 0; break; case 'i': { fn_list *ifile = calloc(sizeof(fn_list), 1); ifile->filename = optarg; ifile->next = pre_input; pre_input = ifile; break; } case 't': tflag++; break; case 'p': permissive = 0; break; case 'q': qflag++; break; case 'S': top_rule_name = optarg; break; default: usage(); } } argc -= optind; argv += optind; if (argc > 1) usage(); predefine(pre_input); /* Parse the grammar, perhaps spouting errors. */ parse_from((argc > 0)? argv[0] : NULL); /* If we're not quiet, then output the grammar again. */ if (!qflag) { if (canon) canonify(rules); for (r = rules; r; r = r->next) { if (r->predefined) { /* do not output */ } else if (r->rule) { printf("%s = ", r->name); printobj(r->rule, tflag); if (cflag) printf(" ; line %d", r->line); printf("\n"); } else { printf("; %s UNDEFINED\n", r->name); } if (r->next == rules) break; } for (r = rules; r; r = r->next) { if (r->used == 0 && r->predefined == 0 && r->rule && strcmp(r->name, top_rule_name)) printf("; %s defined but not used\n", r->name); if (r->next == rules) break; } } rc = summary(); hdestroy(); exit(rc); }
int sftp_cmd_chmod(struct sftp_command *cmd) { char *fname, *mode; int result; struct fxp_attrs attrs; unsigned attrs_clr, attrs_xor, oldperms, newperms; if (back == NULL) { printf("psftp: not connected to a host; use \"open host.name\"\n"); return 0; } if (cmd->nwords < 3) { printf("chmod: expects a mode specifier and a filename\n"); return 0; } /* * Attempt to parse the mode specifier in cmd->words[1]. We * don't support the full horror of Unix chmod; instead we * support a much simpler syntax in which the user can either * specify an octal number, or a comma-separated sequence of * [ugoa]*[-+=][rwxst]+. (The initial [ugoa] sequence may * _only_ be omitted if the only attribute mentioned is t, * since all others require a user/group/other specification. * Additionally, the s attribute may not be specified for any * [ugoa] specifications other than exactly u or exactly g. */ attrs_clr = attrs_xor = 0; mode = cmd->words[1]; if (mode[0] >= '0' && mode[0] <= '9') { if (mode[strspn(mode, "01234567")]) { printf("chmod: numeric file modes should" " contain digits 0-7 only\n"); return 0; } attrs_clr = 07777; sscanf(mode, "%o", &attrs_xor); attrs_xor &= attrs_clr; } else { while (*mode) { char *modebegin = mode; unsigned subset, perms; int action; subset = 0; while (*mode && *mode != ',' && *mode != '+' && *mode != '-' && *mode != '=') { switch (*mode) { case 'u': subset |= 04700; break; /* setuid, user perms */ case 'g': subset |= 02070; break; /* setgid, group perms */ case 'o': subset |= 00007; break; /* just other perms */ case 'a': subset |= 06777; break; /* all of the above */ default: printf("chmod: file mode '%.*s' contains unrecognised" " user/group/other specifier '%c'\n", strcspn(modebegin, ","), modebegin, *mode); return 0; } mode++; } if (!*mode || *mode == ',') { printf("chmod: file mode '%.*s' is incomplete\n", strcspn(modebegin, ","), modebegin); return 0; } action = *mode++; if (!*mode || *mode == ',') { printf("chmod: file mode '%.*s' is incomplete\n", strcspn(modebegin, ","), modebegin); return 0; } perms = 0; while (*mode && *mode != ',') { switch (*mode) { case 'r': perms |= 00444; break; case 'w': perms |= 00222; break; case 'x': perms |= 00111; break; case 't': perms |= 01000; subset |= 01000; break; case 's': if ((subset & 06777) != 04700 && (subset & 06777) != 02070) { printf("chmod: file mode '%.*s': set[ug]id bit should" " be used with exactly one of u or g only\n", strcspn(modebegin, ","), modebegin); return 0; } perms |= 06000; break; default: printf("chmod: file mode '%.*s' contains unrecognised" " permission specifier '%c'\n", strcspn(modebegin, ","), modebegin, *mode); return 0; } mode++; } if (!(subset & 06777) && (perms &~ subset)) { printf("chmod: file mode '%.*s' contains no user/group/other" " specifier and permissions other than 't' \n", strcspn(modebegin, ","), modebegin, *mode); return 0; } perms &= subset; switch (action) { case '+': attrs_clr |= perms; attrs_xor |= perms; break; case '-': attrs_clr |= perms; attrs_xor &= ~perms; break; case '=': attrs_clr |= subset; attrs_xor |= perms; break; } if (*mode) mode++; /* eat comma */ } } fname = canonify(cmd->words[2]); if (!fname) { printf("%s: %s\n", fname, fxp_error()); return 0; } result = fxp_stat(fname, &attrs); if (!result || !(attrs.flags & SSH_FILEXFER_ATTR_PERMISSIONS)) { printf("get attrs for %s: %s\n", fname, result ? "file permissions not provided" : fxp_error()); sfree(fname); return 0; } attrs.flags = SSH_FILEXFER_ATTR_PERMISSIONS; /* perms _only_ */ oldperms = attrs.permissions & 07777; attrs.permissions &= ~attrs_clr; attrs.permissions ^= attrs_xor; newperms = attrs.permissions & 07777; result = fxp_setstat(fname, attrs); if (!result) { printf("set attrs for %s: %s\n", fname, fxp_error()); sfree(fname); return 0; } printf("%s: %04o -> %04o\n", fname, oldperms, newperms); sfree(fname); return 1; }
int sftp_cmd_mv(struct sftp_command *cmd) { char *srcfname, *dstfname; int result; if (back == NULL) { printf("psftp: not connected to a host; use \"open host.name\"\n"); return 0; } if (cmd->nwords < 3) { printf("mv: expects two filenames\n"); return 0; } srcfname = canonify(cmd->words[1]); if (!srcfname) { printf("%s: %s\n", srcfname, fxp_error()); return 0; } dstfname = canonify(cmd->words[2]); if (!dstfname) { printf("%s: %s\n", dstfname, fxp_error()); return 0; } result = fxp_rename(srcfname, dstfname); if (!result) { char const *error = fxp_error(); struct fxp_attrs attrs; /* * The move might have failed because dstfname pointed at a * directory. We check this possibility now: if dstfname * _is_ a directory, we re-attempt the move by appending * the basename of srcfname to dstfname. */ result = fxp_stat(dstfname, &attrs); if (result && (attrs.flags & SSH_FILEXFER_ATTR_PERMISSIONS) && (attrs.permissions & 0040000)) { char *p; char *newname, *newcanon; printf("(destination %s is a directory)\n", dstfname); p = srcfname + strlen(srcfname); while (p > srcfname && p[-1] != '/') p--; newname = dupcat(dstfname, "/", p, NULL); newcanon = canonify(newname); sfree(newname); if (newcanon) { sfree(dstfname); dstfname = newcanon; result = fxp_rename(srcfname, dstfname); error = result ? NULL : fxp_error(); } } if (error) { printf("mv %s %s: %s\n", srcfname, dstfname, error); sfree(srcfname); sfree(dstfname); return 0; } } printf("%s -> %s\n", srcfname, dstfname); sfree(srcfname); sfree(dstfname); return 1; }
/* * Send a file and store it at the remote end. We have two very * similar commands here: `put' and `reput', which differ in that * `reput' checks for the existence of the destination file and * starts from where a previous aborted transfer left off. */ int sftp_general_put(struct sftp_command *cmd, int restart) { struct fxp_handle *fh; char *fname, *origoutfname, *outfname; uint64 offset; FILE *fp; int ret; if (back == NULL) { printf("psftp: not connected to a host; use \"open host.name\"\n"); return 0; } if (cmd->nwords < 2) { printf("put: expects a filename\n"); return 0; } fname = cmd->words[1]; origoutfname = (cmd->nwords == 2 ? stripslashes(cmd->words[1], 1) : cmd->words[2]); outfname = canonify(origoutfname); if (!outfname) { printf("%s: %s\n", origoutfname, fxp_error()); return 0; } fp = fopen(fname, "rb"); if (!fp) { printf("local: unable to open %s\n", fname); sfree(outfname); return 0; } if (restart) { fh = fxp_open(outfname, SSH_FXF_WRITE); } else { fh = fxp_open(outfname, SSH_FXF_WRITE | SSH_FXF_CREAT | SSH_FXF_TRUNC); } if (!fh) { printf("%s: %s\n", outfname, fxp_error()); sfree(outfname); return 0; } if (restart) { char decbuf[30]; struct fxp_attrs attrs; if (!fxp_fstat(fh, &attrs)) { printf("read size of %s: %s\n", outfname, fxp_error()); sfree(outfname); return 0; } if (!(attrs.flags & SSH_FILEXFER_ATTR_SIZE)) { printf("read size of %s: size was not given\n", outfname); sfree(outfname); return 0; } offset = attrs.size; uint64_decimal(offset, decbuf); printf("reput: restarting at file position %s\n", decbuf); if (uint64_compare(offset, uint64_make(0, LONG_MAX)) > 0) { printf("reput: remote file is larger than we can deal with\n"); sfree(outfname); return 0; } if (fseek(fp, offset.lo, SEEK_SET) != 0) fseek(fp, 0, SEEK_END); /* *shrug* */ } else { offset = uint64_make(0, 0); } printf("local:%s => remote:%s\n", fname, outfname); /* * FIXME: we can use FXP_FSTAT here to get the file size, and * thus put up a progress bar. */ ret = 1; while (1) { char buffer[4096]; int len; len = fread(buffer, 1, sizeof(buffer), fp); if (len == -1) { printf("error while reading local file\n"); ret = 0; break; } else if (len == 0) { break; } if (!fxp_write(fh, buffer, offset, len)) { printf("error while writing: %s\n", fxp_error()); ret = 0; break; } offset = uint64_add32(offset, len); } fxp_close(fh); fclose(fp); sfree(outfname); return ret; }
/* * Get a file and save it at the local end. We have two very * similar commands here: `get' and `reget', which differ in that * `reget' checks for the existence of the destination file and * starts from where a previous aborted transfer left off. */ int sftp_general_get(struct sftp_command *cmd, int restart) { struct fxp_handle *fh; char *fname, *outfname; uint64 offset; FILE *fp; int ret; if (back == NULL) { printf("psftp: not connected to a host; use \"open host.name\"\n"); return 0; } if (cmd->nwords < 2) { printf("get: expects a filename\n"); return 0; } fname = canonify(cmd->words[1]); if (!fname) { printf("%s: %s\n", cmd->words[1], fxp_error()); return 0; } outfname = (cmd->nwords == 2 ? stripslashes(cmd->words[1], 0) : cmd->words[2]); fh = fxp_open(fname, SSH_FXF_READ); if (!fh) { printf("%s: %s\n", fname, fxp_error()); sfree(fname); return 0; } if (restart) { fp = fopen(outfname, "rb+"); } else { fp = fopen(outfname, "wb"); } if (!fp) { printf("local: unable to open %s\n", outfname); fxp_close(fh); sfree(fname); return 0; } if (restart) { long posn; fseek(fp, 0L, SEEK_END); posn = ftell(fp); printf("reget: restarting at file position %ld\n", posn); offset = uint64_make(0, posn); } else { offset = uint64_make(0, 0); } printf("remote:%s => local:%s\n", fname, outfname); /* * FIXME: we can use FXP_FSTAT here to get the file size, and * thus put up a progress bar. */ ret = 1; while (1) { char buffer[4096]; int len; int wpos, wlen; len = fxp_read(fh, buffer, offset, sizeof(buffer)); if ((len == -1 && fxp_error_type() == SSH_FX_EOF) || len == 0) break; if (len == -1) { printf("error while reading: %s\n", fxp_error()); ret = 0; break; } wpos = 0; while (wpos < len) { wlen = fwrite(buffer, 1, len - wpos, fp); if (wlen <= 0) { printf("error while writing local file\n"); ret = 0; break; } wpos += wlen; } if (wpos < len) { /* we had an error */ ret = 0; break; } offset = uint64_add32(offset, len); } fclose(fp); fxp_close(fh); sfree(fname); return ret; }
int sftp_cmd_ls(struct sftp_command *cmd) { struct fxp_handle *dirh; struct fxp_names *names; struct fxp_name *ournames; int nnames, namesize; char *dir, *cdir; int i; if (back == NULL) { printf("psftp: not connected to a host; use \"open host.name\"\n"); return 0; } if (cmd->nwords < 2) dir = "."; else dir = cmd->words[1]; cdir = canonify(dir); if (!cdir) { printf("%s: %s\n", dir, fxp_error()); return 0; } printf("Listing directory %s\n", cdir); dirh = fxp_opendir(cdir); if (dirh == NULL) { printf("Unable to open %s: %s\n", dir, fxp_error()); } else { nnames = namesize = 0; ournames = NULL; while (1) { names = fxp_readdir(dirh); if (names == NULL) { if (fxp_error_type() == SSH_FX_EOF) break; printf("Reading directory %s: %s\n", dir, fxp_error()); break; } if (names->nnames == 0) { fxp_free_names(names); break; } if (nnames + names->nnames >= namesize) { namesize += names->nnames + 128; ournames = srealloc(ournames, namesize * sizeof(*ournames)); } for (i = 0; i < names->nnames; i++) ournames[nnames++] = names->names[i]; names->nnames = 0; /* prevent free_names */ fxp_free_names(names); } fxp_close(dirh); /* * Now we have our filenames. Sort them by actual file * name, and then output the longname parts. */ qsort(ournames, nnames, sizeof(*ournames), sftp_ls_compare); /* * And print them. */ for (i = 0; i < nnames; i++) printf("%s\n", ournames[i].longname); } sfree(cdir); return 1; }
/** Perform a path lookup. * * @param path Path to be resolved; it must be a NULL-terminated * string. * @param lflag Flags to be used during lookup. * @param result Empty structure where the lookup result will be stored. * Can be NULL. * @param altroot If non-empty, will be used instead of rootfs as the root * of the whole VFS tree. * * @return EOK on success or an error code from errno.h. * */ int vfs_lookup_internal(char *path, int lflag, vfs_lookup_res_t *result, vfs_pair_t *altroot, ...) { vfs_pair_t *root; if (altroot) root = altroot; else root = &rootfs; if (!root->fs_handle) return ENOENT; size_t len; path = canonify(path, &len); if (!path) return EINVAL; fs_index_t index = 0; if (lflag & L_LINK) { va_list ap; va_start(ap, altroot); index = va_arg(ap, fs_index_t); va_end(ap); } fibril_mutex_lock(&plb_mutex); plb_entry_t entry; link_initialize(&entry.plb_link); entry.len = len; size_t first; /* the first free index */ size_t last; /* the last free index */ if (list_empty(&plb_entries)) { first = 0; last = PLB_SIZE - 1; } else { plb_entry_t *oldest = list_get_instance( list_first(&plb_entries), plb_entry_t, plb_link); plb_entry_t *newest = list_get_instance( list_last(&plb_entries), plb_entry_t, plb_link); first = (newest->index + newest->len) % PLB_SIZE; last = (oldest->index - 1) % PLB_SIZE; } if (first <= last) { if ((last - first) + 1 < len) { /* * The buffer cannot absorb the path. */ fibril_mutex_unlock(&plb_mutex); return ELIMIT; } } else { if (PLB_SIZE - ((first - last) + 1) < len) { /* * The buffer cannot absorb the path. */ fibril_mutex_unlock(&plb_mutex); return ELIMIT; } } /* * We know the first free index in PLB and we also know that there is * enough space in the buffer to hold our path. */ entry.index = first; entry.len = len; /* * Claim PLB space by inserting the entry into the PLB entry ring * buffer. */ list_append(&entry.plb_link, &plb_entries); fibril_mutex_unlock(&plb_mutex); /* * Copy the path into PLB. */ size_t cnt1 = min(len, (PLB_SIZE - first) + 1); size_t cnt2 = len - cnt1; memcpy(&plb[first], path, cnt1); memcpy(plb, &path[cnt1], cnt2); ipc_call_t answer; async_exch_t *exch = vfs_exchange_grab(root->fs_handle); aid_t req = async_send_5(exch, VFS_OUT_LOOKUP, (sysarg_t) first, (sysarg_t) (first + len - 1) % PLB_SIZE, (sysarg_t) root->service_id, (sysarg_t) lflag, (sysarg_t) index, &answer); sysarg_t rc; async_wait_for(req, &rc); vfs_exchange_release(exch); fibril_mutex_lock(&plb_mutex); list_remove(&entry.plb_link); /* * Erasing the path from PLB will come handy for debugging purposes. */ memset(&plb[first], 0, cnt1); memset(plb, 0, cnt2); fibril_mutex_unlock(&plb_mutex); if ((int) rc < EOK) return (int) rc; if (!result) return EOK; result->triplet.fs_handle = (fs_handle_t) rc; result->triplet.service_id = (service_id_t) IPC_GET_ARG1(answer); result->triplet.index = (fs_index_t) IPC_GET_ARG2(answer); result->size = (aoff64_t) MERGE_LOUP32(IPC_GET_ARG3(answer), IPC_GET_ARG4(answer)); result->lnkcnt = (unsigned int) IPC_GET_ARG5(answer); if (lflag & L_FILE) result->type = VFS_NODE_FILE; else if (lflag & L_DIRECTORY) result->type = VFS_NODE_DIRECTORY; else result->type = VFS_NODE_UNKNOWN; return EOK; }