/** * 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; }
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); }
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; }
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; }
int ntfs_ftruncate_r(struct _reent *r, int fd, off_t len) { ntfs_log_trace("fd %p, len %llu\n", (void *) fd, (u64) len); ntfs_file_state* file = STATE(((intptr_t)(s64)fd)); //ntfs_file_state* file = STATE(((s64) fd)); // Sanity check if (!file || !file->vd || !file->ni || !file->data_na) { r->_errno = EINVAL; return -1; } // Lock ntfsLock(file->vd); // Check that we are allowed to write to this file if (!file->write) { ntfsUnlock(file->vd); r->_errno = EACCES; return -1; } // For compressed files, only deleting and expanding contents are implemented if (file->compressed && len > 0 && len < file->data_na->initialized_size) { ntfsUnlock(file->vd); r->_errno = ENOTSUP; return -1; } // Resize the files data attribute, either by expanding or truncating if (file->compressed && len > file->data_na->initialized_size) { char zero = 0; if (ntfs_attr_pwrite(file->data_na, len - 1, 1, &zero) <= 0) { ntfsUnlock(file->vd); r->_errno = errno; return -1; } } else { if (ntfs_attr_truncate(file->data_na, len)) { ntfsUnlock(file->vd); r->_errno = errno; return -1; } } // Mark the file for archiving (if we actually changed something) if (file->len != file->data_na->data_size) file->ni->flags |= FILE_ATTR_ARCHIVE; // Update file times (if we actually changed something) if (file->len != file->data_na->data_size) ntfsUpdateTimes(file->vd, file->ni, NTFS_UPDATE_AMCTIME); // Update the files data length file->len = file->data_na->data_size; // Sync the file (and its attributes) to disc ntfsSync(file->vd, file->ni); // Unlock ntfsUnlock(file->vd); return 0; }
/** * 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; }
/** * 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; }
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 = cookie->inode; ntfs_attr *na = cookie->stream; size_t size = *_length; int total = 0; status_t result = B_NO_ERROR; TRACE("%s - ENTER!!\n", __FUNCTION__); if (ns->flags & B_FS_IS_READONLY) { ERROR("ntfs is read-only\n"); return EROFS; } if (pos < 0) { *_length = 0; return EINVAL; } LOCK_VOL(ns); TRACE("%s - ENTER\n", __FUNCTION__); // it is a named stream if (na) { 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; return EINVAL; } if (total > 0) fs_ntfs_update_times(_vol, ni, NTFS_UPDATE_ATIME); // XXX needed ? exit: TRACE("%s - EXIT, result is %s\n", __FUNCTION__, strerror(result)); UNLOCK_VOL(ns); return result; }
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; }
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_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; }
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; }
static int ntfs_full_allocation(ntfs_attr *na, ntfs_attr_search_ctx *ctx, s64 alloc_offs, s64 alloc_len) { ATTR_RECORD *attr; ntfs_inode *ni; s64 initialized_size; s64 data_size; int err; err = 0; initialized_size = na->initialized_size; data_size = na->data_size; if (na->allocated_size <= alloc_offs) { /* * Request is fully beyond what was already allocated : * only need to expand the attribute */ err = ntfs_attr_truncate(na, alloc_offs); if (!err) err = ntfs_attr_truncate_solid(na, alloc_offs + alloc_len); } else { /* * Request overlaps what was already allocated : * We may have to fill existing holes, and force zeroes * into clusters which are visible. */ if ((alloc_offs + alloc_len) > na->allocated_size) err = ntfs_attr_truncate(na, alloc_offs + alloc_len); if (!err) err = ntfs_inner_allocation(na, alloc_offs, alloc_len); } /* Set the sizes, even after an error, to keep consistency */ na->initialized_size = initialized_size; /* Restore the original apparent size if requested or error */ if (err || opts.no_size_change || ((alloc_offs + alloc_len) < data_size)) na->data_size = data_size; else { /* * "man 1 fallocate" does not define the new apparent size * when size change is allowed (no --keep-size). * Assuming the same as no FALLOC_FL_KEEP_SIZE in fallocate(2) : * "the file size will be changed if offset + len is greater * than the file size" // TODO check the behavior of another file system */ na->data_size = alloc_offs + alloc_len; } if (!err) { /* Find the attribute, which may have been relocated for allocations */ if (ntfs_attr_lookup(attr_type, attr_name, attr_name_len, CASE_SENSITIVE, 0, NULL, 0, ctx)) { err = -1; ntfs_log_error("Failed to locate the attribute\n"); } else { /* Feed the sizes into the attribute */ attr = ctx->attr; attr->data_size = cpu_to_le64(na->data_size); attr->initialized_size = cpu_to_le64(na->initialized_size); attr->allocated_size = cpu_to_le64(na->allocated_size); if (na->data_flags & ATTR_IS_SPARSE) attr->compressed_size = cpu_to_le64(na->compressed_size); /* Copy the unnamed data attribute sizes to inode */ if ((attr_type == AT_DATA) && !attr_name_len) { ni = na->ni; ni->data_size = na->data_size; if (na->data_flags & ATTR_IS_SPARSE) { ni->allocated_size = na->compressed_size; ni->flags |= FILE_ATTR_SPARSE_FILE; } else ni->allocated_size = na->allocated_size; } } } return (err); }
/** * 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_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; }