static int read_string(FILE *cfile) { int i, c, bs; /* * Read in characters until an un-escaped '"' is encountered. And * then unvis the data that was read. */ bs = i = 0; memset(visbuf, 0, sizeof(visbuf)); while ((c = get_char(cfile)) != EOF) { if (c == '"' && bs == 0) break; visbuf[i++] = c; if (bs) bs = 0; else if (c == '\\') bs = 1; if (i == sizeof(visbuf) - 1) break; } if (bs == 1) visbuf[--i] = '\0'; i = strnunvis(tokbuf, visbuf, sizeof(tokbuf)); if (c == EOF) parse_warn("eof in string constant"); else if (i == -1 || i >= sizeof(tokbuf)) parse_warn("string constant too long"); tval = tokbuf; return (TOK_STRING); }
int main(int argc, char *argv[]) { char inp[UCHAR_MAX + 1]; char out[4 * UCHAR_MAX + 1]; int i, j, fail = 0; ssize_t owant, o, r; for (i = 0; i <= UCHAR_MAX; i++) { inp[i] = i; } strvisx(out, inp, UCHAR_MAX + 1, 0); printf("%s\n", out); for (i = 0; i < NTESTS; i++) { arc4random_buf(ibuf, sizeof(ibuf) - 1); ibuf[sizeof(ibuf) - 1] = '\0'; title = 0; for (j = 0; j < sizeof(flags)/sizeof(flags[0]); j++) { owant = sizeof(ibuf); o = strnvis(obuf, ibuf, owant, flags[j]); if (o >= owant) { owant = o + 1; o = strnvis(obuf, ibuf, owant, flags[j]); if (o > owant) { dotitle(i, j); printf("HUGE overflow\n"); } if (o < owant - 1) { dotitle(i, j); printf("over-estimate of overflow\n"); } } else if (o > strlen(ibuf) * 4) { dotitle(i, j); printf("wants too much %d %d\n", o, strlen(ibuf) * 4); continue; } r = strnunvis(rbuf, obuf, sizeof rbuf); if (r == -1) { dotitle(i, j); printf("cannot decode\n"); printf("%s\n", obuf); fail = 1; } else if (r != strlen(ibuf)) { dotitle(i, j); printf("rlen %d != inlen %d\n", r, strlen(ibuf)); printf("%s\n", obuf); printf("%s\n", rbuf); fail = 1; } else if (bcmp(ibuf, rbuf, r)) { dotitle(i, j); printf("strings are different\n"); printf("%s\n", ibuf); printf("%s\n", rbuf); fail = 1; } } } exit(fail); }
static int read_spec(struct mtree_reader *r, char *s) { struct mtree_entry *entry; char name[MAXPATHLEN]; char *slash, *file, *word, *next; int ret; int skip = 0; /* Try to detect the format if it isn't known yet. */ if (r->path_last == -1 && detect_format(r, s) == -1) return (-1); if (r->path_last != 1) { /* * Path is surely in the first field, either because we know * the format or there is just one single field. */ read_word(s, &file, &next); assert(file != NULL); if (IS_DOTDOT(file)) { /* Only change the parent, keywords are ignored. */ if (r->parent == NULL) { mtree_reader_set_error(r, EINVAL, "`..' not allowed, no parent directory"); return (-1); } r->parent = r->parent->parent; return (0); } } entry = mtree_entry_create_empty(); if (entry == NULL) { mtree_reader_set_error(r, errno, NULL); return (-1); } if (r->path_last != 1) { if (next != NULL) { /* Read keyword that follow the path. */ ret = read_keywords(r, next, &entry->data, 1); if (ret == -1) { mtree_entry_free(entry); return (-1); } } } else { /* Path is at the end, read the keywords first. */ for (file = NULL; file == NULL;) { read_word(s, &word, &next); assert(word != NULL); if (next != NULL) { ret = read_keyword(r, word, &entry->data, 1); if (ret == -1) { mtree_entry_free(entry); return (-1); } s = next; } else file = word; } assert(file != NULL); if (IS_DOTDOT(file)) { mtree_reader_set_error(r, EINVAL, "`..' not allowed in " "this format"); mtree_entry_free(entry); return (-1); } } /* Copy /set values to the entry. */ mtree_entry_data_copy_keywords(&entry->data, &r->defaults, r->defaults.keywords, 0); /* * See if we should skip this file. */ if (SKIP_TYPE(r->options, entry->data.type)) { /* * With directories in version 1.0 format, we'll have to worry * about potentially setting the directory as the current * parent, other types can be skipped immediately. */ if (entry->data.type != MTREE_ENTRY_DIR || r->path_last == 1) goto skip; else skip = 1; } /* Save the file path and name. */ if (strnunvis(name, sizeof(name), file) == -1) { mtree_reader_set_error(r, ENAMETOOLONG, "File name too long: `%s'", file); mtree_entry_free(entry); return (-1); } if ((slash = strchr(name, '/')) != NULL) { /* * If the name contains a slash, it is a relative path. * These do not change the working directory. */ if (skip) goto skip; ret = mtree_cleanup_path(name, &entry->path, &entry->name); if (ret == -1) { mtree_reader_set_error(r, errno, NULL); mtree_entry_free(entry); return (-1); } } else { entry->name = strdup(name); if (entry->name == NULL) { mtree_reader_set_error(r, errno, NULL); mtree_entry_free(entry); return (-1); } entry->parent = r->parent; entry->path = create_v1_path(entry); if (entry->path == NULL) { mtree_reader_set_error(r, errno, NULL); mtree_entry_free(entry); return (-1); } } /* * We may want to skip this entry if a filter told us to skip * children of a directory, which is a parent of this entry. * * There is also no need to worry about changing the parent if * this happens. */ if (!skip && r->skip_trie != NULL) { char *tok; char *endptr; for (tok = strtok_r(name, "/", &endptr); tok != NULL; tok = strtok_r(NULL, "/", &endptr)) { if (mtree_trie_find(r->skip_trie, tok) != NULL) { mtree_entry_free(entry); return (0); } } } /* * Mark the current entry as the current directory. This only applies * to classic (1.0) entries and must be done after keywords are read. */ if (slash == NULL && entry->data.type == MTREE_ENTRY_DIR) r->parent = entry; if (skip) goto skip; if (r->filter != NULL) { int result; /* Apply filter. */ result = r->filter(entry, r->filter_data); if (result & MTREE_ENTRY_SKIP_CHILDREN) { if (entry->data.type == MTREE_ENTRY_DIR) { struct mtree_entry *found, *start; if (r->skip_trie == NULL) { r->skip_trie = mtree_trie_create(NULL); if (r->skip_trie == NULL) { mtree_reader_set_error(r, errno, NULL); return (-1); } } if (mtree_trie_insert(r->skip_trie, entry->path, TRIE_ITEM) == -1) { mtree_reader_set_error(r, errno, NULL); return (-1); } strcpy(name, entry->path); strcat(name, "/"); /* * Remove children that are already in the * list. */ start = r->entries; for (;;) { found = mtree_entry_find_prefix(start, name); if (found == NULL) break; start = found->next; r->entries = mtree_entry_unlink( r->entries, found); mtree_entry_free(found); } } } if (result & MTREE_ENTRY_SKIP) goto skip; } r->entries = mtree_entry_prepend(r->entries, entry); return (0); skip: /* * Skipping directory which is the current parent would make its files * end up in a wrong directory. * * Keep such entries in a list of `loose' entries instead, these will * be deleted when all the work is done. */ if (entry == r->parent) r->loose = mtree_entry_prepend(r->loose, entry); else mtree_entry_free(entry); return (0); }
/* * Read a single mtree keyword and either set or unset it in `data'. */ static int read_keyword(struct mtree_reader *r, char *s, struct mtree_entry_data *data, int set) { char *value; char name[MAXPATHLEN]; const char *endptr; uint64_t keyword; int64_t num; value = strchr(s, '='); if (value != NULL) *value++ = '\0'; keyword = mtree_keyword_parse(s); if (keyword == 0) { /* * Unsupported keyword, this is not an error. */ WARN("Ignoring unknown keyword `%s'", s); return (0); } if (!set || (r->spec_keywords & keyword) == 0) { /* Unset the keyword. */ data->keywords &= ~keyword; return (0); } /* * Read the keyword value. * * Wherever possible, the value is validated and error is indicated * in case the value is invalid or missing. */ errno = 0; switch (keyword) { case MTREE_KEYWORD_CKSUM: if (value == NULL) { errno = ENOENT; break; } num = mtree_atol(value, &endptr); if (num < 0 || num > UINT32_MAX || *endptr != '\0') errno = EINVAL; else data->cksum = (uint32_t)num; break; case MTREE_KEYWORD_CONTENTS: if (value == NULL) { errno = ENOENT; break; } if (strnunvis(name, sizeof(name), value) == -1) errno = ENAMETOOLONG; else mtree_copy_string(&data->contents, value); break; case MTREE_KEYWORD_DEVICE: if (value == NULL) { errno = ENOENT; break; } if (data->device == NULL) { data->device = mtree_device_create(); if (data->device == NULL) break; } if (mtree_device_parse(data->device, value) == -1) mtree_reader_set_error(r, errno, "%s", mtree_device_get_error(data->device)); break; case MTREE_KEYWORD_FLAGS: if (value == NULL) { errno = ENOENT; break; } mtree_copy_string(&data->flags, value); break; case MTREE_KEYWORD_GID: if (value == NULL) { errno = ENOENT; break; } data->st_gid = mtree_atol(value, &endptr); if (*endptr != '\0') errno = EINVAL; break; case MTREE_KEYWORD_GNAME: if (value == NULL) { errno = ENOENT; break; } mtree_copy_string(&data->gname, value); break; case MTREE_KEYWORD_IGNORE: /* No value */ break; case MTREE_KEYWORD_INODE: if (value == NULL) { errno = ENOENT; break; } num = mtree_atol(value, &endptr); if (num < 0 || *endptr != '\0') errno = EINVAL; else data->st_ino = (uint64_t)num; break; case MTREE_KEYWORD_LINK: if (value == NULL) { errno = ENOENT; break; } if (strnunvis(name, sizeof(name), value) == -1) errno = ENAMETOOLONG; else mtree_copy_string(&data->link, name); break; case MTREE_KEYWORD_MD5: case MTREE_KEYWORD_MD5DIGEST: if (value == NULL) { errno = ENOENT; break; } mtree_copy_string(&data->md5digest, value); break; case MTREE_KEYWORD_MODE: if (value == NULL) { errno = ENOENT; break; } if (value[0] >= '0' && value[0] <= '9') { data->st_mode = (int)mtree_atol8(value, &endptr); if (*endptr != '\0') errno = EINVAL; } else errno = EINVAL; break; case MTREE_KEYWORD_NLINK: if (value == NULL) { errno = ENOENT; break; } data->st_nlink = mtree_atol(value, &endptr); if (*endptr != '\0') errno = EINVAL; break; case MTREE_KEYWORD_NOCHANGE: /* No value */ break; case MTREE_KEYWORD_OPTIONAL: /* No value */ break; case MTREE_KEYWORD_RESDEVICE: if (value == NULL) { errno = ENOENT; break; } if (data->resdevice == NULL) { data->resdevice = mtree_device_create(); if (data->resdevice == NULL) break; } if (mtree_device_parse(data->resdevice, value) == -1) mtree_reader_set_error(r, errno, "%s", mtree_device_get_error(data->resdevice)); break; case MTREE_KEYWORD_RIPEMD160DIGEST: case MTREE_KEYWORD_RMD160: case MTREE_KEYWORD_RMD160DIGEST: if (value == NULL) { errno = ENOENT; break; } mtree_copy_string(&data->rmd160digest, value); break; case MTREE_KEYWORD_SHA1: case MTREE_KEYWORD_SHA1DIGEST: if (value == NULL) { errno = ENOENT; break; } mtree_copy_string(&data->sha1digest, value); break; case MTREE_KEYWORD_SHA256: case MTREE_KEYWORD_SHA256DIGEST: if (value == NULL) { errno = ENOENT; break; } mtree_copy_string(&data->sha256digest, value); break; case MTREE_KEYWORD_SHA384: case MTREE_KEYWORD_SHA384DIGEST: if (value == NULL) { errno = ENOENT; break; } mtree_copy_string(&data->sha384digest, value); break; case MTREE_KEYWORD_SHA512: case MTREE_KEYWORD_SHA512DIGEST: if (value == NULL) { errno = ENOENT; break; } mtree_copy_string(&data->sha512digest, value); break; case MTREE_KEYWORD_SIZE: if (value == NULL) { errno = ENOENT; break; } /* * It doesn't make a lot of sense to allow negative values * here, but this value is related to off_t, which is signed. */ data->st_size = mtree_atol(value, &endptr); if (*endptr != '\0') errno = EINVAL; break; case MTREE_KEYWORD_TAGS: if (value == NULL) { errno = ENOENT; break; } mtree_copy_string(&data->tags, value); break; case MTREE_KEYWORD_TIME: if (value == NULL) { errno = ENOENT; break; } num = mtree_atol10(value, &endptr); if (num < TIME_T_MIN || num > TIME_T_MAX) { errno = EINVAL; break; } if (*endptr != '\0' && *endptr != '.') { errno = EINVAL; break; } data->st_mtim.tv_sec = (time_t)num; if (*endptr == '.') { num = mtree_atol10(endptr + 1, &endptr); if (*endptr != '\0') { errno = EINVAL; break; } data->st_mtim.tv_nsec = (long)num; } else data->st_mtim.tv_nsec = 0; break; case MTREE_KEYWORD_TYPE: if (value == NULL) { errno = ENOENT; break; } data->type = mtree_entry_type_parse(value); if (data->type == MTREE_ENTRY_UNKNOWN) errno = EINVAL; break; case MTREE_KEYWORD_UID: if (value == NULL) { errno = ENOENT; break; } data->st_uid = mtree_atol(value, &endptr); if (*endptr != '\0') errno = EINVAL; break; case MTREE_KEYWORD_UNAME: if (value == NULL) { errno = ENOENT; break; } mtree_copy_string(&data->uname, value); break; } if (errno != 0) { if (errno == ENOENT) mtree_reader_set_error(r, errno, "`%s': missing keyword " "value", s); else if (errno == EINVAL) mtree_reader_set_error(r, errno, "`%s': invalid keyword " "value `%s'", s, value); else if (errno == ENAMETOOLONG) mtree_reader_set_error(r, errno, "`%s': file name too " "long: `%s'", s, value); else { /* These should be just memory errors. */ mtree_reader_set_error(r, errno, NULL); } return (-1); } data->keywords |= keyword; return (0); }