X-Git-Url: https://git.sven.stormbind.net/?a=blobdiff_plain;f=libexfat%2Fnode.c;h=9cffbcb8a68cfc36da6965560578ddb0cc1d6c4c;hb=1e7534bbaa34e6c2a8c9809dfcf6588112cbdee4;hp=a3f00af1ba0d700a43d18397731fbd754eb13640;hpb=914023217c196c0831fe3295d9d8c7176b7e3609;p=sven%2Ffuse-exfat.git diff --git a/libexfat/node.c b/libexfat/node.c index a3f00af..9cffbcb 100644 --- a/libexfat/node.c +++ b/libexfat/node.c @@ -3,7 +3,7 @@ exFAT file system implementation library. Free exFAT implementation. - Copyright (C) 2010-2017 Andrew Nayenko + Copyright (C) 2010-2018 Andrew Nayenko This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -87,7 +87,7 @@ static int read_entries(struct exfat* ef, struct exfat_node* dir, size = exfat_generic_pread(ef, dir, entries, sizeof(struct exfat_entry[n]), offset); - if (size == sizeof(struct exfat_entry[n])) + if (size == (ssize_t) sizeof(struct exfat_entry) * n) return 0; /* success */ if (size == 0) return -ENOENT; @@ -108,7 +108,7 @@ static int write_entries(struct exfat* ef, struct exfat_node* dir, size = exfat_generic_pwrite(ef, dir, entries, sizeof(struct exfat_entry[n]), offset); - if (size == sizeof(struct exfat_entry[n])) + if (size == (ssize_t) sizeof(struct exfat_entry) * n) return 0; /* success */ if (size < 0) return -EIO; @@ -135,9 +135,10 @@ static void init_node_meta1(struct exfat_node* node, node->attrib = le16_to_cpu(meta1->attrib); node->continuations = meta1->continuations; node->mtime = exfat_exfat2unix(meta1->mdate, meta1->mtime, - meta1->mtime_cs); + meta1->mtime_cs, meta1->mtime_tzo); /* there is no centiseconds field for atime */ - node->atime = exfat_exfat2unix(meta1->adate, meta1->atime, 0); + node->atime = exfat_exfat2unix(meta1->adate, meta1->atime, + 0, meta1->atime_tzo); } static void init_node_meta2(struct exfat_node* node, @@ -204,10 +205,13 @@ static bool check_entries(const struct exfat_entry* entry, int n) return true; } -static bool check_node(const struct exfat_node* node, le16_t actual_checksum, - int cluster_size, const struct exfat_entry_meta1* meta1, +static bool check_node(const struct exfat* ef, struct exfat_node* node, + le16_t actual_checksum, const struct exfat_entry_meta1* meta1, const struct exfat_entry_meta2* meta2) { + int cluster_size = CLUSTER_SIZE(*ef->sb); + uint64_t clusters_heap_size = + (uint64_t) le32_to_cpu(ef->sb->cluster_count) * cluster_size; char buffer[EXFAT_UTF8_NAME_BUFFER_MAX]; bool ret = true; @@ -220,7 +224,8 @@ static bool check_node(const struct exfat_node* node, le16_t actual_checksum, exfat_get_name(node, buffer); exfat_error("'%s' has invalid checksum (%#hx != %#hx)", buffer, le16_to_cpu(actual_checksum), le16_to_cpu(meta1->checksum)); - ret = false; + if (!EXFAT_REPAIR(invalid_node_checksum, ef, node)) + ret = false; } /* @@ -251,7 +256,7 @@ static bool check_node(const struct exfat_node* node, le16_t actual_checksum, node->start_cluster); ret = false; } - if (node->size > 0 && CLUSTER_INVALID(node->start_cluster)) + if (node->size > 0 && CLUSTER_INVALID(*ef->sb, node->start_cluster)) { exfat_get_name(node, buffer); exfat_error("'%s' points to invalid cluster %#x", buffer, @@ -259,6 +264,15 @@ static bool check_node(const struct exfat_node* node, le16_t actual_checksum, ret = false; } + /* File or directory cannot be larger than clusters heap. */ + if (node->size > clusters_heap_size) + { + exfat_get_name(node, buffer); + exfat_error("'%s' is larger than clusters heap: %"PRIu64" > %"PRIu64, + buffer, node->size, clusters_heap_size); + ret = false; + } + /* Empty file or directory must be marked as non-contiguous. */ if (node->size == 0 && node->is_contiguous) { @@ -280,8 +294,8 @@ static bool check_node(const struct exfat_node* node, le16_t actual_checksum, return ret; } -static int parse_file_entries(struct exfat* ef, struct exfat_node* parent, - struct exfat_node* node, const struct exfat_entry* entries, int n) +static int parse_file_entries(struct exfat* ef, struct exfat_node* node, + const struct exfat_entry* entries, int n) { const struct exfat_entry_meta1* meta1; const struct exfat_entry_meta2* meta2; @@ -314,8 +328,7 @@ static int parse_file_entries(struct exfat* ef, struct exfat_node* parent, init_node_meta2(node, meta2); init_node_name(node, entries + 2, mandatory_entries - 2); - if (!check_node(node, exfat_calc_checksum(entries, n), - CLUSTER_SIZE(*ef->sb), meta1, meta2)) + if (!check_node(ef, node, exfat_calc_checksum(entries, n), meta1, meta2)) return -EIO; return 0; @@ -337,7 +350,7 @@ static int parse_file_entry(struct exfat* ef, struct exfat_node* parent, return -ENOMEM; (*node)->entry_offset = *offset; - rc = parse_file_entries(ef, parent, *node, entries, n); + rc = parse_file_entries(ef, *node, entries, n); if (rc != 0) { free(*node); @@ -383,6 +396,7 @@ static int readdir(struct exfat* ef, struct exfat_node* parent, const struct exfat_entry_label* label; uint64_t upcase_size = 0; le16_t* upcase_comp = NULL; + le16_t label_name[EXFAT_ENAME_MAX]; for (;;) { @@ -401,7 +415,7 @@ static int readdir(struct exfat* ef, struct exfat_node* parent, if (ef->upcase != NULL) break; upcase = (const struct exfat_entry_upcase*) &entry; - if (CLUSTER_INVALID(le32_to_cpu(upcase->start_cluster))) + if (CLUSTER_INVALID(*ef->sb, le32_to_cpu(upcase->start_cluster))) { exfat_error("invalid cluster 0x%x in upcase table", le32_to_cpu(upcase->start_cluster)); @@ -452,14 +466,13 @@ static int readdir(struct exfat* ef, struct exfat_node* parent, case EXFAT_ENTRY_BITMAP: bitmap = (const struct exfat_entry_bitmap*) &entry; ef->cmap.start_cluster = le32_to_cpu(bitmap->start_cluster); - if (CLUSTER_INVALID(ef->cmap.start_cluster)) + if (CLUSTER_INVALID(*ef->sb, ef->cmap.start_cluster)) { exfat_error("invalid cluster 0x%x in clusters bitmap", ef->cmap.start_cluster); return -EIO; } - ef->cmap.size = le32_to_cpu(ef->sb->cluster_count) - - EXFAT_FIRST_DATA_CLUSTER; + ef->cmap.size = le32_to_cpu(ef->sb->cluster_count); if (le64_to_cpu(bitmap->size) < DIV_ROUND_UP(ef->cmap.size, 8)) { exfat_error("invalid clusters bitmap size: %"PRIu64 @@ -496,7 +509,10 @@ static int readdir(struct exfat* ef, struct exfat_node* parent, exfat_error("too long label (%hhu chars)", label->length); return -EIO; } - if (utf16_to_utf8(ef->label, label->name, + /* copy to a temporary buffer to avoid unaligned access to a + packed member */ + memcpy(label_name, label->name, sizeof(label_name)); + if (exfat_utf16_to_utf8(ef->label, label_name, sizeof(ef->label), EXFAT_ENAME_MAX) != 0) return -EIO; break; @@ -506,7 +522,8 @@ static int readdir(struct exfat* ef, struct exfat_node* parent, break; /* deleted entry, ignore it */ exfat_error("unknown entry type %#hhx", entry.type); - return -EIO; + if (!EXFAT_REPAIR(unknown_entry, ef, parent, &entry, *offset)) + return -EIO; } *offset += sizeof(entry); } @@ -615,6 +632,7 @@ int exfat_flush_node(struct exfat* ef, struct exfat_node* node) struct exfat_entry_meta1* meta1 = (struct exfat_entry_meta1*) &entries[0]; struct exfat_entry_meta2* meta2 = (struct exfat_entry_meta2*) &entries[1]; int rc; + le16_t edate, etime; if (!node->is_dirty) return 0; /* no need to flush */ @@ -633,9 +651,14 @@ int exfat_flush_node(struct exfat* ef, struct exfat_node* node) return -EIO; meta1->attrib = cpu_to_le16(node->attrib); - exfat_unix2exfat(node->mtime, &meta1->mdate, &meta1->mtime, - &meta1->mtime_cs); - exfat_unix2exfat(node->atime, &meta1->adate, &meta1->atime, NULL); + exfat_unix2exfat(node->mtime, &edate, &etime, + &meta1->mtime_cs, &meta1->mtime_tzo); + meta1->mdate = edate; + meta1->mtime = etime; + exfat_unix2exfat(node->atime, &edate, &etime, + NULL, &meta1->atime_tzo); + meta1->adate = edate; + meta1->atime = etime; meta2->size = meta2->valid_size = cpu_to_le64(node->size); meta2->start_cluster = cpu_to_le32(node->start_cluster); meta2->flags = EXFAT_FLAG_ALWAYS1; @@ -718,7 +741,7 @@ static int shrink_directory(struct exfat* ef, struct exfat_node* dir, /* two subentries with meta info */ entries += 2; /* subentries with file name */ - entries += DIV_ROUND_UP(utf16_length(last_node->name), + entries += DIV_ROUND_UP(exfat_utf16_length(last_node->name), EXFAT_ENAME_MAX); } @@ -786,7 +809,7 @@ static int check_slot(struct exfat* ef, struct exfat_node* dir, off_t offset, { struct exfat_entry entries[n]; int rc; - size_t i; + int i; /* Root directory contains entries, that don't have any nodes associated with them (clusters bitmap, upper case table, label). We need to be @@ -824,7 +847,7 @@ static int find_slot(struct exfat* ef, struct exfat_node* dir, return -ENOMEM; } for (p = dir->child; p != NULL; p = p->next) - for (i = 0; i < 1 + p->continuations; i++) + for (i = 0; i < 1u + p->continuations; i++) BMAP_SET(dmap, p->entry_offset / sizeof(struct exfat_entry) + i); /* find a slot in the directory entries bitmap */ @@ -835,20 +858,24 @@ static int find_slot(struct exfat* ef, struct exfat_node* dir, if (contiguous++ == 0) *offset = (off_t) i * sizeof(struct exfat_entry); if (contiguous == n) + { + int rc; + /* suitable slot is found, check that it's not occupied */ - switch (check_slot(ef, dir, *offset, n)) + rc = check_slot(ef, dir, *offset, n); + if (rc == -EINVAL) { - case 0: - free(dmap); - return 0; - case -EIO: - free(dmap); - return -EIO; - case -EINVAL: - /* slot is occupied, continue searching */ + /* slot at (i-n) is occupied, go back and check (i-n+1) */ + i -= contiguous - 1; contiguous = 0; - break; } + else + { + /* slot is free or an error occurred */ + free(dmap); + return rc; + } + } } else contiguous = 0; @@ -865,27 +892,29 @@ static int find_slot(struct exfat* ef, struct exfat_node* dir, } static int commit_entry(struct exfat* ef, struct exfat_node* dir, - const le16_t* name, cluster_t cluster, off_t offset, uint16_t attrib) + const le16_t* name, off_t offset, uint16_t attrib) { struct exfat_node* node; - const size_t name_length = utf16_length(name); + const size_t name_length = exfat_utf16_length(name); const int name_entries = DIV_ROUND_UP(name_length, EXFAT_ENAME_MAX); struct exfat_entry entries[2 + name_entries]; struct exfat_entry_meta1* meta1 = (struct exfat_entry_meta1*) &entries[0]; struct exfat_entry_meta2* meta2 = (struct exfat_entry_meta2*) &entries[1]; int i; int rc; + le16_t edate, etime; memset(entries, 0, sizeof(struct exfat_entry[2])); meta1->type = EXFAT_ENTRY_FILE; meta1->continuations = 1 + name_entries; meta1->attrib = cpu_to_le16(attrib); - exfat_unix2exfat(time(NULL), &meta1->crdate, &meta1->crtime, - &meta1->crtime_cs); - meta1->adate = meta1->mdate = meta1->crdate; - meta1->atime = meta1->mtime = meta1->crtime; + exfat_unix2exfat(time(NULL), &edate, &etime, + &meta1->crtime_cs, &meta1->crtime_tzo); + meta1->adate = meta1->mdate = meta1->crdate = edate; + meta1->atime = meta1->mtime = meta1->crtime = etime; meta1->mtime_cs = meta1->crtime_cs; /* there is no atime_cs */ + meta1->atime_tzo = meta1->mtime_tzo = meta1->crtime_tzo; meta2->type = EXFAT_ENTRY_FILE_INFO; meta2->flags = EXFAT_FLAG_ALWAYS1; @@ -925,7 +954,6 @@ static int create(struct exfat* ef, const char* path, uint16_t attrib) { struct exfat_node* dir; struct exfat_node* existing; - cluster_t cluster = EXFAT_CLUSTER_BAD; off_t offset = -1; le16_t name[EXFAT_NAME_MAX + 1]; int rc; @@ -941,13 +969,13 @@ static int create(struct exfat* ef, const char* path, uint16_t attrib) } rc = find_slot(ef, dir, &offset, - 2 + DIV_ROUND_UP(utf16_length(name), EXFAT_ENAME_MAX)); + 2 + DIV_ROUND_UP(exfat_utf16_length(name), EXFAT_ENAME_MAX)); if (rc != 0) { exfat_put_node(ef, dir); return rc; } - rc = commit_entry(ef, dir, name, cluster, offset, attrib); + rc = commit_entry(ef, dir, name, offset, attrib); if (rc != 0) { exfat_put_node(ef, dir); @@ -995,10 +1023,9 @@ int exfat_mkdir(struct exfat* ef, const char* path) } static int rename_entry(struct exfat* ef, struct exfat_node* dir, - struct exfat_node* node, const le16_t* name, cluster_t new_cluster, - off_t new_offset) + struct exfat_node* node, const le16_t* name, off_t new_offset) { - const size_t name_length = utf16_length(name); + const size_t name_length = exfat_utf16_length(name); const int name_entries = DIV_ROUND_UP(name_length, EXFAT_ENAME_MAX); struct exfat_entry entries[2 + name_entries]; struct exfat_entry_meta1* meta1 = (struct exfat_entry_meta1*) &entries[0]; @@ -1048,7 +1075,6 @@ int exfat_rename(struct exfat* ef, const char* old_path, const char* new_path) struct exfat_node* node; struct exfat_node* existing; struct exfat_node* dir; - cluster_t cluster = EXFAT_CLUSTER_BAD; off_t offset = -1; le16_t name[EXFAT_NAME_MAX + 1]; int rc; @@ -1122,14 +1148,14 @@ int exfat_rename(struct exfat* ef, const char* old_path, const char* new_path) } rc = find_slot(ef, dir, &offset, - 2 + DIV_ROUND_UP(utf16_length(name), EXFAT_ENAME_MAX)); + 2 + DIV_ROUND_UP(exfat_utf16_length(name), EXFAT_ENAME_MAX)); if (rc != 0) { exfat_put_node(ef, dir); exfat_put_node(ef, node); return rc; } - rc = rename_entry(ef, dir, node, name, cluster, offset); + rc = rename_entry(ef, dir, node, name, offset); if (rc != 0) { exfat_put_node(ef, dir); @@ -1191,7 +1217,8 @@ int exfat_set_label(struct exfat* ef, const char* label) struct exfat_entry_label entry; memset(label_utf16, 0, sizeof(label_utf16)); - rc = utf8_to_utf16(label_utf16, label, EXFAT_ENAME_MAX + 1, strlen(label)); + rc = exfat_utf8_to_utf16(label_utf16, label, EXFAT_ENAME_MAX + 1, + strlen(label)); if (rc != 0) return rc; @@ -1202,7 +1229,7 @@ int exfat_set_label(struct exfat* ef, const char* label) return rc; entry.type = EXFAT_ENTRY_LABEL; - entry.length = utf16_length(label_utf16); + entry.length = exfat_utf16_length(label_utf16); memcpy(entry.name, label_utf16, sizeof(entry.name)); if (entry.length == 0) entry.type ^= EXFAT_ENTRY_VALID;