static int shrink_directory(struct exfat* ef, struct exfat_node* dir, off64_t deleted_offset) { const struct exfat_node* node; const struct exfat_node* last_node; uint64_t entries = 1; /* a directory always has at leat 1 entry (EOD) */ uint64_t new_size; struct exfat_entry eod; off64_t eod_offset; int rc; if (!(dir->flags & EXFAT_ATTRIB_DIR)) exfat_bug("attempted to shrink a file"); if (!(dir->flags & EXFAT_ATTRIB_CACHED)) exfat_bug("attempted to shrink uncached directory"); for (last_node = node = dir->child; node; node = node->next) { if (deleted_offset < node->entry_offset) { /* there are other entries after the removed one, no way to shrink this directory */ return 0; } if (last_node->entry_offset < node->entry_offset) last_node = node; } if (last_node) { /* offset of the last entry */ entries += last_node->entry_offset / sizeof(struct exfat_entry); /* two subentries with meta info */ entries += 2; /* subentries with file name */ entries += DIV_ROUND_UP(utf16_length(last_node->name), EXFAT_ENAME_MAX); } new_size = DIV_ROUND_UP(entries * sizeof(struct exfat_entry), CLUSTER_SIZE(*ef->sb)) * CLUSTER_SIZE(*ef->sb); if (new_size == dir->size) return 0; rc = exfat_truncate(ef, dir, new_size); if (rc != 0) return rc; /* put EOD entry at the end of the last cluster */ memset(&eod, 0, sizeof(eod)); eod_offset = new_size - sizeof(struct exfat_entry); if (last_node) exfat_write_raw(&eod, sizeof(eod), co2o(ef, last_node->entry_cluster, eod_offset), ef->fd); else exfat_write_raw(&eod, sizeof(eod), co2o(ef, dir->start_cluster, eod_offset), ef->fd); return 0; }
void exfat_flush_node(struct exfat* ef, struct exfat_node* node) { cluster_t cluster; off64_t offset; off64_t meta1_offset, meta2_offset; struct exfat_entry_meta1 meta1; struct exfat_entry_meta2 meta2; if (ef->ro) exfat_bug("unable to flush node to read-only FS"); if (node->parent == NULL) return; /* do not flush unlinked node */ cluster = node->entry_cluster; offset = node->entry_offset; meta1_offset = co2o(ef, cluster, offset); next_entry(ef, node->parent, &cluster, &offset); meta2_offset = co2o(ef, cluster, offset); exfat_read_raw(&meta1, sizeof(meta1), meta1_offset, ef->fd); if (meta1.type != EXFAT_ENTRY_FILE) exfat_bug("invalid type of meta1: 0x%hhx", meta1.type); meta1.attrib = cpu_to_le16(node->flags); exfat_unix2exfat(node->mtime, &meta1.mdate, &meta1.mtime, &meta1.mtime_cs); exfat_unix2exfat(node->atime, &meta1.adate, &meta1.atime, NULL); exfat_read_raw(&meta2, sizeof(meta2), meta2_offset, ef->fd); if (meta2.type != EXFAT_ENTRY_FILE_INFO) exfat_bug("invalid type of meta2: 0x%hhx", meta2.type); meta2.size = meta2.real_size = cpu_to_le64(node->size); meta2.start_cluster = cpu_to_le32(node->start_cluster); meta2.flags = EXFAT_FLAG_ALWAYS1; /* empty files must not be marked as contiguous */ if (node->size != 0 && IS_CONTIGUOUS(*node)) meta2.flags |= EXFAT_FLAG_CONTIGUOUS; /* name hash remains unchanged, no need to recalculate it */ meta1.checksum = exfat_calc_checksum(&meta1, &meta2, node->name); exfat_write_raw(&meta1, sizeof(meta1), meta1_offset, ef->fd); exfat_write_raw(&meta2, sizeof(meta2), meta2_offset, ef->fd); node->flags &= ~EXFAT_ATTRIB_DIRTY; }
static void erase_entry(struct exfat* ef, struct exfat_node* node) { cluster_t cluster = node->entry_cluster; off64_t offset = node->entry_offset; int name_entries = DIV_ROUND_UP(utf16_length(node->name), EXFAT_ENAME_MAX); uint8_t entry_type; entry_type = EXFAT_ENTRY_FILE & ~EXFAT_ENTRY_VALID; exfat_write_raw(&entry_type, 1, co2o(ef, cluster, offset), ef->fd); next_entry(ef, node->parent, &cluster, &offset); entry_type = EXFAT_ENTRY_FILE_INFO & ~EXFAT_ENTRY_VALID; exfat_write_raw(&entry_type, 1, co2o(ef, cluster, offset), ef->fd); while (name_entries--) { next_entry(ef, node->parent, &cluster, &offset); entry_type = EXFAT_ENTRY_FILE_NAME & ~EXFAT_ENTRY_VALID; exfat_write_raw(&entry_type, 1, co2o(ef, cluster, offset), ef->fd); } }
ssize_t exfat_write(struct exfat* ef, struct exfat_node* node, const void* buffer, size_t size, off_t offset) { cluster_t cluster; const char* bufp = buffer; off_t lsize, loffset, remainder; if (offset + size > node->size) { int rc = exfat_truncate(ef, node, offset + size); if (rc != 0) return rc; } if (size == 0) return 0; cluster = exfat_advance_cluster(ef, node, offset / CLUSTER_SIZE(*ef->sb)); if (CLUSTER_INVALID(cluster)) { exfat_error("got invalid cluster"); return -1; } loffset = offset % CLUSTER_SIZE(*ef->sb); remainder = size; while (remainder > 0) { if (CLUSTER_INVALID(cluster)) { exfat_error("got invalid cluster"); return -1; } lsize = MIN(CLUSTER_SIZE(*ef->sb) - loffset, remainder); exfat_write_raw(bufp, lsize, exfat_c2o(ef, cluster) + loffset, ef->fd); bufp += lsize; loffset = 0; remainder -= lsize; cluster = exfat_next_cluster(ef, node, cluster); } exfat_update_mtime(node); return size - remainder; }