static int64_t mtree_atol(char **p) { if (**p != '0') return mtree_atol10(p); if ((*p)[1] == 'x' || (*p)[1] == 'X') { *p += 2; return mtree_atol16(p); } return mtree_atol8(p); }
/* * Parse a single keyword and its value. */ static int parse_keyword(struct archive_read *a, struct mtree *mtree, struct archive_entry *entry, struct mtree_option *opt, int *parsed_kws) { char *val, *key; key = opt->value; if (*key == '\0') return (ARCHIVE_OK); if (strcmp(key, "nochange") == 0) { *parsed_kws |= MTREE_HAS_NOCHANGE; return (ARCHIVE_OK); } if (strcmp(key, "optional") == 0) { *parsed_kws |= MTREE_HAS_OPTIONAL; return (ARCHIVE_OK); } if (strcmp(key, "ignore") == 0) { /* * The mtree processing is not recursive, so * recursion will only happen for explicitly listed * entries. */ return (ARCHIVE_OK); } val = strchr(key, '='); if (val == NULL) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Malformed attribute \"%s\" (%d)", key, key[0]); return (ARCHIVE_WARN); } *val = '\0'; ++val; switch (key[0]) { case 'c': if (strcmp(key, "content") == 0 || strcmp(key, "contents") == 0) { parse_escapes(val, NULL); archive_strcpy(&mtree->contents_name, val); break; } if (strcmp(key, "cksum") == 0) break; case 'd': if (strcmp(key, "device") == 0) { *parsed_kws |= MTREE_HAS_DEVICE; return parse_device(&a->archive, entry, val); } case 'f': if (strcmp(key, "flags") == 0) { *parsed_kws |= MTREE_HAS_FFLAGS; archive_entry_copy_fflags_text(entry, val); break; } case 'g': if (strcmp(key, "gid") == 0) { *parsed_kws |= MTREE_HAS_GID; archive_entry_set_gid(entry, mtree_atol10(&val)); break; } if (strcmp(key, "gname") == 0) { *parsed_kws |= MTREE_HAS_GNAME; archive_entry_copy_gname(entry, val); break; } case 'l': if (strcmp(key, "link") == 0) { archive_entry_copy_symlink(entry, val); break; } case 'm': if (strcmp(key, "md5") == 0 || strcmp(key, "md5digest") == 0) break; if (strcmp(key, "mode") == 0) { if (val[0] >= '0' && val[0] <= '9') { *parsed_kws |= MTREE_HAS_PERM; archive_entry_set_perm(entry, (mode_t)mtree_atol8(&val)); } else { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Symbolic mode \"%s\" unsupported", val); return ARCHIVE_WARN; } break; } case 'n': if (strcmp(key, "nlink") == 0) { *parsed_kws |= MTREE_HAS_NLINK; archive_entry_set_nlink(entry, (unsigned int)mtree_atol10(&val)); break; } case 'r': if (strcmp(key, "rmd160") == 0 || strcmp(key, "rmd160digest") == 0) break; case 's': if (strcmp(key, "sha1") == 0 || strcmp(key, "sha1digest") == 0) break; if (strcmp(key, "sha256") == 0 || strcmp(key, "sha256digest") == 0) break; if (strcmp(key, "sha384") == 0 || strcmp(key, "sha384digest") == 0) break; if (strcmp(key, "sha512") == 0 || strcmp(key, "sha512digest") == 0) break; if (strcmp(key, "size") == 0) { archive_entry_set_size(entry, mtree_atol10(&val)); break; } case 't': if (strcmp(key, "tags") == 0) { /* * Comma delimited list of tags. * Ignore the tags for now, but the interface * should be extended to allow inclusion/exclusion. */ break; } if (strcmp(key, "time") == 0) { int64_t m; int64_t my_time_t_max = get_time_t_max(); int64_t my_time_t_min = get_time_t_min(); long ns; *parsed_kws |= MTREE_HAS_MTIME; m = mtree_atol10(&val); /* Replicate an old mtree bug: * 123456789.1 represents 123456789 * seconds and 1 nanosecond. */ if (*val == '.') { ++val; ns = (long)mtree_atol10(&val); } else ns = 0; if (m > my_time_t_max) m = my_time_t_max; else if (m < my_time_t_min) m = my_time_t_min; archive_entry_set_mtime(entry, (time_t)m, ns); break; } if (strcmp(key, "type") == 0) { switch (val[0]) { case 'b': if (strcmp(val, "block") == 0) { archive_entry_set_filetype(entry, AE_IFBLK); break; } case 'c': if (strcmp(val, "char") == 0) { archive_entry_set_filetype(entry, AE_IFCHR); break; } case 'd': if (strcmp(val, "dir") == 0) { archive_entry_set_filetype(entry, AE_IFDIR); break; } case 'f': if (strcmp(val, "fifo") == 0) { archive_entry_set_filetype(entry, AE_IFIFO); break; } if (strcmp(val, "file") == 0) { archive_entry_set_filetype(entry, AE_IFREG); break; } case 'l': if (strcmp(val, "link") == 0) { archive_entry_set_filetype(entry, AE_IFLNK); break; } default: archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Unrecognized file type \"%s\"; assuming \"file\"", val); archive_entry_set_filetype(entry, AE_IFREG); return (ARCHIVE_WARN); } *parsed_kws |= MTREE_HAS_TYPE; break; } case 'u': if (strcmp(key, "uid") == 0) { *parsed_kws |= MTREE_HAS_UID; archive_entry_set_uid(entry, mtree_atol10(&val)); break; } if (strcmp(key, "uname") == 0) { *parsed_kws |= MTREE_HAS_UNAME; archive_entry_copy_uname(entry, val); break; } default: archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Unrecognized key %s=%s", key, val); return (ARCHIVE_WARN); } return (ARCHIVE_OK); }
/* * 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); }