static bool 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; if (exfat_pwrite(ef->dev, &entry_type, 1, co2o(ef, cluster, offset)) < 0) { exfat_error("failed to erase meta1 entry"); return false; } next_entry(ef, node->parent, &cluster, &offset); entry_type = EXFAT_ENTRY_FILE_INFO & ~EXFAT_ENTRY_VALID; if (exfat_pwrite(ef->dev, &entry_type, 1, co2o(ef, cluster, offset)) < 0) { exfat_error("failed to erase meta2 entry"); return false; } while (name_entries--) { next_entry(ef, node->parent, &cluster, &offset); entry_type = EXFAT_ENTRY_FILE_NAME & ~EXFAT_ENTRY_VALID; if (exfat_pwrite(ef->dev, &entry_type, 1, co2o(ef, cluster, offset)) < 0) { exfat_error("failed to erase name entry"); return false; } } return true; }
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; }
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 = 0; uint64_t new_size; 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 == 0) /* directory always has at least 1 cluster */ new_size = CLUSTER_SIZE(*ef->sb); if (new_size == dir->size) return 0; rc = exfat_truncate(ef, dir, new_size, true); if (rc != 0) return rc; return 0; }
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); } }
int fat_directory_write(fat_directory_t *di, const char *name, fat_dentry_t *de) { int rc; void *data; fat_instance_t *instance; rc = fs_instance_get(di->nodep->idx->service_id, &data); assert(rc == EOK); instance = (fat_instance_t *) data; if (fat_valid_short_name(name)) { /* * NAME could be directly stored in dentry without creating * LFN. */ fat_dentry_name_set(de, name); if (fat_directory_is_sfn_exist(di, de)) return EEXIST; rc = fat_directory_lookup_free(di, 1); if (rc != EOK) return rc; rc = fat_directory_write_dentry(di, de); return rc; } else if (instance->lfn_enabled && fat_valid_name(name)) { /* We should create long entries to store name */ int long_entry_count; uint8_t checksum; uint16_t wname[FAT_LFN_NAME_SIZE]; size_t lfn_size, lfn_offset; rc = str_to_utf16(wname, FAT_LFN_NAME_SIZE, name); if (rc != EOK) return rc; lfn_size = utf16_length(wname); long_entry_count = lfn_size / FAT_LFN_ENTRY_SIZE; if (lfn_size % FAT_LFN_ENTRY_SIZE) long_entry_count++; rc = fat_directory_lookup_free(di, long_entry_count + 1); if (rc != EOK) return rc; aoff64_t start_pos = di->pos; /* Write Short entry */ rc = fat_directory_create_sfn(di, de, name); if (rc != EOK) return rc; checksum = fat_dentry_chksum(de->name); rc = fat_directory_seek(di, start_pos + long_entry_count); if (rc != EOK) return rc; rc = fat_directory_write_dentry(di, de); if (rc != EOK) return rc; /* Write Long entry by parts */ lfn_offset = 0; fat_dentry_t *d; size_t idx = 0; do { rc = fat_directory_prev(di); if (rc != EOK) return rc; rc = fat_directory_get(di, &d); if (rc != EOK) return rc; fat_lfn_set_entry(wname, &lfn_offset, lfn_size + 1, d); FAT_LFN_CHKSUM(d) = checksum; FAT_LFN_ORDER(d) = ++idx; di->b->dirty = true; } while (lfn_offset < lfn_size); FAT_LFN_ORDER(d) |= FAT_LFN_LAST; rc = fat_directory_seek(di, start_pos + long_entry_count); return rc; } return ENOTSUP; }