| /* |
| * Misc FS utilities |
| * |
| * Copyright (c) 2016-2017 Fabrice Bellard |
| * |
| * Permission is hereby granted, free of charge, to any person obtaining a copy |
| * of this software and associated documentation files (the "Software"), to deal |
| * in the Software without restriction, including without limitation the rights |
| * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
| * copies of the Software, and to permit persons to whom the Software is |
| * furnished to do so, subject to the following conditions: |
| * |
| * The above copyright notice and this permission notice shall be included in |
| * all copies or substantial portions of the Software. |
| * |
| * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
| * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
| * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL |
| * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
| * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
| * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN |
| * THE SOFTWARE. |
| */ |
| #include <stdlib.h> |
| #include <stdio.h> |
| #include <stdarg.h> |
| #include <string.h> |
| #include <inttypes.h> |
| #include <assert.h> |
| #include <fcntl.h> |
| #include <errno.h> |
| #include <unistd.h> |
| #include <time.h> |
| #include <ctype.h> |
| #include <sys/file.h> |
| |
| #include "cutils.h" |
| #include "list.h" |
| #include "fs_utils.h" |
| |
| /* last byte is the version */ |
| const uint8_t encrypted_file_magic[4] = { 0xfb, 0xa2, 0xe9, 0x01 }; |
| |
| char *compose_path(const char *path, const char *name) |
| { |
| int path_len, name_len; |
| char *d, *q; |
| |
| if (path[0] == '\0') { |
| d = strdup(name); |
| } else { |
| path_len = strlen(path); |
| name_len = strlen(name); |
| d = malloc(path_len + 1 + name_len + 1); |
| q = d; |
| memcpy(q, path, path_len); |
| q += path_len; |
| if (path[path_len - 1] != '/') |
| *q++ = '/'; |
| memcpy(q, name, name_len + 1); |
| } |
| return d; |
| } |
| |
| char *compose_url(const char *base_url, const char *name) |
| { |
| if (strchr(name, ':')) { |
| return strdup(name); |
| } else { |
| return compose_path(base_url, name); |
| } |
| } |
| |
| void skip_line(const char **pp) |
| { |
| const char *p; |
| p = *pp; |
| while (*p != '\n' && *p != '\0') |
| p++; |
| if (*p == '\n') |
| p++; |
| *pp = p; |
| } |
| |
| char *quoted_str(const char *str) |
| { |
| const char *s; |
| char *q; |
| int c; |
| char *buf; |
| |
| if (str[0] == '\0') |
| goto use_quote; |
| s = str; |
| while (*s != '\0') { |
| if (*s <= ' ' || *s > '~') |
| goto use_quote; |
| s++; |
| } |
| return strdup(str); |
| use_quote: |
| buf = malloc(strlen(str) * 4 + 2 + 1); |
| q = buf; |
| s = str; |
| *q++ = '"'; |
| while (*s != '\0') { |
| c = *(uint8_t *)s; |
| if (c < ' ' || c == 127) { |
| q += sprintf(q, "\\x%02x", c); |
| } else if (c == '\\' || c == '\"') { |
| q += sprintf(q, "\\%c", c); |
| } else { |
| *q++ = c; |
| } |
| s++; |
| } |
| *q++ = '"'; |
| *q = '\0'; |
| return buf; |
| } |
| |
| int parse_fname(char *buf, int buf_size, const char **pp) |
| { |
| const char *p; |
| char *q; |
| int c, h; |
| |
| p = *pp; |
| while (isspace_nolf(*p)) |
| p++; |
| if (*p == '\0') |
| return -1; |
| q = buf; |
| if (*p == '"') { |
| p++; |
| for(;;) { |
| c = *p++; |
| if (c == '\0' || c == '\n') { |
| return -1; |
| } else if (c == '\"') { |
| break; |
| } else if (c == '\\') { |
| c = *p++; |
| switch(c) { |
| case '\'': |
| case '\"': |
| case '\\': |
| goto add_char; |
| case 'n': |
| c = '\n'; |
| goto add_char; |
| case 'r': |
| c = '\r'; |
| goto add_char; |
| case 't': |
| c = '\t'; |
| goto add_char; |
| case 'x': |
| h = from_hex(*p++); |
| if (h < 0) |
| return -1; |
| c = h << 4; |
| h = from_hex(*p++); |
| if (h < 0) |
| return -1; |
| c |= h; |
| goto add_char; |
| default: |
| return -1; |
| } |
| } else { |
| add_char: |
| if (q >= buf + buf_size - 1) |
| return -1; |
| *q++ = c; |
| } |
| } |
| } else { |
| while (!isspace_nolf(*p) && *p != '\0' && *p != '\n') { |
| if (q >= buf + buf_size - 1) |
| return -1; |
| *q++ = *p++; |
| } |
| } |
| *q = '\0'; |
| *pp = p; |
| return 0; |
| } |
| |
| int parse_uint32_base(uint32_t *pval, const char **pp, int base) |
| { |
| const char *p, *p1; |
| p = *pp; |
| while (isspace_nolf(*p)) |
| p++; |
| *pval = strtoul(p, (char **)&p1, base); |
| if (p1 == p) |
| return -1; |
| *pp = p1; |
| return 0; |
| } |
| |
| int parse_uint64_base(uint64_t *pval, const char **pp, int base) |
| { |
| const char *p, *p1; |
| p = *pp; |
| while (isspace_nolf(*p)) |
| p++; |
| *pval = strtoull(p, (char **)&p1, base); |
| if (p1 == p) |
| return -1; |
| *pp = p1; |
| return 0; |
| } |
| |
| int parse_uint64(uint64_t *pval, const char **pp) |
| { |
| return parse_uint64_base(pval, pp, 0); |
| } |
| |
| int parse_uint32(uint32_t *pval, const char **pp) |
| { |
| return parse_uint32_base(pval, pp, 0); |
| } |
| |
| int parse_time(uint32_t *psec, uint32_t *pnsec, const char **pp) |
| { |
| const char *p; |
| uint32_t v, m; |
| p = *pp; |
| if (parse_uint32(psec, &p) < 0) |
| return -1; |
| v = 0; |
| if (*p == '.') { |
| p++; |
| /* XXX: inefficient */ |
| m = 1000000000; |
| v = 0; |
| while (*p >= '0' && *p <= '9') { |
| m /= 10; |
| v += (*p - '0') * m; |
| p++; |
| } |
| } |
| *pnsec = v; |
| *pp = p; |
| return 0; |
| } |
| |
| int parse_file_id(FSFileID *pval, const char **pp) |
| { |
| return parse_uint64_base(pval, pp, 16); |
| } |
| |
| char *file_id_to_filename(char *buf, FSFileID file_id) |
| { |
| sprintf(buf, "%016" PRIx64, file_id); |
| return buf; |
| } |
| |
| void encode_hex(char *str, const uint8_t *buf, int len) |
| { |
| int i; |
| for(i = 0; i < len; i++) |
| sprintf(str + 2 * i, "%02x", buf[i]); |
| } |
| |
| int decode_hex(uint8_t *buf, const char *str, int len) |
| { |
| int h0, h1, i; |
| |
| for(i = 0; i < len; i++) { |
| h0 = from_hex(str[2 * i]); |
| if (h0 < 0) |
| return -1; |
| h1 = from_hex(str[2 * i + 1]); |
| if (h1 < 0) |
| return -1; |
| buf[i] = (h0 << 4) | h1; |
| } |
| return 0; |
| } |
| |
| /* return NULL if no end of header found */ |
| const char *skip_header(const char *p) |
| { |
| p = strstr(p, "\n\n"); |
| if (!p) |
| return NULL; |
| return p + 2; |
| } |
| |
| /* return 0 if OK, < 0 if error */ |
| int parse_tag(char *buf, int buf_size, const char *str, const char *tag) |
| { |
| char tagname[128], *q; |
| const char *p, *p1; |
| int len; |
| |
| p = str; |
| for(;;) { |
| if (*p == '\0' || *p == '\n') |
| break; |
| q = tagname; |
| while (*p != ':' && *p != '\n' && *p != '\0') { |
| if ((q - tagname) < sizeof(tagname) - 1) |
| *q++ = *p; |
| p++; |
| } |
| *q = '\0'; |
| if (*p != ':') |
| return -1; |
| p++; |
| while (isspace_nolf(*p)) |
| p++; |
| p1 = p; |
| p = strchr(p, '\n'); |
| if (!p) |
| len = strlen(p1); |
| else |
| len = p - p1; |
| if (!strcmp(tagname, tag)) { |
| if (len > buf_size - 1) |
| len = buf_size - 1; |
| memcpy(buf, p1, len); |
| buf[len] = '\0'; |
| return 0; |
| } |
| if (!p) |
| break; |
| else |
| p++; |
| } |
| return -1; |
| } |
| |
| int parse_tag_uint64(uint64_t *pval, const char *str, const char *tag) |
| { |
| char buf[64]; |
| const char *p; |
| if (parse_tag(buf, sizeof(buf), str, tag)) |
| return -1; |
| p = buf; |
| return parse_uint64(pval, &p); |
| } |
| |
| int parse_tag_file_id(FSFileID *pval, const char *str, const char *tag) |
| { |
| char buf[64]; |
| const char *p; |
| if (parse_tag(buf, sizeof(buf), str, tag)) |
| return -1; |
| p = buf; |
| return parse_uint64_base(pval, &p, 16); |
| } |
| |
| int parse_tag_version(const char *str) |
| { |
| uint64_t version; |
| if (parse_tag_uint64(&version, str, "Version")) |
| return -1; |
| return version; |
| } |
| |
| BOOL is_url(const char *path) |
| { |
| return (strstart(path, "http:", NULL) || |
| strstart(path, "https:", NULL) || |
| strstart(path, "file:", NULL)); |
| } |