int ntfs_delete_reparse_index(ntfs_inode *ni) { ntfs_index_context *xr; ntfs_inode *xrni; ntfs_attr *na; le32 reparse_tag; int res; res = 0; na = ntfs_attr_open(ni, AT_REPARSE_POINT, AT_UNNAMED, 0); if (na) { /* * read the existing reparse data (the tag is enough) * and un-index it */ xr = open_reparse_index(ni->vol); if (xr) { if (remove_reparse_index(na,xr,&reparse_tag) < 0) res = -1; xrni = xr->ni; ntfs_index_entry_mark_dirty(xr); NInoSetDirty(xrni); ntfs_index_ctx_put(xr); ntfs_inode_close(xrni); } ntfs_attr_close(na); } return (res); }
/** * ntfs_inode_update_times - update selected time fields for ntfs inode * @ni: ntfs inode for which update time fields * @mask: select which time fields should be updated * * This function updates time fields to current time. Fields to update are * selected using @mask (see enum @ntfs_time_update_flags for posssible values). */ void ntfs_inode_update_times(ntfs_inode *ni, ntfs_time_update_flags mask) { time_t now; if (!ni) { ntfs_log_error("%s(): Invalid arguments.\n", __FUNCTION__); return; } if ((ni->mft_no < FILE_first_user && ni->mft_no != FILE_root) || NVolReadOnly(ni->vol) || !mask) return; now = time(NULL); if (mask & NTFS_UPDATE_ATIME) ni->last_access_time = now; if (mask & NTFS_UPDATE_MTIME) ni->last_data_change_time = now; if (mask & NTFS_UPDATE_CTIME) ni->last_mft_change_time = now; set_nino_flag(ni, TimesDirty); NInoFileNameSetDirty(ni); NInoSetDirty(ni); }
int ntfs_delete_object_id_index(ntfs_inode *ni) { ntfs_index_context *xo; ntfs_inode *xoni; ntfs_attr *na; OBJECT_ID_ATTR old_attr; int res; res = 0; na = ntfs_attr_open(ni, AT_OBJECT_ID, AT_UNNAMED, 0); if (na) { /* * read the existing object id * and un-index it */ xo = open_object_id_index(ni->vol); if (xo) { if (remove_object_id_index(na,xo,&old_attr) < 0) res = -1; xoni = xo->ni; ntfs_index_entry_mark_dirty(xo); NInoSetDirty(xoni); ntfs_index_ctx_put(xo); ntfs_inode_close(xoni); } ntfs_attr_close(na); } return (res); }
static int add_object_id(ntfs_inode *ni, int flags) { int res; u8 dummy; res = -1; /* default return */ if (!ntfs_attr_exist(ni,AT_OBJECT_ID, AT_UNNAMED,0)) { if (!(flags & XATTR_REPLACE)) { /* * no object id attribute : add one, * apparently, this does not feed the new value in * Note : NTFS version must be >= 3 */ if (ni->vol->major_ver >= 3) { res = ntfs_attr_add(ni, AT_OBJECT_ID, AT_UNNAMED, 0, &dummy, (s64)0); NInoSetDirty(ni); } else errno = ENOTSUP; } else errno = ENODATA; } else { if (flags & XATTR_CREATE) errno = EEXIST; else res = 0; } return (res); }
static int update_object_id(ntfs_inode *ni, ntfs_index_context *xo, const OBJECT_ID_ATTR *value, size_t size) { OBJECT_ID_ATTR old_attr; ntfs_attr *na; int oldsize; int written; int res; res = 0; na = ntfs_attr_open(ni, AT_OBJECT_ID, AT_UNNAMED, 0); if (na) { /* remove the existing index entry */ oldsize = remove_object_id_index(na,xo,&old_attr); if (oldsize < 0) res = -1; else { /* resize attribute */ res = ntfs_attr_truncate(na, (s64)sizeof(GUID)); /* write the object_id in attribute */ if (!res && value) { written = (int)ntfs_attr_pwrite(na, (s64)0, (s64)sizeof(GUID), &value->object_id); if (written != (s64)sizeof(GUID)) { ntfs_log_error("Failed to update " "object id\n"); errno = EIO; res = -1; } } /* write index part if provided */ if (!res && ((size < sizeof(OBJECT_ID_ATTR)) || set_object_id_index(ni,xo,value))) { /* * If cannot index, try to remove the object * id and log the error. There will be an * inconsistency if removal fails. */ ntfs_attr_rm(na); ntfs_log_error("Failed to index object id." " Possible corruption.\n"); } } ntfs_attr_close(na); NInoSetDirty(ni); } else res = -1; return (res); }
static int update_reparse_data(ntfs_inode *ni, ntfs_index_context *xr, const char *value, size_t size) { int res; int written; int oldsize; ntfs_attr *na; le32 reparse_tag; res = 0; na = ntfs_attr_open(ni, AT_REPARSE_POINT, AT_UNNAMED, 0); if (na) { /* remove the existing reparse data */ oldsize = remove_reparse_index(na,xr,&reparse_tag); if (oldsize < 0) res = -1; else { /* resize attribute */ res = ntfs_attr_truncate(na, (s64)size); /* overwrite value if any */ if (!res && value) { written = (int)ntfs_attr_pwrite(na, (s64)0, (s64)size, value); if (written != (s64)size) { ntfs_log_error("Failed to update " "reparse data\n"); errno = EIO; res = -1; } } if (!res && set_reparse_index(ni,xr, ((const REPARSE_POINT*)value)->reparse_tag) && (oldsize > 0)) { /* * If cannot index, try to remove the reparse * data and log the error. There will be an * inconsistency if removal fails. */ ntfs_attr_rm(na); ntfs_log_error("Failed to index reparse data." " Possible corruption.\n"); } } ntfs_attr_close(na); NInoSetDirty(ni); } else res = -1; return (res); }
int ntfs_set_ntfs_object_id(ntfs_inode *ni, const char *value, size_t size, int flags) { OBJECT_ID_INDEX_KEY key; ntfs_inode *xoni; ntfs_index_context *xo; int res; res = 0; if (ni && value && (size >= sizeof(GUID))) { xo = open_object_id_index(ni->vol); if (xo) { /* make sure the GUID was not used somewhere */ memcpy(&key.object_id, value, sizeof(GUID)); if (ntfs_index_lookup(&key, sizeof(OBJECT_ID_INDEX_KEY), xo)) { ntfs_index_ctx_reinit(xo); res = add_object_id(ni, flags); if (!res) { /* update value and index */ res = update_object_id(ni,xo, (const OBJECT_ID_ATTR*)value, size); } } else { /* GUID is present elsewhere */ res = -1; errno = EEXIST; } xoni = xo->ni; ntfs_index_entry_mark_dirty(xo); NInoSetDirty(xoni); ntfs_index_ctx_put(xo); ntfs_inode_close(xoni); } else { res = -1; } } else { errno = EINVAL; res = -1; } return (res ? -1 : 0); }
static int ntfs_fallocate(ntfs_inode *ni, s64 alloc_offs, s64 alloc_len) { s64 allocated_size; s64 data_size; ntfs_attr_search_ctx *ctx; ntfs_attr *na; runlist_element *oldrl; const char *errmess; int save_errno; int err; err = 0; /* Open the specified attribute. */ na = ntfs_attr_open(ni, attr_type, attr_name, attr_name_len); if (!na) { ntfs_log_perror("Failed to open attribute 0x%lx: ", (unsigned long)le32_to_cpu(attr_type)); err = -1; } else { errmess = (const char*)NULL; if (na->data_flags & ATTR_IS_COMPRESSED) { errmess= "Cannot fallocate a compressed file"; } /* Locate the attribute record, needed for updating sizes */ ctx = ntfs_attr_get_search_ctx(ni, NULL); if (!ctx) { errmess = "Failed to allocate a search context"; } if (errmess) { ntfs_log_error("%s\n",errmess); err = -1; } else { /* Get and save the initial allocations */ allocated_size = na->allocated_size; data_size = ni->data_size; err = ntfs_attr_map_whole_runlist(na); if (!err) { oldrl = ntfs_save_rl(na->rl); if (oldrl) { err = ntfs_full_allocation(na, ctx, alloc_offs, alloc_len); if (err) { save_errno = errno; ni->allocated_size = allocated_size; ni->data_size = data_size; ntfs_restore_rl(na, oldrl); errno = save_errno; } else { free(oldrl); /* Mark file name dirty, to update the sizes in directories */ NInoFileNameSetDirty(ni); NInoSetDirty(ni); } } else err = -1; } ntfs_attr_put_search_ctx(ctx); } /* Close the attribute. */ ntfs_attr_close(na); } return (err); }
/** * ntfs_inode_mark_dirty - set the inode (and its base inode if it exists) dirty * @ni: ntfs inode to set dirty * * Set the inode @ni dirty so it is written out later (at the latest at * ntfs_inode_close() time). If @ni is an extent inode, set the base inode * dirty, too. * * This function cannot fail. */ void ntfs_inode_mark_dirty(ntfs_inode *ni) { NInoSetDirty(ni); if (ni->nr_extents == -1) NInoSetDirty(ni->base_ni); }
/** * ntfs_inode_sync - write the inode (and its dirty extents) to disk * @ni: ntfs inode to write * * Write the inode @ni to disk as well as its dirty extent inodes if such * exist and @ni is a base inode. If @ni is an extent inode, only @ni is * written completely disregarding its base inode and any other extent inodes. * * For a base inode with dirty extent inodes if any writes fail for whatever * reason, the failing inode is skipped and the sync process is continued. At * the end the error condition that brought about the failure is returned. Thus * the smallest amount of data loss possible occurs. * * Return 0 on success or -1 on error with errno set to the error code. * The following error codes are defined: * EINVAL - Invalid arguments were passed to the function. * EBUSY - Inode and/or one of its extents is busy, try again later. * EIO - I/O error while writing the inode (or one of its extents). */ int ntfs_inode_sync(ntfs_inode *ni) { int ret = 0; int err = 0; if (!ni) { errno = EINVAL; ntfs_log_error("Failed to sync NULL inode\n"); return -1; } ntfs_log_enter("Entering for inode %lld\n", (long long)ni->mft_no); /* Update STANDARD_INFORMATION. */ if ((ni->mrec->flags & MFT_RECORD_IN_USE) && ni->nr_extents != -1 && ntfs_inode_sync_standard_information(ni)) { if (!err || errno == EIO) { err = errno; if (err != EIO) err = EBUSY; } } /* Update FILE_NAME's in the index. */ if ((ni->mrec->flags & MFT_RECORD_IN_USE) && ni->nr_extents != -1 && NInoFileNameTestAndClearDirty(ni) && ntfs_inode_sync_file_name(ni)) { if (!err || errno == EIO) { err = errno; if (err != EIO) err = EBUSY; } ntfs_log_perror("Failed to sync FILE_NAME (inode %lld)", (long long)ni->mft_no); NInoFileNameSetDirty(ni); } /* Write out attribute list from cache to disk. */ if ((ni->mrec->flags & MFT_RECORD_IN_USE) && ni->nr_extents != -1 && NInoAttrList(ni) && NInoAttrListTestAndClearDirty(ni)) { ntfs_attr *na; na = ntfs_attr_open(ni, AT_ATTRIBUTE_LIST, AT_UNNAMED, 0); if (!na) { if (!err || errno == EIO) { err = errno; if (err != EIO) err = EBUSY; ntfs_log_perror("Attribute list sync failed " "(open, inode %lld)", (long long)ni->mft_no); } NInoAttrListSetDirty(ni); goto sync_inode; } if (na->data_size == ni->attr_list_size) { if (ntfs_attr_pwrite(na, 0, ni->attr_list_size, ni->attr_list) != ni->attr_list_size) { if (!err || errno == EIO) { err = errno; if (err != EIO) err = EBUSY; ntfs_log_perror("Attribute list sync " "failed (write, inode %lld)", (long long)ni->mft_no); } NInoAttrListSetDirty(ni); } } else { err = EIO; ntfs_log_error("Attribute list sync failed (bad size, " "inode %lld)\n", (long long)ni->mft_no); NInoAttrListSetDirty(ni); } ntfs_attr_close(na); } sync_inode: /* Write this inode out to the $MFT (and $MFTMirr if applicable). */ if (NInoTestAndClearDirty(ni)) { if (ntfs_mft_record_write(ni->vol, ni->mft_no, ni->mrec)) { if (!err || errno == EIO) { err = errno; if (err != EIO) err = EBUSY; } NInoSetDirty(ni); ntfs_log_perror("MFT record sync failed, inode %lld", (long long)ni->mft_no); } } /* If this is a base inode with extents write all dirty extents, too. */ if (ni->nr_extents > 0) { s32 i; for (i = 0; i < ni->nr_extents; ++i) { ntfs_inode *eni; eni = ni->extent_nis[i]; if (!NInoTestAndClearDirty(eni)) continue; if (ntfs_mft_record_write(eni->vol, eni->mft_no, eni->mrec)) { if (!err || errno == EIO) { err = errno; if (err != EIO) err = EBUSY; } NInoSetDirty(eni); ntfs_log_perror("Extent MFT record sync failed," " inode %lld/%lld", (long long)ni->mft_no, (long long)eni->mft_no); } } } if (err) { errno = err; ret = -1; } ntfs_log_leave("\n"); return ret; }
ntfs_inode *ntfsCreate (ntfs_vd *vd, const char *path, mode_t type, const char *target) { ntfs_inode *dir_ni = NULL, *ni = NULL; char *dir = NULL; char *name = NULL; ntfschar *uname = NULL, *utarget = NULL; int uname_len, utarget_len; // Sanity check if (!vd) { errno = ENODEV; return NULL; } // You cannot link between devices if(target) { if(vd != ntfsGetVolume(target)) { errno = EXDEV; return NULL; } } // Get the actual paths of the entry path = ntfsRealPath(path); target = ntfsRealPath(target); if (!path) { errno = EINVAL; return NULL; } // Lock ntfsLock(vd); // Get the unicode name for the entry and find its parent directory // TODO: This looks horrible, clean it up dir = strdup(path); if (!dir) { errno = EINVAL; goto cleanup; } name = strrchr(dir, '/'); if (name) name++; else name = dir; uname_len = ntfsLocalToUnicode(name, &uname); if (uname_len < 0) { errno = EINVAL; goto cleanup; } name = strrchr(dir, '/'); if(name) { name++; name[0] = 0; } // Open the entries parent directory dir_ni = ntfsOpenEntry(vd, dir); if (!dir_ni) { goto cleanup; } // Create the entry switch (type) { // Symbolic link case S_IFLNK: if (!target) { errno = EINVAL; goto cleanup; } utarget_len = ntfsLocalToUnicode(target, &utarget); if (utarget_len < 0) { errno = EINVAL; goto cleanup; } ni = ntfs_create_symlink(dir_ni, 0, uname, uname_len, utarget, utarget_len); break; // Directory or file case S_IFDIR: case S_IFREG: ni = ntfs_create(dir_ni, 0, uname, uname_len, type); break; } // If the entry was created if (ni) { // Mark the entry for archiving ni->flags |= FILE_ATTR_ARCHIVE; // Mark the entry as dirty NInoSetDirty(ni); // Sync the entry to disc ntfsSync(vd, ni); // Update parent directories times ntfsUpdateTimes(vd, dir_ni, NTFS_UPDATE_MCTIME); } cleanup: if(dir_ni) ntfsCloseEntry(vd, dir_ni); // use free because the value was not allocated with ntfs_alloc if(utarget) free(utarget); if(uname) free(uname); if(dir) ntfs_free(dir); // Unlock ntfsUnlock(vd); return ni; }
int ntfs_remove_ntfs_reparse_data(ntfs_inode *ni) { int res; int olderrno; ntfs_attr *na; ntfs_inode *xrni; ntfs_index_context *xr; le32 reparse_tag; res = 0; if (ni) { /* * open and delete the reparse data */ na = ntfs_attr_open(ni, AT_REPARSE_POINT, AT_UNNAMED,0); if (na) { /* first remove index (reparse data needed) */ xr = open_reparse_index(ni->vol); if (xr) { if (remove_reparse_index(na,xr, &reparse_tag) < 0) { res = -1; } else { /* now remove attribute */ res = ntfs_attr_rm(na); if (!res) { ni->flags &= ~FILE_ATTR_REPARSE_POINT; NInoFileNameSetDirty(ni); } else { /* * If we could not remove the * attribute, try to restore the * index and log the error. There * will be an inconsistency if * the reindexing fails. */ set_reparse_index(ni, xr, reparse_tag); ntfs_log_error( "Failed to remove reparse data." " Possible corruption.\n"); } } xrni = xr->ni; ntfs_index_entry_mark_dirty(xr); NInoSetDirty(xrni); ntfs_index_ctx_put(xr); ntfs_inode_close(xrni); } olderrno = errno; ntfs_attr_close(na); /* avoid errno pollution */ if (errno == ENOENT) errno = olderrno; } else { errno = ENODATA; res = -1; } NInoSetDirty(ni); } else { errno = EINVAL; res = -1; } return (res ? -1 : 0); }
int ntfs_set_ntfs_reparse_data(ntfs_inode *ni, const char *value, size_t size, int flags) { int res; u8 dummy; ntfs_inode *xrni; ntfs_index_context *xr; res = 0; if (ni && valid_reparse_data(ni, (const REPARSE_POINT*)value, size)) { xr = open_reparse_index(ni->vol); if (xr) { if (!ntfs_attr_exist(ni,AT_REPARSE_POINT, AT_UNNAMED,0)) { if (!(flags & XATTR_REPLACE)) { /* * no reparse data attribute : add one, * apparently, this does not feed the new value in * Note : NTFS version must be >= 3 */ if (ni->vol->major_ver >= 3) { res = ntfs_attr_add(ni, AT_REPARSE_POINT, AT_UNNAMED,0,&dummy, (s64)0); if (!res) { ni->flags |= FILE_ATTR_REPARSE_POINT; NInoFileNameSetDirty(ni); } NInoSetDirty(ni); } else { errno = EOPNOTSUPP; res = -1; } } else { errno = ENODATA; res = -1; } } else { if (flags & XATTR_CREATE) { errno = EEXIST; res = -1; } } if (!res) { /* update value and index */ res = update_reparse_data(ni,xr,value,size); } xrni = xr->ni; ntfs_index_entry_mark_dirty(xr); NInoSetDirty(xrni); ntfs_index_ctx_put(xr); ntfs_inode_close(xrni); } else { res = -1; } } else { errno = EINVAL; res = -1; } return (res ? -1 : 0); }
int ntfs_remove_ntfs_object_id(ntfs_inode *ni) { int res; int olderrno; ntfs_attr *na; ntfs_inode *xoni; ntfs_index_context *xo; int oldsize; OBJECT_ID_ATTR old_attr; res = 0; if (ni) { /* * open and delete the object id */ na = ntfs_attr_open(ni, AT_OBJECT_ID, AT_UNNAMED,0); if (na) { /* first remove index (old object id needed) */ xo = open_object_id_index(ni->vol); if (xo) { oldsize = remove_object_id_index(na,xo, &old_attr); if (oldsize < 0) { res = -1; } else { /* now remove attribute */ res = ntfs_attr_rm(na); if (res && (oldsize > (int)sizeof(GUID))) { /* * If we could not remove the * attribute, try to restore the * index and log the error. There * will be an inconsistency if * the reindexing fails. */ set_object_id_index(ni, xo, &old_attr); ntfs_log_error( "Failed to remove object id." " Possible corruption.\n"); } } xoni = xo->ni; ntfs_index_entry_mark_dirty(xo); NInoSetDirty(xoni); ntfs_index_ctx_put(xo); ntfs_inode_close(xoni); } olderrno = errno; ntfs_attr_close(na); /* avoid errno pollution */ if (errno == ENOENT) errno = olderrno; } else { errno = ENODATA; res = -1; } NInoSetDirty(ni); } else { errno = EINVAL; res = -1; } return (res ? -1 : 0); }