/** * ntfs_ir_truncate - Truncate index root attribute * * Returns STATUS_OK, STATUS_RESIDENT_ATTRIBUTE_FILLED_MFT or STATUS_ERROR. */ static int ntfs_ir_truncate(ntfs_index_context *icx, int data_size) { ntfs_attr *na; int ret; ntfs_log_trace("Entering\n"); na = ntfs_attr_open(icx->ni, AT_INDEX_ROOT, icx->name, icx->name_len); if (!na) { ntfs_log_perror("Failed to open INDEX_ROOT"); return STATUS_ERROR; } /* * INDEX_ROOT must be resident and its entries can be moved to * INDEX_BLOCK, so ENOSPC isn't a real error. */ ret = ntfs_attr_truncate(na, data_size + offsetof(INDEX_ROOT, index)); if (ret == STATUS_OK) { icx->ir = ntfs_ir_lookup2(icx->ni, icx->name, icx->name_len); if (!icx->ir) return STATUS_ERROR; icx->ir->index.allocated_size = cpu_to_le32(data_size); } else if (ret == STATUS_ERROR) ntfs_log_perror("Failed to truncate INDEX_ROOT"); ntfs_attr_close(na); return ret; }
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); }
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); }
status_t fs_rstat(fs_volume *_vol, fs_vnode *_node, struct stat *stbuf) { nspace *ns = (nspace*)_vol->private_volume; vnode *node = (vnode*)_node->private_node; ntfs_inode *ni = NULL; ntfs_attr *na; status_t result = B_NO_ERROR; LOCK_VOL(ns); ERRPRINT("fs_rstat - ENTER:\n"); if (ns == NULL || node == NULL ||stbuf == NULL) { result = ENOENT; goto exit; } ni = ntfs_inode_open(ns->ntvol, node->vnid); if (ni == NULL) { result = ENOENT; goto exit; } if (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY) { // Directory stbuf->st_mode = FS_DIR_MODE; na = ntfs_attr_open(ni, AT_INDEX_ALLOCATION, NTFS_INDEX_I30, 4); if (na) { stbuf->st_size = na->data_size; stbuf->st_blocks = na->allocated_size >> 9; ntfs_attr_close(na); } stbuf->st_nlink = 1; } else {
/** * cat */ static int cat(ntfs_volume *vol, ntfs_inode *inode, ATTR_TYPES type, ntfschar *name, int namelen) { const int bufsize = 4096; char *buffer; ntfs_attr *attr; s64 bytes_read, written; s64 offset; u32 block_size; buffer = malloc(bufsize); if (!buffer) return 1; attr = ntfs_attr_open(inode, type, name, namelen); if (!attr) { ntfs_log_error("Cannot find attribute type 0x%x.\n", le32_to_cpu(type)); free(buffer); return 1; } if ((inode->mft_no < 2) && (attr->type == AT_DATA)) block_size = vol->mft_record_size; else if (attr->type == AT_INDEX_ALLOCATION) block_size = index_get_size(inode); else block_size = 0; offset = 0; for (;;) { if (!opts.raw && block_size > 0) { // These types have fixup bytes_read = ntfs_attr_mst_pread(attr, offset, 1, block_size, buffer); if (bytes_read > 0) bytes_read *= block_size; } else { bytes_read = ntfs_attr_pread(attr, offset, bufsize, buffer); } //ntfs_log_info("read %lld bytes\n", bytes_read); if (bytes_read == -1) { ntfs_log_perror("ERROR: Couldn't read file"); break; } if (!bytes_read) break; written = fwrite(buffer, 1, bytes_read, stdout); if (written != bytes_read) { ntfs_log_perror("ERROR: Couldn't output all data!"); break; } offset += bytes_read; } ntfs_attr_close(attr); free(buffer); return 0; }
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 ntfs_attr *ntfs_ia_open(ntfs_index_context *icx, ntfs_inode *ni) { ntfs_attr *na; na = ntfs_attr_open(ni, AT_INDEX_ALLOCATION, icx->name, icx->name_len); if (!na) { ntfs_log_perror("Failed to open index allocation of inode " "%llu", (unsigned long long)ni->mft_no); return NULL; } return na; }
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); }
static int ntfs_ibm_modify(ntfs_index_context *icx, VCN vcn, int set) { u8 byte; s64 pos = ntfs_ibm_vcn_to_pos(icx, vcn); u32 bpos = pos / 8; u32 bit = 1 << (pos % 8); ntfs_attr *na; int ret = STATUS_ERROR; ntfs_log_trace("%s vcn: %lld\n", set ? "set" : "clear", (long long)vcn); na = ntfs_attr_open(icx->ni, AT_BITMAP, icx->name, icx->name_len); if (!na) { ntfs_log_perror("Failed to open $BITMAP attribute"); return -1; } if (set) { if (na->data_size < bpos + 1) { if (ntfs_attr_truncate(na, (na->data_size + 8) & ~7)) { ntfs_log_perror("Failed to truncate AT_BITMAP"); goto err_na; } } } if (ntfs_attr_pread(na, bpos, 1, &byte) != 1) { ntfs_log_perror("Failed to read $BITMAP"); goto err_na; } if (set) byte |= bit; else byte &= ~bit; if (ntfs_attr_pwrite(na, bpos, 1, &byte) != 1) { ntfs_log_perror("Failed to write $Bitmap"); goto err_na; } ret = STATUS_OK; err_na: ntfs_attr_close(na); return ret; }
static status_t get_node_type(ntfs_inode* ni, int* _type) { ntfs_attr* na; if (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY) { // Directory *_type = S_IFDIR; return B_OK; } else { // Regular or Interix (INTX) file *_type = S_IFREG; if (ni->flags & FILE_ATTR_SYSTEM) { na = ntfs_attr_open(ni, AT_DATA, NULL,0); if (!na) { return ENOENT; } // Check whether it's Interix symbolic link if (na->data_size <= sizeof(INTX_FILE_TYPES) + sizeof(ntfschar) * PATH_MAX && na->data_size > sizeof(INTX_FILE_TYPES)) { INTX_FILE *intx_file; intx_file = ntfs_malloc(na->data_size); if (!intx_file) { ntfs_attr_close(na); return EINVAL; } if (ntfs_attr_pread(na, 0, na->data_size, intx_file) != na->data_size) { free(intx_file); ntfs_attr_close(na); return EINVAL; } if (intx_file->magic == INTX_SYMBOLIC_LINK) *_type = FS_SLNK_MODE; free(intx_file); } ntfs_attr_close(na); } } return B_OK; }
int ntfsStat (ntfs_vd *vd, ntfs_inode *ni, struct stat *st) { ntfs_attr *na = NULL; int res = 0; // Sanity check if (!vd) { errno = ENODEV; return -1; } // Sanity check if (!ni) { errno = ENOENT; return -1; } // Short circuit cases were we don't actually have to do anything if (!st) return 0; // Lock ntfsLock(vd); // Zero out the stat buffer memset(st, 0, sizeof(struct stat)); // Is this entry a directory if (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY) { st->st_mode = S_IFDIR | (0777 & ~vd->dmask); st->st_nlink = 1; // Open the directories index allocation table attribute na = ntfs_attr_open(ni, AT_INDEX_ALLOCATION, NTFS_INDEX_I30, 4); if (na) { st->st_size = na->data_size; st->st_blocks = na->allocated_size >> 9; ntfs_attr_close(na); } // Else it must be a file } else {
status_t fs_read_attrib_stat(fs_volume *_vol, fs_vnode *_node, void *_cookie, struct stat *stat) { nspace *ns = (nspace *)_vol->private_volume; vnode *node = (vnode *)_node->private_node; attrcookie *cookie = (attrcookie *)_cookie; ntfs_inode *ni = NULL; ntfs_attr *na = NULL; status_t result = B_NO_ERROR; LOCK_VOL(ns); ni = ntfs_inode_open(ns->ntvol, node->vnid); if (ni == NULL) { result = errno; goto exit; } na = ntfs_attr_open(ni, AT_DATA, cookie->uname, cookie->uname_len); if (na == NULL) { result = errno; goto exit; } stat->st_type = cookie->type; stat->st_size = na ? na->data_size - sizeof(uint32) : 0; exit: if (na != NULL) ntfs_attr_close(na); if (ni != NULL) ntfs_inode_close(ni); UNLOCK_VOL(ns); return B_NO_ERROR; }
/** * ntfs_attrlist_entry_add - add an attribute list attribute entry * @ni: opened ntfs inode, which contains that attribute * @attr: attribute record to add to attribute list * * Return 0 on success and -1 on error with errno set to the error code. The * following error codes are defined: * EINVAL - Invalid arguments passed to function. * ENOMEM - Not enough memory to allocate necessary buffers. * EIO - I/O error occurred or damaged filesystem. * EEXIST - Such attribute already present in attribute list. */ int ntfs_attrlist_entry_add(ntfs_inode *ni, ATTR_RECORD *attr) { ATTR_LIST_ENTRY *ale; leMFT_REF mref; ntfs_attr *na = NULL; ntfs_attr_search_ctx *ctx; u8 *new_al; int entry_len, entry_offset, err; ntfs_log_trace("Entering for inode 0x%llx, attr 0x%x.\n", (long long) ni->mft_no, (unsigned) le32_to_cpu(attr->type)); if (!ni || !attr) { ntfs_log_trace("Invalid arguments.\n"); errno = EINVAL; return -1; } mref = MK_LE_MREF(ni->mft_no, le16_to_cpu(ni->mrec->sequence_number)); if (ni->nr_extents == -1) ni = ni->u.base_ni; if (!NInoAttrList(ni)) { ntfs_log_trace("Attribute list isn't present.\n"); errno = ENOENT; return -1; } /* Determine size and allocate memory for new attribute list. */ entry_len = (sizeof(ATTR_LIST_ENTRY) + sizeof(ntfschar) * attr->name_length + 7) & ~7; new_al = malloc(ni->attr_list_size + entry_len); if (!new_al) { ntfs_log_trace("Not enough memory.\n"); err = ENOMEM; return -1; } /* Find place for the new entry. */ ctx = ntfs_attr_get_search_ctx(ni, NULL); if (!ctx) { err = errno; ntfs_log_trace("Failed to obtain attribute search context.\n"); goto err_out; } if (!ntfs_attr_lookup(attr->type, (attr->name_length) ? (ntfschar*) ((u8*)attr + le16_to_cpu(attr->name_offset)) : AT_UNNAMED, attr->name_length, CASE_SENSITIVE, (attr->non_resident) ? sle64_to_cpu(attr->u.nonres.lowest_vcn) : 0, (attr->non_resident) ? NULL : ((u8*)attr + le16_to_cpu(attr->u.res.value_offset)), (attr->non_resident) ? 0 : le32_to_cpu(attr->u.res.value_length), ctx)) { /* Found some extent, check it to be before new extent. */ if (ctx->al_entry->lowest_vcn == attr->u.nonres.lowest_vcn) { err = EEXIST; ntfs_log_trace("Such attribute already present in the " "attribute list.\n"); ntfs_attr_put_search_ctx(ctx); goto err_out; } /* Add new entry after this extent. */ ale = (ATTR_LIST_ENTRY*)((u8*)ctx->al_entry + le16_to_cpu(ctx->al_entry->length)); } else { /* Check for real errors. */ if (errno != ENOENT) { err = errno; ntfs_log_trace("Attribute lookup failed.\n"); ntfs_attr_put_search_ctx(ctx); goto err_out; } /* No previous extents found. */ ale = ctx->al_entry; } /* Don't need it anymore, @ctx->al_entry points to @ni->attr_list. */ ntfs_attr_put_search_ctx(ctx); /* Determine new entry offset. */ entry_offset = ((u8 *)ale - ni->attr_list); /* Set pointer to new entry. */ ale = (ATTR_LIST_ENTRY *)(new_al + entry_offset); /* Form new entry. */ ale->type = attr->type; ale->length = cpu_to_le16(entry_len); ale->name_length = attr->name_length; ale->name_offset = offsetof(ATTR_LIST_ENTRY, name); if (attr->non_resident) ale->lowest_vcn = attr->u.nonres.lowest_vcn; else ale->lowest_vcn = 0; ale->mft_reference = mref; ale->instance = attr->instance; NTFS_ON_DEBUG(memset(ale->name, 0, ((u8*)((u8*)ale + entry_len)) - ((u8*)ale->name))); /* Shut up, valgrind. */ memcpy(ale->name, (u8 *)attr + le16_to_cpu(attr->name_offset), attr->name_length * sizeof(ntfschar)); /* Resize $ATTRIBUTE_LIST to new length. */ na = ntfs_attr_open(ni, AT_ATTRIBUTE_LIST, AT_UNNAMED, 0); if (!na) { err = errno; ntfs_log_trace("Failed to open $ATTRIBUTE_LIST attribute.\n"); goto err_out; } if (ntfs_attr_truncate(na, ni->attr_list_size + entry_len)) { err = errno; ntfs_log_trace("$ATTRIBUTE_LIST resize failed.\n"); goto err_out; } /* Copy entries from old attribute list to new. */ memcpy(new_al, ni->attr_list, entry_offset); memcpy(new_al + entry_offset + entry_len, ni->attr_list + entry_offset, ni->attr_list_size - entry_offset); /* Set new runlist. */ free(ni->attr_list); ni->attr_list = new_al; ni->attr_list_size = ni->attr_list_size + entry_len; NInoAttrListSetDirty(ni); /* Done! */ ntfs_attr_close(na); return 0; err_out: if (na) ntfs_attr_close(na); free(new_al); errno = err; return -1; }
/** * 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; }
status_t fs_open_attrib(fs_volume *_vol, fs_vnode *_node, const char *name, int openMode, void **_cookie) { nspace *ns = (nspace*)_vol->private_volume; vnode *node = (vnode*)_node->private_node; attrcookie *cookie = NULL; ntfschar *uname = NULL; int ulen; ntfs_inode *ni = NULL; ntfs_attr *na = NULL; status_t result = B_NO_ERROR; TRACE("%s - ENTER\n", __FUNCTION__); LOCK_VOL(ns); if (node == NULL) { result = EINVAL; goto exit; } ni = ntfs_inode_open(ns->ntvol, node->vnid); if (ni == NULL) { result = errno; goto exit; } // UXA demangling TODO // check for EA first... TODO: WRITEME // check for a named stream if (true) { uname = ntfs_calloc(MAX_PATH); ulen = ntfs_mbstoucs(name, &uname); if (ulen < 0) { result = EILSEQ; goto exit; } na = ntfs_attr_open(ni, AT_DATA, uname, ulen); if (na) { if (openMode & O_TRUNC) { if (ntfs_attr_truncate(na, 0)) result = errno; } } else { result = ENOENT; goto exit; } } cookie = (attrcookie*)ntfs_calloc(sizeof(attrcookie)); if (cookie != NULL) { cookie->omode = openMode; *_cookie = (void*)cookie; cookie->inode = ni; cookie->stream = na; ni = NULL; na = NULL; } else result = ENOMEM; exit: if (uname) free(uname); if (na) ntfs_attr_close(na); if (ni) ntfs_inode_close(ni); TRACE("%s - EXIT, result is %s\n", __FUNCTION__, strerror(result)); UNLOCK_VOL(ns); return result; }
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); }
/** * ntfs_inode_add_attrlist - add attribute list to inode and fill it * @ni: opened ntfs inode to which add attribute list * * 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. * EEXIST - Attribute list already exist. * EIO - Input/Ouput error occurred. * ENOMEM - Not enough memory to perform add. */ int ntfs_inode_add_attrlist(ntfs_inode *ni) { int err; ntfs_attr_search_ctx *ctx; u8 *al = NULL, *aln; int al_len = 0; ATTR_LIST_ENTRY *ale = NULL; ntfs_attr *na; if (!ni) { errno = EINVAL; ntfs_log_perror("%s", __FUNCTION__); return -1; } ntfs_log_trace("inode %llu\n", (unsigned long long) ni->mft_no); if (NInoAttrList(ni) || ni->nr_extents) { errno = EEXIST; ntfs_log_perror("Inode already has attribute list"); return -1; } /* Form attribute list. */ ctx = ntfs_attr_get_search_ctx(ni, NULL); if (!ctx) { err = errno; goto err_out; } /* Walk through all attributes. */ while (!ntfs_attr_lookup(AT_UNUSED, NULL, 0, 0, 0, NULL, 0, ctx)) { int ale_size; if (ctx->attr->type == AT_ATTRIBUTE_LIST) { err = EIO; ntfs_log_perror("Attribute list already present"); goto put_err_out; } ale_size = (sizeof(ATTR_LIST_ENTRY) + sizeof(ntfschar) * ctx->attr->name_length + 7) & ~7; al_len += ale_size; aln = realloc(al, al_len); if (!aln) { err = errno; ntfs_log_perror("Failed to realloc %d bytes", al_len); goto put_err_out; } ale = (ATTR_LIST_ENTRY *)(aln + ((u8 *)ale - al)); al = aln; memset(ale, 0, ale_size); /* Add attribute to attribute list. */ ale->type = ctx->attr->type; ale->length = cpu_to_le16((sizeof(ATTR_LIST_ENTRY) + sizeof(ntfschar) * ctx->attr->name_length + 7) & ~7); ale->name_length = ctx->attr->name_length; ale->name_offset = (u8 *)ale->name - (u8 *)ale; if (ctx->attr->non_resident) ale->lowest_vcn = ctx->attr->lowest_vcn; else ale->lowest_vcn = 0; ale->mft_reference = MK_LE_MREF(ni->mft_no, le16_to_cpu(ni->mrec->sequence_number)); ale->instance = ctx->attr->instance; memcpy(ale->name, (u8 *)ctx->attr + le16_to_cpu(ctx->attr->name_offset), ctx->attr->name_length * sizeof(ntfschar)); ale = (ATTR_LIST_ENTRY *)(al + al_len); } /* Check for real error occurred. */ if (errno != ENOENT) { err = errno; ntfs_log_perror("%s: Attribute lookup failed, inode %lld", __FUNCTION__, (long long)ni->mft_no); goto put_err_out; } /* Set in-memory attribute list. */ ni->attr_list = al; ni->attr_list_size = al_len; NInoSetAttrList(ni); NInoAttrListSetDirty(ni); /* Free space if there is not enough it for $ATTRIBUTE_LIST. */ if (le32_to_cpu(ni->mrec->bytes_allocated) - le32_to_cpu(ni->mrec->bytes_in_use) < offsetof(ATTR_RECORD, resident_end)) { if (ntfs_inode_free_space(ni, offsetof(ATTR_RECORD, resident_end))) { /* Failed to free space. */ err = errno; ntfs_log_perror("Failed to free space for attrlist"); goto rollback; } } /* Add $ATTRIBUTE_LIST to mft record. */ if (ntfs_resident_attr_record_add(ni, AT_ATTRIBUTE_LIST, NULL, 0, NULL, 0, 0) < 0) { err = errno; ntfs_log_perror("Couldn't add $ATTRIBUTE_LIST to MFT"); goto rollback; } /* Resize it. */ na = ntfs_attr_open(ni, AT_ATTRIBUTE_LIST, AT_UNNAMED, 0); if (!na) { err = errno; ntfs_log_perror("Failed to open just added $ATTRIBUTE_LIST"); goto remove_attrlist_record; } if (ntfs_attr_truncate(na, al_len)) { err = errno; ntfs_log_perror("Failed to resize just added $ATTRIBUTE_LIST"); ntfs_attr_close(na); goto remove_attrlist_record;; } ntfs_attr_put_search_ctx(ctx); ntfs_attr_close(na); return 0; remove_attrlist_record: /* Prevent ntfs_attr_recorm_rm from freeing attribute list. */ ni->attr_list = NULL; NInoClearAttrList(ni); /* Remove $ATTRIBUTE_LIST record. */ ntfs_attr_reinit_search_ctx(ctx); if (!ntfs_attr_lookup(AT_ATTRIBUTE_LIST, NULL, 0, CASE_SENSITIVE, 0, NULL, 0, ctx)) { if (ntfs_attr_record_rm(ctx)) ntfs_log_perror("Rollback failed to remove attrlist"); } else ntfs_log_perror("Rollback failed to find attrlist"); /* Setup back in-memory runlist. */ ni->attr_list = al; ni->attr_list_size = al_len; NInoSetAttrList(ni); rollback: /* * Scan attribute list for attributes that placed not in the base MFT * record and move them to it. */ ntfs_attr_reinit_search_ctx(ctx); ale = (ATTR_LIST_ENTRY*)al; while ((u8*)ale < al + al_len) { if (MREF_LE(ale->mft_reference) != ni->mft_no) { if (!ntfs_attr_lookup(ale->type, ale->name, ale->name_length, CASE_SENSITIVE, sle64_to_cpu(ale->lowest_vcn), NULL, 0, ctx)) { if (ntfs_attr_record_move_to(ctx, ni)) ntfs_log_perror("Rollback failed to " "move attribute"); } else ntfs_log_perror("Rollback failed to find attr"); ntfs_attr_reinit_search_ctx(ctx); } ale = (ATTR_LIST_ENTRY*)((u8*)ale + le16_to_cpu(ale->length)); } /* Remove in-memory attribute list. */ ni->attr_list = NULL; ni->attr_list_size = 0; NInoClearAttrList(ni); NInoAttrListClearDirty(ni); put_err_out: ntfs_attr_put_search_ctx(ctx); err_out: free(al); errno = err; return -1; }
/** * ntfs_cat_decrypt - Decrypt the contents of an encrypted file to stdout. * @inode: An encrypted file's inode structure, as obtained by * ntfs_inode_open(). * @fek: A file encryption key. As obtained by ntfs_inode_fek_get(). */ static int ntfs_cat_decrypt(ntfs_inode *inode, ntfs_fek *fek) { int bufsize = 512; unsigned char *buffer; ntfs_attr *attr; s64 bytes_read, written, offset, total; s64 old_data_size, old_initialized_size; int i; buffer = malloc(bufsize); if (!buffer) return 1; attr = ntfs_attr_open(inode, AT_DATA, NULL, 0); if (!attr) { ntfs_log_error("Cannot cat a directory.\n"); free(buffer); return 1; } total = attr->data_size; // hack: make sure attr will not be commited to disk if you use this. // clear the encrypted bit, otherwise the library won't allow reading. NAttrClearEncrypted(attr); // extend the size, we may need to read past the end of the stream. old_data_size = attr->data_size; old_initialized_size = attr->initialized_size; attr->data_size = attr->initialized_size = attr->allocated_size; offset = 0; while (total > 0) { bytes_read = ntfs_attr_pread(attr, offset, 512, buffer); if (bytes_read == -1) { ntfs_log_perror("ERROR: Couldn't read file"); break; } if (!bytes_read) break; if ((i = ntfs_fek_decrypt_sector(fek, buffer, offset)) < bytes_read) { ntfs_log_perror("ERROR: Couldn't decrypt all data!"); ntfs_log_error("%u/%lld/%lld/%lld\n", i, (long long)bytes_read, (long long)offset, (long long)total); break; } if (bytes_read > total) bytes_read = total; written = fwrite(buffer, 1, bytes_read, stdout); if (written != bytes_read) { ntfs_log_perror("ERROR: Couldn't output all data!"); break; } offset += bytes_read; total -= bytes_read; } attr->data_size = old_data_size; attr->initialized_size = old_initialized_size; NAttrSetEncrypted(attr); ntfs_attr_close(attr); free(buffer); return 0; }
bool ntfsSetVolumeName (const char *name, const char *volumeName) { ntfs_vd *vd = NULL; ntfs_attr *na = NULL; ntfschar *ulabel = NULL; int ulabel_len; // Sanity check if (!name) { errno = EINVAL; return false; } // Get the devices volume descriptor vd = ntfsGetVolume(name); if (!vd) { errno = ENODEV; return false; } // Lock ntfsLock(vd); // Convert the new volume name to unicode ulabel_len = ntfsLocalToUnicode(volumeName, &ulabel) * sizeof(ntfschar); if (ulabel_len < 0) { ntfsUnlock(vd); errno = EINVAL; return false; } // Check if the volume name attribute exists na = ntfs_attr_open(vd->vol->vol_ni, AT_VOLUME_NAME, NULL, 0); if (na) { // It does, resize it to match the length of the new volume name if (ntfs_attr_truncate(na, ulabel_len)) { ntfs_free(ulabel); ntfsUnlock(vd); return false; } // Write the new volume name if (ntfs_attr_pwrite(na, 0, ulabel_len, ulabel) != ulabel_len) { ntfs_free(ulabel); ntfsUnlock(vd); return false; } } else { // It doesn't, create it now if (ntfs_attr_add(vd->vol->vol_ni, AT_VOLUME_NAME, NULL, 0, (u8*)ulabel, ulabel_len)) { ntfs_free(ulabel); ntfsUnlock(vd); return false; } } // Reset the volumes name cache (as it has now been changed) vd->name[0] = '\0'; // Close the volume name attribute if (na) ntfs_attr_close(na); // Sync the volume node if (ntfs_inode_sync(vd->vol->vol_ni)) { ntfs_free(ulabel); ntfsUnlock(vd); return false; } // Clean up ntfs_free(ulabel); // Unlock ntfsUnlock(vd); return true; }
status_t fs_read_attrib(fs_volume *_vol, fs_vnode *_node, void *_cookie, off_t pos, void *buffer, size_t *len) { nspace *ns = (nspace *)_vol->private_volume; vnode *node = (vnode *)_node->private_node; attrcookie *cookie = (attrcookie *)_cookie; ntfs_inode *ni = NULL; ntfs_attr *na = NULL; size_t size = *len; int total = 0; status_t result = B_NO_ERROR; if (pos < 0) { *len = 0; return EINVAL; } LOCK_VOL(ns); TRACE("%s - ENTER vnid: %d\n", __FUNCTION__, node->vnid); ni = ntfs_inode_open(ns->ntvol, node->vnid); if (ni == NULL) { result = errno; goto exit; } na = ntfs_attr_open(ni, AT_DATA, cookie->uname, cookie->uname_len); if (na == NULL) { result = errno; goto exit; } pos += sizeof(uint32); // it is a named stream if (na != NULL) { if (pos + size > na->data_size) size = na->data_size - pos; while (size) { off_t bytesRead = ntfs_attr_pread(na, pos, size, buffer); if (bytesRead < (s64)size) { ERROR("ntfs_attr_pread returned less bytes than " "requested.\n"); } if (bytesRead <= 0) { *len = 0; result = EINVAL; goto exit; } size -= bytesRead; pos += bytesRead; total += bytesRead; } *len = total; } else { *len = 0; result = ENOENT; // TODO } exit: if (na != NULL) ntfs_attr_close(na); if (ni != NULL) ntfs_inode_close(ni); TRACE("%s - EXIT, result is %s\n", __FUNCTION__, strerror(result)); UNLOCK_VOL(ns); return result; }
status_t fs_write_attrib(fs_volume *_vol, fs_vnode *_node, void *_cookie, off_t pos, const void *buffer, size_t *_length) { nspace *ns = (nspace *)_vol->private_volume; vnode *node = (vnode *)_node->private_node; attrcookie *cookie = (attrcookie *)_cookie; ntfs_inode *ni = NULL; ntfs_attr *na = NULL; size_t size = *_length; char *attr_name = NULL; char *real_name = NULL; int total = 0; status_t result = B_NO_ERROR; TRACE("%s - ENTER vnode: %d\n", __FUNCTION__, node->vnid); if (ns->flags & B_FS_IS_READONLY) { return B_READ_ONLY_DEVICE; } if (pos < 0) { *_length = 0; return EINVAL; } LOCK_VOL(ns); ni = ntfs_inode_open(ns->ntvol, node->vnid); if (ni == NULL) { result = errno; goto exit; } na = ntfs_attr_open(ni, AT_DATA, cookie->uname, cookie->uname_len); if (na == NULL) { result = errno; goto exit; } pos += sizeof(uint32); // it is a named stream if (na != NULL) { if (cookie->omode & O_APPEND) pos = na->data_size; if (pos + size > na->data_size) { ntfs_mark_free_space_outdated(ns); if (ntfs_attr_truncate(na, pos + size)) size = na->data_size - pos; else notify_stat_changed(ns->id, MREF(ni->mft_no), B_STAT_SIZE); } while (size) { off_t bytesWritten = ntfs_attr_pwrite(na, pos, size, buffer); if (bytesWritten < (s64)size) ERROR("%s - ntfs_attr_pwrite returned less bytes than " "requested.\n", __FUNCTION__); if (bytesWritten <= 0) { ERROR("%s - ntfs_attr_pwrite()<=0\n", __FUNCTION__); *_length = 0; result = EINVAL; goto exit; } size -= bytesWritten; pos += bytesWritten; total += bytesWritten; } *_length = total; } else { *_length = 0; result = EINVAL; goto exit; } if (ntfs_ucstombs(na->name, na->name_len, &attr_name, 0) >= 0) { if (attr_name != NULL) { if(strncmp(attr_name, kHaikuAttrPrefix, strlen(kHaikuAttrPrefix)) !=0 ) goto exit; real_name = attr_name + strlen(kHaikuAttrPrefix); notify_attribute_changed(ns->id, MREF(ni->mft_no), real_name, B_ATTR_CHANGED); free(attr_name); } } exit: if (na != NULL) ntfs_attr_close(na); if (ni != NULL) ntfs_inode_close(ni); TRACE("%s - EXIT, result is %s\n", __FUNCTION__, strerror(result)); UNLOCK_VOL(ns); return result; }
/** * ntfs_feed_encrypt - Encrypt the contents of stdin to an encrypted file * @inode: An encrypted file's inode structure, as obtained by * ntfs_inode_open(). * @fek: A file encryption key. As obtained by ntfs_inode_fek_get(). */ static int ntfs_feed_encrypt(ntfs_inode *inode, ntfs_fek *fek) { const int bufsize = 512; unsigned char *buffer; ntfs_attr *attr; s64 bytes_read, written, offset, total; unsigned char *b; long val; int count; int i; buffer = (unsigned char*)malloc(bufsize); if (!buffer) return 1; attr = ntfs_attr_open(inode, AT_DATA, NULL, 0); if (!attr) { ntfs_log_error("Cannot feed into a directory.\n"); goto rejected; } total = 0; if (!(attr->data_flags & ATTR_IS_ENCRYPTED)) { ntfs_log_error("The data stream was not encrypted\n"); goto rejected; } inode->vol->efs_raw = TRUE; if (ntfs_attr_truncate(attr, 0)) { ntfs_log_error("Failed to truncate the data stream\n"); goto rejected; } offset = 0; do { bytes_read = fread(buffer, 1, bufsize, stdin); if (bytes_read <= 0) { if (bytes_read < 0) ntfs_log_perror("ERROR: Couldn't read data"); } else { if (bytes_read < bufsize) { /* Fill with random data */ srandom((unsigned int)(sle64_to_cpu( inode->last_data_change_time) /100000000)); count = bufsize - bytes_read; b = &buffer[bytes_read]; do { val = random(); switch (count) { default : *b++ = val; val >>= 8; case 3 : *b++ = val; val >>= 8; case 2 : *b++ = val; val >>= 8; case 1 : *b++ = val; val >>= 8; } count -= 4; } while (count > 0); } if ((i = ntfs_fek_encrypt_sector(fek, buffer, offset)) < bufsize) { ntfs_log_perror("ERROR: Couldn't encrypt all data!"); ntfs_log_error("%u/%lld/%lld/%lld\n", i, (long long)bytes_read, (long long)offset, (long long)total); break; } written = ntfs_attr_pwrite(attr, offset, bufsize, buffer); if (written != bufsize) { ntfs_log_perror("ERROR: Couldn't output all data!"); break; } offset += bufsize; total += bytes_read; } } while (bytes_read == bufsize); ntfs_attr_truncate(attr, total); inode->last_data_change_time = ntfs_current_time(); NAttrSetEncrypted(attr); ntfs_attr_close(attr); free(buffer); return 0; rejected : free(buffer); return (-1); }
/** * ntfs_attrlist_entry_rm - remove an attribute list attribute entry * @ctx: attribute search context describing the attribute list entry * * Remove the attribute list entry @ctx->al_entry from the attribute list. * * Return 0 on success and -1 on error with errno set to the error code. */ int ntfs_attrlist_entry_rm(ntfs_attr_search_ctx *ctx) { u8 *new_al; int new_al_len; ntfs_inode *base_ni; ntfs_attr *na; ATTR_LIST_ENTRY *ale; int err; if (!ctx || !ctx->ntfs_ino || !ctx->al_entry) { ntfs_log_trace("Invalid arguments.\n"); errno = EINVAL; return -1; } if (ctx->base_ntfs_ino) base_ni = ctx->base_ntfs_ino; else base_ni = ctx->ntfs_ino; ale = ctx->al_entry; ntfs_log_trace("Entering for inode 0x%llx, attr 0x%x, lowest_vcn %lld." "\n", (long long) ctx->ntfs_ino->mft_no, (unsigned) le32_to_cpu(ctx->al_entry->type), (long long) sle64_to_cpu(ctx->al_entry->lowest_vcn)); if (!NInoAttrList(base_ni)) { ntfs_log_trace("Attribute list isn't present.\n"); errno = ENOENT; return -1; } /* Allocate memory for new attribute list. */ new_al_len = base_ni->attr_list_size - le16_to_cpu(ale->length); new_al = malloc(new_al_len); if (!new_al) { ntfs_log_trace("Not enough memory.\n"); errno = ENOMEM; return -1; } /* Reisze $ATTRIBUTE_LIST to new length. */ na = ntfs_attr_open(base_ni, AT_ATTRIBUTE_LIST, AT_UNNAMED, 0); if (!na) { err = errno; ntfs_log_trace("Failed to open $ATTRIBUTE_LIST attribute.\n"); goto err_out; } if (ntfs_attr_truncate(na, new_al_len)) { err = errno; ntfs_log_trace("$ATTRIBUTE_LIST resize failed.\n"); goto err_out; } /* Copy entries from old attribute list to new. */ memcpy(new_al, base_ni->attr_list, (u8*)ale - base_ni->attr_list); memcpy(new_al + ((u8*)ale - base_ni->attr_list), (u8*)ale + le16_to_cpu( ale->length), new_al_len - ((u8*)ale - base_ni->attr_list)); /* Set new runlist. */ free(base_ni->attr_list); base_ni->attr_list = new_al; base_ni->attr_list_size = new_al_len; NInoAttrListSetDirty(base_ni); /* Done! */ ntfs_attr_close(na); return 0; err_out: if (na) ntfs_attr_close(na); free(new_al); errno = err; return -1; }
int ntfs_file_to_sectors(struct _reent *r, const char *path, uint32_t *sec_out, uint32_t *size_out, int max, int phys) { ntfs_file_state fileStruct; ntfs_file_state* file = &fileStruct; uint32_t s_count = 0; //size_t len; off64_t len; // Get the volume descriptor for this path file->vd = ntfsGetVolume(path); if (!file->vd) { r->_errno = ENODEV; return -1; } // Lock ntfsLock(file->vd); // Try and find the file and (if found) ensure that it is not a directory file->ni = ntfsOpenEntry(file->vd, path); if (file->ni && (file->ni->mrec->flags & MFT_RECORD_IS_DIRECTORY)) { ntfsCloseEntry(file->vd, file->ni); ntfsUnlock(file->vd); r->_errno = EISDIR; return -1; } // Sanity check, the file should be open by now if (!file->ni) { ntfsUnlock(file->vd); r->_errno = ENOENT; return -1; } // Open the files data attribute file->data_na = ntfs_attr_open(file->ni, AT_DATA, AT_UNNAMED, 0); if(!file->data_na) { ntfsCloseEntry(file->vd, file->ni); ntfsUnlock(file->vd); return -1; } // Determine if this files data is compressed and/or encrypted file->compressed = NAttrCompressed(file->data_na) || (file->ni->flags & FILE_ATTR_COMPRESSED); file->encrypted = NAttrEncrypted(file->data_na) || (file->ni->flags & FILE_ATTR_ENCRYPTED); // We cannot read/write encrypted files if (file->encrypted) { ntfs_attr_close(file->data_na); ntfsCloseEntry(file->vd, file->ni); ntfsUnlock(file->vd); r->_errno = EACCES; return -1; } // Set the files current position and length file->pos = 0; len = file->len = file->data_na->data_size; struct ntfs_device *dev = file->vd->dev; gekko_fd *fd = DEV_FD(dev); while (len && s_count < max) { //size_t ret = ntfs_attr_to_sectors(file->data_na, file->pos, len, sec_out, size_out, max, &s_count, (u32) fd->sectorSize); off64_t ret = ntfs_attr_to_sectors(file->data_na, file->pos, len, sec_out, size_out, max, &s_count, (u32) fd->sectorSize); if (ret <= 0 || ret > len) { ntfsUnlock(file->vd); r->_errno = errno; return -1; } len -= ret; file->pos += ret; } if (phys) { uint32_t i; for (i = 0; i < s_count; i++) { sec_out[i] += fd->startSector; } } ntfs_attr_close(file->data_na); ntfsCloseEntry(file->vd, file->ni); // Unlock ntfsUnlock(file->vd); return s_count; }
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); }
static int ntfs_copy(disk_t *disk_car, const partition_t *partition, dir_data_t *dir_data, const file_info_t *file) { const unsigned long int first_inode=file->st_ino; ntfs_inode *inode; struct ntfs_dir_struct *ls=(struct ntfs_dir_struct*)dir_data->private_dir_data; inode = ntfs_inode_open (ls->vol, first_inode); if (!inode) { log_error("ntfs_copy: ntfs_inode_open failed for %s\n", dir_data->current_directory); return -1; } { char *buffer; char *new_file; ntfs_attr *attr=NULL; FILE *f_out; char *stream_name; s64 offset; u32 block_size; buffer = (char *)MALLOC(bufsize); if (!buffer) { ntfs_inode_close(inode); return -2; } stream_name=strrchr(dir_data->current_directory, ':'); if(stream_name) stream_name++; if(stream_name != NULL) { ntfschar *stream_name_ucs=NULL; #ifdef NTFS_MBSTOUCS_HAVE_TWO_ARGUMENTS const int len=ntfs_mbstoucs(stream_name, &stream_name_ucs); #else const int len=ntfs_mbstoucs(stream_name, &stream_name_ucs, 0); #endif if(len < 0) log_error("ntfs_mbstoucs failed\n"); else attr = ntfs_attr_open(inode, AT_DATA, stream_name_ucs, len); } else attr = ntfs_attr_open(inode, AT_DATA, NULL, 0); if (!attr) { log_error("Cannot find attribute type 0x%lx.\n", (long) AT_DATA); free(buffer); ntfs_inode_close(inode); return -3; } if ((inode->mft_no < 2) && (attr->type == AT_DATA)) block_size = ls->vol->mft_record_size; else if (attr->type == AT_INDEX_ALLOCATION) block_size = index_get_size(inode); else block_size = 0; #if defined(__CYGWIN__) || defined(__MINGW32__) if(stream_name) { /* fopen() create normal files instead of ADS with ':' replaced by an UTF char * replace ':' by '_' instead */ stream_name--; *stream_name='_'; f_out=fopen_local(&new_file, dir_data->local_dir, dir_data->current_directory); } else f_out=fopen_local(&new_file, dir_data->local_dir, dir_data->current_directory); #else f_out=fopen_local(&new_file, dir_data->local_dir, dir_data->current_directory); #endif if(!f_out) { log_critical("Can't create file %s: %s\n",new_file, strerror(errno)); free(new_file); ntfs_attr_close(attr); free(buffer); ntfs_inode_close(inode); return -4; } offset = 0; for (;;) { s64 bytes_read, written; if (block_size > 0) { // These types have fixup bytes_read = ntfs_attr_mst_pread(attr, offset, 1, block_size, buffer); bytes_read *= block_size; } else { bytes_read = ntfs_attr_pread(attr, offset, bufsize, buffer); } //ntfs_log_info("read %lld bytes\n", bytes_read); if (bytes_read < 0) { log_error("ERROR: Couldn't read file"); break; } if (!bytes_read) break; written = fwrite(buffer, 1, bytes_read, f_out); if (written != bytes_read) { log_error("ERROR: Couldn't output all data!"); break; } offset += bytes_read; } fclose(f_out); set_date(new_file, file->td_atime, file->td_mtime); free(new_file); ntfs_attr_close(attr); free(buffer); } /* Finished with the inode; release it. */ ntfs_inode_close(inode); return 0; }
status_t fs_open_attrib(fs_volume *_vol, fs_vnode *_node, const char *name, int openMode, void **_cookie) { nspace *ns = (nspace*)_vol->private_volume; vnode *node = (vnode*)_node->private_node; attrcookie *cookie = NULL; ntfschar *uname = NULL; int ulen; ntfs_inode *ni = NULL; ntfs_attr *na = NULL; status_t result = B_NO_ERROR; uint32 type = B_XATTR_TYPE; TRACE("%s - ENTER - name: [%s] vnid: %d\n", __FUNCTION__, name, node->vnid); LOCK_VOL(ns); if (node == NULL) { result = EINVAL; goto exit; } ni = ntfs_inode_open(ns->ntvol, node->vnid); if (ni == NULL) { result = errno; goto exit; } // UXA demangling TODO // check for EA first... TODO: WRITEME // check for a named stream if (true) { char ntfs_attr_name[MAX_PATH] = {0}; strcat(ntfs_attr_name, kHaikuAttrPrefix); strcat(ntfs_attr_name, name); uname = ntfs_calloc(MAX_PATH); ulen = ntfs_mbstoucs(ntfs_attr_name, &uname); if (ulen < 0) { result = EILSEQ; goto exit; } na = ntfs_attr_open(ni, AT_DATA, uname, ulen); if (na != NULL) { if (openMode & O_TRUNC) { if (ns->flags & B_FS_IS_READONLY) { result = B_READ_ONLY_DEVICE; goto exit; } else { if (ntfs_attr_truncate(na, sizeof(uint32))) { result = errno; goto exit; } } } if (ntfs_attr_pread(na, 0, sizeof(uint32), &type) != sizeof(uint32)) { result = errno; goto exit; } } else { result = ENOENT; goto exit; } } cookie = (attrcookie*)ntfs_calloc(sizeof(attrcookie)); if (cookie != NULL) { cookie->omode = openMode; cookie->vnid = node->vnid; cookie->uname = uname; cookie->uname_len = ulen; cookie->type = type; *_cookie = (void*)cookie; uname = NULL; } else result = ENOMEM; exit: if (uname != NULL) free(uname); if (na != NULL) ntfs_attr_close(na); if (ni != NULL) ntfs_inode_close(ni); TRACE("%s - EXIT, result is %s\n", __FUNCTION__, strerror(result)); UNLOCK_VOL(ns); return result; }
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_open_r(struct _reent *r, void *fileStruct, const char *path, int flags, int mode) { ntfs_log_trace("fileStruct %p, path %s, flags %i, mode %i\n", (void *) fileStruct, path, flags, mode); ntfs_file_state* file = STATE(fileStruct); // Get the volume descriptor for this path file->vd = ntfsGetVolume(path); if (!file->vd) { r->_errno = ENODEV; return -1; } // Lock ntfsLock(file->vd); // Determine which mode the file is opened for file->flags = flags; if ((flags & 0x03) == O_RDONLY) { file->read = true; file->write = false; file->append = false; } else if ((flags & 0x03) == O_WRONLY) { file->read = false; file->write = true; file->append = (flags & O_APPEND); } else if ((flags & 0x03) == O_RDWR) { file->read = true; file->write = true; file->append = (flags & O_APPEND); } else { r->_errno = EACCES; ntfsUnlock(file->vd); return -1; } // Try and find the file and (if found) ensure that it is not a directory file->ni = ntfsOpenEntry(file->vd, path); if (file->ni && (file->ni->mrec->flags & MFT_RECORD_IS_DIRECTORY)) { ntfsCloseEntry(file->vd, file->ni); ntfsUnlock(file->vd); r->_errno = EISDIR; return -1; } // Are we creating this file? if ((flags & O_CREAT) && !file->ni) { // Create the file file->ni = ntfsCreate(file->vd, path, S_IFREG, NULL); if (!file->ni) { ntfsUnlock(file->vd); return -1; } } // Sanity check, the file should be open by now if (!file->ni) { ntfsUnlock(file->vd); r->_errno = ENOENT; return -1; } // Open the files data attribute file->data_na = ntfs_attr_open(file->ni, AT_DATA, AT_UNNAMED, 0); if (!file->data_na) { ntfsCloseEntry(file->vd, file->ni); ntfsUnlock(file->vd); return -1; } // Determine if this files data is compressed and/or encrypted file->compressed = NAttrCompressed(file->data_na) || (file->ni->flags & FILE_ATTR_COMPRESSED); file->encrypted = NAttrEncrypted(file->data_na) || (file->ni->flags & FILE_ATTR_ENCRYPTED); // We cannot read/write encrypted files if (file->encrypted) { ntfs_attr_close(file->data_na); ntfsCloseEntry(file->vd, file->ni); ntfsUnlock(file->vd); r->_errno = EACCES; return -1; } // Make sure we aren't trying to write to a read-only file if ((file->ni->flags & FILE_ATTR_READONLY) && file->write) { ntfs_attr_close(file->data_na); ntfsCloseEntry(file->vd, file->ni); ntfsUnlock(file->vd); r->_errno = EROFS; return -1; } // Truncate the file if requested if ((flags & O_TRUNC) && file->write) { if (ntfs_attr_truncate(file->data_na, 0)) { ntfs_attr_close(file->data_na); ntfsCloseEntry(file->vd, file->ni); ntfsUnlock(file->vd); r->_errno = errno; return -1; } } // Set the files current position and length file->pos = 0; file->len = file->data_na->data_size; ntfs_log_trace("file->len %llu\n", file->len); // Update file times ntfsUpdateTimes(file->vd, file->ni, NTFS_UPDATE_ATIME); // Insert the file into the double-linked FILO list of open files if (file->vd->firstOpenFile) { file->nextOpenFile = file->vd->firstOpenFile; file->vd->firstOpenFile->prevOpenFile = file; } else { file->nextOpenFile = NULL; } file->prevOpenFile = NULL; file->vd->firstOpenFile = file; file->vd->openFileCount++; file->is_ntfs = 1; // Unlock ntfsUnlock(file->vd); return (int)(intptr_t)fileStruct; //return (int)(s64) fileStruct; }
static int ntfsrec_emit_file(struct ntfsrec_copy *state, ntfs_inode *inode, const char *name) { ntfs_attr *data_attribute; char *old_path_end; if (ntfsrec_append_filename(state, name, &old_path_end) == NR_FALSE) { printf("Error: path %s and filename %s are too long.\n", state->path, name); return NR_FALSE; } data_attribute = ntfs_attr_open(inode, AT_DATA, NULL, 0); if (data_attribute != NULL) { int output_fd; unsigned int block_size = 0, retries = 0; s64 offset = 0; output_fd = open(state->path, O_WRONLY | O_CREAT); if (output_fd != -1) { if (inode->mft_no < 2) { block_size = state->volume->mft_record_size; } for(;;) { s64 bytes_read = 0; if (block_size > 0) { bytes_read = ntfs_attr_mst_pread(data_attribute, offset, 1, block_size, state->file_buffer); bytes_read *= block_size; } else { bytes_read = ntfs_attr_pread(data_attribute, offset, NR_FILE_BUFFER_SIZE, state->file_buffer); } if (bytes_read == -1) { unsigned int actual_size = block_size > 0 ? block_size : NR_FILE_BUFFER_SIZE; if (retries++ < state->opt.retries) { state->stats.retries++; continue; } state->stats.errors++; printf("Error: failed %u times to read %s, skipping %d bytes\n", retries, name, actual_size); lseek(output_fd, actual_size, SEEK_CUR); offset += actual_size; continue; } if (bytes_read == 0) { break; } if (write(output_fd, state->file_buffer, bytes_read) < 0) { printf("Error: unable to write to output file %s\n", state->path); if (retries++ < state->opt.retries) { state->stats.retries++; continue; } break; } retries = 0; offset += bytes_read; } close(output_fd); } state->stats.files++; ntfs_attr_close(data_attribute); } else { printf("Error: can't access the data for %s\n", name); } *old_path_end = '\0'; state->current_path_end = old_path_end; return NR_TRUE; }