📄 dir.c
字号:
ntfs_log_trace("failed.\n"); if (ctx) ntfs_attr_put_search_ctx(ctx); free(ia); free(bmp); if (bmp_na) ntfs_attr_close(bmp_na); if (ia_na) ntfs_attr_close(ia_na); errno = eo; return -1;}/** * __ntfs_create - create object on ntfs volume * @dir_ni: ntfs inode for directory in which create new object * @name: unicode name of new object * @name_len: length of the name in unicode characters * @type: type of the object to create * @dev: major and minor device numbers (obtained from makedev()) * @target: target in unicode (only for symlinks) * @target_len: length of target in unicode charcters * * Internal, use ntfs_create{,_device,_symlink} wrappers instead. * * @type can be: * S_IFREG to create regular file * S_IFDIR to create directory * S_IFBLK to create block device * S_IFCHR to create character device * S_IFLNK to create symbolic link * S_IFIFO to create FIFO * S_IFSOCK to create socket * other values are invalid. * * @dev is used only if @type is S_IFBLK or S_IFCHR, in other cases its value * ignored. * * @target and @target_len are used only if @type is S_IFLNK, in other cases * their value ignored. * * Return opened ntfs inode that describes created object on success or NULL * on error with errno set to the error code. */static ntfs_inode *__ntfs_create(ntfs_inode *dir_ni, ntfschar *name, u8 name_len, dev_t type, dev_t dev, ntfschar *target, u8 target_len){ ntfs_inode *ni; int rollback_data = 0; FILE_NAME_ATTR *fn = NULL; STANDARD_INFORMATION *si = NULL; int err, fn_len, si_len; ntfs_log_trace("Entering.\n"); /* Sanity checks. */ if (!dir_ni || !name || !name_len) { ntfs_log_error("Invalid arguments.\n"); errno = EINVAL; return NULL; } /* Allocate MFT record for new file. */ ni = ntfs_mft_record_alloc(dir_ni->vol, NULL); if (!ni) { err = errno; ntfs_log_error("Failed to allocate new MFT record: %s.\n", strerror(err)); errno = err; return NULL; } /* * Create STANDARD_INFORMATION attribute. Write STANDARD_INFORMATION * version 1.2, windows will upgrade it to version 3 if needed. */ si_len = offsetof(STANDARD_INFORMATION, v1_end); si = calloc(1, si_len); if (!si) { err = errno; ntfs_log_error("Not enough memory.\n"); goto err_out; } si->creation_time = utc2ntfs(ni->creation_time); si->last_data_change_time = utc2ntfs(ni->last_data_change_time); si->last_mft_change_time = utc2ntfs(ni->last_mft_change_time); si->last_access_time = utc2ntfs(ni->last_access_time); if (!S_ISREG(type) && !S_ISDIR(type)) { si->file_attributes = FILE_ATTR_SYSTEM; ni->flags = FILE_ATTR_SYSTEM; } /* Add STANDARD_INFORMATION to inode. */ if (ntfs_attr_add(ni, AT_STANDARD_INFORMATION, AT_UNNAMED, 0, (u8*)si, si_len)) { err = errno; ntfs_log_error("Failed to add STANDARD_INFORMATION " "attribute.\n"); goto err_out; } if (S_ISDIR(type)) { INDEX_ROOT *ir = NULL; INDEX_ENTRY *ie; int ir_len, index_len; /* Create INDEX_ROOT attribute. */ index_len = sizeof(INDEX_HEADER) + sizeof(INDEX_ENTRY_HEADER); ir_len = offsetof(INDEX_ROOT, index) + index_len; ir = calloc(1, ir_len); if (!ir) { err = errno; ntfs_log_error("Not enough memory.\n"); goto err_out; } ir->type = AT_FILE_NAME; ir->collation_rule = COLLATION_FILE_NAME; ir->index_block_size = cpu_to_le32(ni->vol->indx_record_size); if (ni->vol->cluster_size <= ni->vol->indx_record_size) ir->clusters_per_index_block = ni->vol->indx_record_size >> ni->vol->cluster_size_bits; else ir->clusters_per_index_block = -ni->vol->indx_record_size_bits; ir->index.entries_offset = cpu_to_le32(sizeof(INDEX_HEADER)); ir->index.index_length = cpu_to_le32(index_len); ir->index.allocated_size = cpu_to_le32(index_len); ie = (INDEX_ENTRY*)((u8*)ir + sizeof(INDEX_ROOT)); ie->length = cpu_to_le16(sizeof(INDEX_ENTRY_HEADER)); ie->key_length = 0; ie->flags = INDEX_ENTRY_END; /* Add INDEX_ROOT attribute to inode. */ if (ntfs_attr_add(ni, AT_INDEX_ROOT, NTFS_INDEX_I30, 4, (u8*)ir, ir_len)) { err = errno; free(ir); ntfs_log_error("Failed to add INDEX_ROOT attribute.\n"); goto err_out; } } else { INTX_FILE *data; int data_len; switch (type) { case S_IFBLK: case S_IFCHR: data_len = offsetof(INTX_FILE, device_end); data = malloc(data_len); if (!data) { err = errno; ntfs_log_error("Not enough memory for " "content of DATA " "attribute.\n"); goto err_out; } data->major = cpu_to_le64(major(dev)); data->minor = cpu_to_le64(minor(dev)); if (type == S_IFBLK) data->magic = INTX_BLOCK_DEVICE; if (type == S_IFCHR) data->magic = INTX_CHARACTER_DEVICE; break; case S_IFLNK: data_len = sizeof(INTX_FILE_TYPES) + target_len * sizeof(ntfschar); data = malloc(data_len); if (!data) { err = errno; ntfs_log_error("Not enough memory for " "content of DATA " "attribute.\n"); goto err_out; } data->magic = INTX_SYMBOLIC_LINK; memcpy(data->target, target, target_len * sizeof(ntfschar)); break; case S_IFSOCK: data = NULL; data_len = 1; break; default: /* FIFO or regular file. */ data = NULL; data_len = 0; break; } /* Add DATA attribute to inode. */ if (ntfs_attr_add(ni, AT_DATA, AT_UNNAMED, 0, (u8*)data, data_len)) { err = errno; ntfs_log_error("Failed to add DATA attribute.\n"); goto err_out; } rollback_data = 1; free(data); } /* Create FILE_NAME attribute. */ fn_len = sizeof(FILE_NAME_ATTR) + name_len * sizeof(ntfschar); fn = calloc(1, fn_len); if (!fn) { err = errno; ntfs_log_error("Not enough memory.\n"); goto err_out; } fn->parent_directory = MK_LE_MREF(dir_ni->mft_no, le16_to_cpu(dir_ni->mrec->sequence_number)); fn->file_name_length = name_len; fn->file_name_type = FILE_NAME_POSIX; if (S_ISDIR(type)) fn->file_attributes = FILE_ATTR_I30_INDEX_PRESENT; if (!S_ISREG(type) && !S_ISDIR(type)) fn->file_attributes = FILE_ATTR_SYSTEM; fn->creation_time = utc2ntfs(ni->creation_time); fn->last_data_change_time = utc2ntfs(ni->last_data_change_time); fn->last_mft_change_time = utc2ntfs(ni->last_mft_change_time); fn->last_access_time = utc2ntfs(ni->last_access_time); memcpy(fn->file_name, name, name_len * sizeof(ntfschar)); /* Add FILE_NAME attribute to inode. */ if (ntfs_attr_add(ni, AT_FILE_NAME, AT_UNNAMED, 0, (u8*)fn, fn_len)) { err = errno; ntfs_log_error("Failed to add FILE_NAME attribute.\n"); goto err_out; } /* Add FILE_NAME attribute to index. */ if (ntfs_index_add_filename(dir_ni, fn, MK_MREF(ni->mft_no, le16_to_cpu(ni->mrec->sequence_number)))) { err = errno; ntfs_log_error("Failed to add entry to the index: %s.\n", strerror(err)); goto err_out; } /* Set hard links count and directory flag. */ ni->mrec->link_count = cpu_to_le16(1); if (S_ISDIR(type)) ni->mrec->flags |= MFT_RECORD_IS_DIRECTORY; ntfs_inode_mark_dirty(ni); /* Done! */ free(fn); free(si); ntfs_log_trace("Done.\n"); return ni;err_out: ntfs_log_trace("Failed.\n"); if (rollback_data) { ntfs_attr *na; na = ntfs_attr_open(ni, AT_DATA, AT_UNNAMED, 0); if (!na) ntfs_log_perror("Failed to open data attribute of " " inode 0x%llx. Run chkdsk.\n", (unsigned long long)ni->mft_no); else if (ntfs_attr_rm(na)) ntfs_log_perror("Failed to remove data attribute of " "inode 0x%llx. Run chkdsk.\n", (unsigned long long)ni->mft_no); } /* * Free extent MFT records (should not exist any with current * ntfs_create implementation, but for any case if something will be * changed in the future). */ while (ni->nr_extents) if (ntfs_mft_record_free(ni->vol, *(ni->extent_nis))) { err = errno; ntfs_log_error("Failed to free extent MFT record. " "Leaving inconsistent metadata.\n"); } if (ntfs_mft_record_free(ni->vol, ni)) ntfs_log_error("Failed to free MFT record. " "Leaving inconsistent metadata. Run chkdsk.\n"); free(fn); free(si); errno = err; return NULL;}/** * Some wrappers around __ntfs_create() ... */ntfs_inode *ntfs_create(ntfs_inode *dir_ni, ntfschar *name, u8 name_len, dev_t type){ if (type != S_IFREG && type != S_IFDIR && type != S_IFIFO && type != S_IFSOCK) { ntfs_log_error("Invalid arguments.\n"); return NULL; } return __ntfs_create(dir_ni, name, name_len, type, 0, NULL, 0);}ntfs_inode *ntfs_create_device(ntfs_inode *dir_ni, ntfschar *name, u8 name_len, dev_t type, dev_t dev){ if (type != S_IFCHR && type != S_IFBLK) { ntfs_log_error("Invalid arguments.\n"); return NULL; } return __ntfs_create(dir_ni, name, name_len, type, dev, NULL, 0);}ntfs_inode *ntfs_create_symlink(ntfs_inode *dir_ni, ntfschar *name, u8 name_len, ntfschar *target, u8 target_len){ if (!target || !target_len) { ntfs_log_error("Invalid arguments.\n"); return NULL; } return __ntfs_create(dir_ni, name, name_len, S_IFLNK, 0, target, target_len);}/** * ntfs_delete - delete file or directory from ntfs volume * @ni: ntfs inode for object to delte * @dir_ni: ntfs inode for directory in which delete object * @name: unicode name of the object to delete * @name_len: length of the name in unicode characters * * @ni is always closed after the call to this function (even if it failed), * user does not need to call ntfs_inode_close himself. * * Return 0 on success or -1 on error with errno set to the error code. */int ntfs_delete(ntfs_inode *ni, ntfs_inode *dir_ni, ntfschar *name, u8 name_len){ ntfs_attr_search_ctx *actx = NULL; ntfs_index_context *ictx = NULL; FILE_NAME_ATTR *fn = NULL; BOOL looking_for_dos_name = FALSE, looking_for_win32_name = FALSE; BOOL case_sensitive_match = TRUE; int err = 0; ntfs_log_trace("Entering.\n"); if (!ni || !dir_ni || !name || !name_len) { ntfs_log_error("Invalid arguments.\n"); errno = EINVAL; goto err_out; } if (ni->nr_extents == -1) ni = ni->base_ni; if (dir_ni->nr_extents == -1) dir_ni = dir_ni->base_ni; /* * Search for FILE_NAME attribute with such name. If it's in POSIX or * WIN32_AND_DOS namespace, then simply remove it from index and inode. * If filename in DOS or in WIN32 namespace, then remove DOS name first, * only then remove WIN32 name. Mark WIN32 name as POSIX name to prevent * chkdsk to complain about DOS name absentation, in case if DOS name * had been successfully deleted, but WIN32 name removing failed. */ actx = ntfs_attr_get_search_ctx(ni, NULL); if (!actx) goto err_out;search: while (!ntfs_attr_lookup(AT_FILE_NAME, AT_UNNAMED, 0, CASE_SENSITIVE, 0, NULL, 0, actx)) { errno = 0; fn = (FILE_NAME_ATTR*)((u8*)actx->attr + le16_to_cpu(actx->attr->value_offset)); if (looking_for_dos_name) { if (fn->file_name_type == FILE_NAME_DOS) break; else continue; } if (looking_for_win32_name) { if (fn->file_name_type == FILE_NAME_WIN32) break; else continue; } if (dir_ni->mft_no == MREF_LE(fn->parent_directory) && ntfs_names_are_equal(fn->file_name, fn->file_name_length, name, name_len, (fn->file_name_type == FILE_NAME_POSIX || case_sensitive_match) ? CASE_SENSITIVE : IGNORE_CASE, ni->vol->upcase, ni->vol->upcase_len)) { if (fn->file_name_type == FILE_NAME_WIN32) { looking_for_dos_name = TRUE; ntfs_attr_reinit_search_ctx(actx); continue; } if (fn->file_name_type == FILE_NAME_DOS) looking_for_dos_name = TRUE; break; } } if (errno) { /* * If case sensitive search failed, then try once again * ignoring case. */ if (errno == ENOENT && case_sensitive_match) { case_sensitive_match = FALSE; ntfs_attr_reinit_search_ctx(actx); goto search; } goto err_out; } /* If deleting directory check it to be empty. */ if (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY) { ntfs_attr *na; na = ntfs_attr_open(ni, AT_INDEX_ROOT, NTFS_INDEX_I30, 4); if (!na) { ntfs_log_error("Corrupt directory or library bug.\n"); errno = EIO; goto err_out; } /* * Do not allow non-empty directory deletion if hard links count * is 1 (always) or 2 (in case if filename in DOS namespace, * because we delete it first in filen which have both WIN32 and * DOS names). */ if ((na->data_size != sizeof(INDEX_ROOT) + sizeof( INDEX_ENTRY_HEADER)) && (le16_to_cpu( ni->mrec->link_count) == 1 || (le16_to_cpu(ni->mrec->link_count) == 2 && fn->file_name_type == FILE_NAME_DOS))) { ntfs_attr_close(na); ntfs_log_error("Directory is not empty.\n"); errno = ENOTEMPTY; goto err_out; } ntfs_attr_close(na); } /* Search for such FILE_NAME in index. */ ictx = ntfs_index_ctx_get(dir_ni, NTFS_INDEX_I30, 4); if (!ictx) goto err_out; if (ntfs_index_lookup(fn, le32_to_cpu(actx->attr->value_length), ictx)) goto err_out; /* Set namespace to POSIX for WIN32 name. */ if (fn->file_name_type == FILE_NAME_WIN32) { fn->file_name_type = FILE_NAME_POSIX; ntfs_inode_mark_dirty(actx->ntfs_ino); ((FILE_NAME_ATTR*)ictx->data)->file_name_type = FILE_NAME_POSIX; ntfs_index_entry_mark_dirty(ictx); } /* Do not support reparse point deletion yet. */ if (((FILE_NAME_ATTR*)ictx->data)->file_attributes & FILE_ATTR_REPARSE_POINT) { errno = EOPNOTSUPP; goto err_out; } /* Remove FILE_NAME from index. */ if (ntfs_index_rm(ictx)) goto err_out; ictx = NULL; /* Remove FILE_NAME from inode. */ if (ntfs_attr_record_rm(actx)) goto err_out; /* Decerement hard link count. */ ni->mrec->link_count = cpu_to_le16(le16_to_cpu( ni->mrec->link_count) - 1); ntfs_inode_mark_dirty(ni); if (looking_for_dos_name) { looking_for_dos_name = FALSE; looking_for_win32_name = TRUE; ntfs_attr_reinit_search_ctx(actx); goto search; } /* TODO: Update object id, quota and securiry indexes if required. */ /* * If hard link count is not equal to zero then we are done. In other * case there are no reference to this inode left, so we should free all * non-resident attributes and mark all MFT record as not in use. */ if (ni->mrec->link_count) goto out; ntfs_attr_reinit_search_ctx(actx); while (!ntfs_attrs_walk(actx)) { if (actx->attr->non_resident) { runlist *rl; rl = ntfs_mapping_pairs_decompress(ni->vol, actx->attr, NULL); if (!rl) { err = errno; ntfs_log_error("Failed to decompress runlist. " "Leaving inconsistent metadata.\n"); continue; } if (ntfs_cluster_free_from_rl(ni->vol, rl)) { err = errno; ntfs_log_error("Failed to free clusters. " "Leaving inconsistent metadata.\n"); continue; } free(rl); } } if (errno != ENOENT) { err = errno; ntfs_log_error("Attribute enumeration failed. " "Probably leaving inconsistent metadata.\n"); } /* All extents should be attached after attribute walk. */ while (ni->nr_extents) if (ntfs_mft_record_free(ni->vol, *(ni->extent_nis))) { err = errno; ntfs_log_error("Failed to free extent MFT record. " "Leaving inconsistent metadata.\n"); } if (ntfs_mft_record_free(ni->vol, ni)) { err = errno; ntfs_log_error("Failed to free base MFT record. " "Leaving inconsistent metadata.\n"); } ni = NULL;out: if (actx) ntfs_attr_put_search_ctx(actx); if (ictx) ntfs_index_ctx_put(ictx); if (ni) ntfs_inode_close(ni); if (err) { ntfs_log_error("Failed.\n"); errno = err; return -1; } ntfs_log_trace("Done.\n"); return 0;err_out: err = errno; goto out;}/** * ntfs_link - create hard link for file or directory * @ni: ntfs inode for object to create hard link * @dir_ni: ntfs inode for directory in which new link should be placed
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -