# HG changeset patch # User Michael Spacefalcon # Date 1393660868 0 # Node ID 3dd74b16df82c838c25b026bd79f11fe3149f9ad # Parent 797468042b3235e97f13269e32c65c81bd566e06 fc-fsio: pathname recursion handling revamped diff -r 797468042b32 -r 3dd74b16df82 rvinterf/etmsync/Makefile --- a/rvinterf/etmsync/Makefile Sat Mar 01 04:04:20 2014 +0000 +++ b/rvinterf/etmsync/Makefile Sat Mar 01 08:01:08 2014 +0000 @@ -4,7 +4,8 @@ INSTBIN=/usr/local/bin FSIO_OBJS= connect.o dispatch.o fdcmd.o fileio.o fsbasics.o fscmdtab.o \ - fserr.o fsiomain.o fsread.o fswrite.o interf.o launchrvif.o + fserr.o fsiomain.o fspath.o fsread.o fswrite.o interf.o \ + launchrvif.o all: ${PROGS} diff -r 797468042b32 -r 3dd74b16df82 rvinterf/etmsync/fsbasics.c --- a/rvinterf/etmsync/fsbasics.c Sat Mar 01 04:04:20 2014 +0000 +++ b/rvinterf/etmsync/fsbasics.c Sat Mar 01 08:01:08 2014 +0000 @@ -11,6 +11,7 @@ #include "ffs.h" #include "tmffs2.h" #include "limits.h" +#include "ffslimits.h" #include "localtypes.h" #include "localstruct.h" #include "exitcodes.h" @@ -75,7 +76,7 @@ return(0); } -do_readdir(state, namebuf) +do_readdir(state, namebuf, namebuflen) u_char *state; char *namebuf; { @@ -101,8 +102,12 @@ if (rvi_msg[5] != 4) goto malformed; slen = rvi_msg[10]; - if (slen < 2 || slen > TMFFS_STRING_SIZE) + if (slen < 2 || rvi_msg_len != slen + 12) goto malformed; + if (slen > namebuflen) { + printf("error: readdir response exceeds provided buffer\n"); + return(ERROR_TARGET); + } if (rvi_msg[11 + slen - 1]) /* must be terminating NUL */ goto malformed; bcopy(rvi_msg + 6, state, 4); @@ -114,7 +119,7 @@ char **argv; { u_char state[4]; - char namebuf[TMFFS_STRING_SIZE]; + char namebuf[256]; int nument, i, rc; rc = do_opendir(argv[1], state, &nument); @@ -125,7 +130,7 @@ return(0); } for (i = 0; i < nument; i++) { - rc = do_readdir(state, namebuf); + rc = do_readdir(state, namebuf, sizeof namebuf); if (rc) return(rc); printf("%s\n", namebuf); diff -r 797468042b32 -r 3dd74b16df82 rvinterf/etmsync/fscmdtab.c --- a/rvinterf/etmsync/fscmdtab.c Sat Mar 01 04:04:20 2014 +0000 +++ b/rvinterf/etmsync/fscmdtab.c Sat Mar 01 08:01:08 2014 +0000 @@ -5,6 +5,7 @@ #include "cmdtab.h" extern int cmd_cpout(); +extern int cmd_cpout_file(); extern int cmd_delete(); extern int cmd_exec(); extern int cmd_exit(); @@ -19,6 +20,7 @@ struct cmdtab cmdtab[] = { {"cpout", 2, 2, cmd_cpout}, + {"cpout-file", 2, 2, cmd_cpout_file}, {"delete", 1, 1, cmd_delete}, {"exec", 1, 1, cmd_exec}, {"exit", 0, 0, cmd_exit}, diff -r 797468042b32 -r 3dd74b16df82 rvinterf/etmsync/fspath.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/rvinterf/etmsync/fspath.c Sat Mar 01 08:01:08 2014 +0000 @@ -0,0 +1,70 @@ +/* + * FFS pathname manipulation functions + */ + +#include +#include +#include +#include +#include +#include "ffs.h" +#include "limits.h" +#include "ffslimits.h" +#include "exitcodes.h" + +validate_ffs_pathname(cand) + char *cand; +{ + char *cp; + int depth, c; + + cp = cand; + if (*cp++ != '/') { + fprintf(stderr, "error: FFS pathnames must be absolute\n"); + return(-1); + } + for (depth = 0; *cp; depth++) { + if (*cp == '/') { + fprintf(stderr, + "error: FFS pathname must not contain duplicate slashes\n"); + return(-1); + } + for (c = 0; *cp && *cp != '/'; cp++) + c++; + if (c > MAX_FN_COMPONENT) { + fprintf(stderr, + "error: FFS pathname component is too long\n"); + return(-1); + } + if (!*cp) + continue; + cp++; + if (!*cp) { + fprintf(stderr, + "error: FFS pathname must not end with a trailing slash\n"); + return(-1); + } + } + if (depth > MAX_NAME_DEPTH) { + fprintf(stderr, "error: FFS pathname exceeds depth limit\n"); + return(-1); + } + return(depth); +} + +char * +pathname_for_ffs_child(parent, childbuf) + char *parent, *childbuf; +{ + int depth; + char *cp; + + depth = validate_ffs_pathname(parent); + if (depth < 0 || depth >= MAX_NAME_DEPTH) + return(0); + strcpy(childbuf, parent); + cp = index(childbuf, '\0'); + if (depth) + *cp++ = '/'; + return(cp); +} diff -r 797468042b32 -r 3dd74b16df82 rvinterf/etmsync/fsread.c --- a/rvinterf/etmsync/fsread.c Sat Mar 01 04:04:20 2014 +0000 +++ b/rvinterf/etmsync/fsread.c Sat Mar 01 08:01:08 2014 +0000 @@ -14,10 +14,13 @@ #include "ffs.h" #include "tmffs2.h" #include "limits.h" +#include "ffslimits.h" #include "localtypes.h" #include "localstruct.h" #include "exitcodes.h" +extern char *pathname_for_ffs_child(); + void ll_print_line(pathname, stat) char *pathname; @@ -44,27 +47,16 @@ } } -void -mk_ffs_child_path_from_readdir(dirpath, rdname, buf) - char *dirpath, *rdname, *buf; -{ - char *tailp; - - tailp = index(dirpath, '\0') - 1; - if (*tailp == '/') - sprintf(buf, "%s%s", dirpath, rdname); - else - sprintf(buf, "%s/%s", dirpath, rdname); -} - cmd_ll(argc, argv) char **argv; { struct stat_info stat; u_char rdstate[4]; - char rdbuf[TMFFS_STRING_SIZE], childpath[TMFFS_STRING_SIZE*2]; + char rdbuf[MAX_FN_COMPONENT+1], childpath[MAX_FULL_PATHNAME+1], *childp; int nument, i, rc; + if (validate_ffs_pathname(argv[1]) < 0) + return(ERROR_USAGE); /* err msg already printed */ rc = do_xlstat(argv[1], &stat); if (rc) return(rc); @@ -79,11 +71,20 @@ printf("\n"); return(0); } + childp = pathname_for_ffs_child(argv[1], childpath); + if (!childp) { + printf("error: non-empty dir at the limit of pathname depth\n"); + return(ERROR_TARGET); + } for (i = 0; i < nument; i++) { - rc = do_readdir(rdstate, rdbuf); + rc = do_readdir(rdstate, rdbuf, MAX_FN_COMPONENT+1); if (rc) return(rc); - mk_ffs_child_path_from_readdir(argv[1], rdbuf, childpath); + if (index(rdbuf, '/')) { + printf("error: readdir result contains a slash\n"); + return(ERROR_TARGET); + } + strcpy(childp, rdbuf); rc = do_xlstat(childpath, &stat); if (rc) { printf("xlstat failed on %s\n", childpath); @@ -148,7 +149,7 @@ return(rc); switch (stat.type) { case OT_FILE: - return cpout_file(ffspath, &stat, hostpath); + return cpout_file(ffspath, hostpath); case OT_DIR: return cpout_dir(ffspath, hostpath); case OT_LINK: @@ -160,9 +161,8 @@ } } -cpout_file(ffspath, stat, hostpath) +cpout_file(ffspath, hostpath) char *ffspath, *hostpath; - struct stat_info *stat; { int tfd; FILE *of; @@ -224,9 +224,10 @@ char *ffspath_dir, *hostpath_dir; { u_char rdstate[4]; - char rdbuf[TMFFS_STRING_SIZE], ffspath_child[TMFFS_STRING_SIZE*2]; + char rdbuf[MAX_FN_COMPONENT+1], ffspath_child[MAX_FULL_PATHNAME+1]; + char *childp; char hostpath_child[MAXPATHLEN]; - int nument, i, rc; + int nument, i, rc, childerr; printf("dir %s\n", ffspath_dir); rc = host_mkdir(hostpath_dir); @@ -235,15 +236,26 @@ rc = do_opendir(ffspath_dir, rdstate, &nument); if (rc) return(rc); + if (!nument) + return(0); + childp = pathname_for_ffs_child(ffspath_dir, ffspath_child); + if (!childp) { + printf("error: non-empty dir at the limit of pathname depth\n"); + return(ERROR_TARGET); + } + childerr = 0; for (i = 0; i < nument; i++) { rc = do_readdir(rdstate, rdbuf); if (rc) return(rc); - mk_ffs_child_path_from_readdir(ffspath_dir, rdbuf, - ffspath_child); + if (index(rdbuf, '/')) { + printf("error: readdir result contains a slash\n"); + return(ERROR_TARGET); + } + strcpy(childp, rdbuf); if (rdbuf[0] == '.') { printf("skipping %s\n", ffspath_child); - return(0); + continue; } if (strlen(hostpath_dir) + strlen(rdbuf) + 2 > sizeof hostpath_child) { @@ -253,14 +265,24 @@ } sprintf(hostpath_child, "%s/%s", hostpath_dir, rdbuf); rc = cpout_object(ffspath_child, hostpath_child); - if (rc) + if (rc && rc != ERROR_TARGET) return(rc); + if (rc) + childerr = rc; } - return(0); + return(childerr); } cmd_cpout(argc, argv) char **argv; { + if (validate_ffs_pathname(argv[1]) < 0) + return(ERROR_USAGE); /* err msg already printed */ return cpout_object(argv[1], argv[2]); } + +cmd_cpout_file(argc, argv) + char **argv; +{ + return cpout_file(argv[1], argv[2]); +} diff -r 797468042b32 -r 3dd74b16df82 rvinterf/etmsync/fswrite.c --- a/rvinterf/etmsync/fswrite.c Sat Mar 01 04:04:20 2014 +0000 +++ b/rvinterf/etmsync/fswrite.c Sat Mar 01 08:01:08 2014 +0000 @@ -186,6 +186,11 @@ cmd_fwrite(argc, argv) char **argv; { + if (strlen(argv[1]) >= TMFFS_STRING_SIZE) { + fprintf(stderr, + "error: pathname arg exceeds string length limit\n"); + return(ERROR_USAGE); + } if (!strcmp(argv[2], "ascii")) return do_short_fwrite(argv[1], argv[3], strlen(argv[3])); else if (!strcmp(argv[2], "hex")) diff -r 797468042b32 -r 3dd74b16df82 rvinterf/include/ffslimits.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/rvinterf/include/ffslimits.h Sat Mar 01 08:01:08 2014 +0000 @@ -0,0 +1,22 @@ +/* + * Limits on FFS filenames and pathnames + * + * The deepest pathname allowed is one of the form /1/2/3/4/5/6, where the + * last component may be a file, a directory or a symlink; if this last + * component is a directory, it has to be empty, because any child of + * that directory would violate the depth limit. + * + * The proper FFS pathname form begins with a slash (all pathnames must + * be absolute, no Unix processes in the fw means no current directories), + * has exactly one slash in each separating place (no double slashes), + * and no trailing slash except in the special case of the root directory, + * whose full pathname is "/". + * + * Each component name is [1,20] characters long; combining this limit + * with the maximum depth of 6 puts the maximum length of a properly-formed + * full pathname at 126 characters. + */ + +#define MAX_FN_COMPONENT 20 +#define MAX_NAME_DEPTH 6 +#define MAX_FULL_PATHNAME ((MAX_FN_COMPONENT+1) * MAX_NAME_DEPTH)