Ejemplo n.º 1
0
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);
}
Ejemplo n.º 2
0
/*
 * 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);
}