static void ntfs_index_ctx_free(ntfs_index_context *icx) { ntfs_log_trace("Entering\n"); if (!icx->entry) return; if (icx->actx) ntfs_attr_put_search_ctx(icx->actx); if (!icx->is_in_root) { if (icx->ib_dirty) { /* FIXME: Error handling!!! */ ntfs_ib_write(icx, icx->ib); } free(icx->ib); } ntfs_attr_close(icx->ia_na); }
static void ntfs_ir_nill(INDEX_ROOT *ir) { INDEX_ENTRY *ie_last; char *ies_start, *ies_end; ntfs_log_trace("Entering\n"); /* * TODO: This function could be much simpler. */ ies_start = (char *)ntfs_ie_get_first(&ir->index); ies_end = (char *)ntfs_ie_get_end(&ir->index); ie_last = ntfs_ie_get_last((INDEX_ENTRY *)ies_start, ies_end); /* * Move the index root termination entry forward */ if ((char *)ie_last > ies_start) { memmove(ies_start, (char *)ie_last, le16_to_cpu(ie_last->length)); ie_last = (INDEX_ENTRY *)ies_start; } }
static int ntfs_ibm_add(ntfs_index_context *icx) { u8 bmp[8]; ntfs_log_trace("Entering\n"); if (ntfs_attr_exist(icx->ni, AT_BITMAP, icx->name, icx->name_len)) return STATUS_OK; /* * AT_BITMAP must be at least 8 bytes. */ memset(bmp, 0, sizeof(bmp)); if (ntfs_attr_add(icx->ni, AT_BITMAP, icx->name, icx->name_len, bmp, sizeof(bmp))) { ntfs_log_perror("Failed to add AT_BITMAP"); return STATUS_ERROR; } return STATUS_OK; }
/** * ntfs_pwrite - positioned write to disk * @dev: device to write to * @pos: position in file descriptor to write to * @count: number of bytes to write * @b: data buffer to write to disk * * This function will write @count bytes from data buffer @b to the device @dev * at position @pos. * * On success, return the number of successfully written bytes. If this number * is lower than @count this means that the write has been interrupted in * flight or that an error was encountered during the write so that the write * is partial. 0 means nothing was written (also return 0 when @count is 0). * * On error and nothing has been written, return -1 with errno set * appropriately to the return code of either seek, write, or set * to EINVAL in case of invalid arguments. */ s64 ntfs_pwrite(struct ntfs_device *dev, const s64 pos, s64 count, const void *b) { s64 written, total, ret = -1; struct ntfs_device_operations *dops; ntfs_log_trace("pos %lld, count %lld\n",(long long)pos,(long long)count); if (!b || count < 0 || pos < 0) { errno = EINVAL; goto out; } if (!count) return 0; if (NDevReadOnly(dev)) { errno = EROFS; goto out; } dops = dev->d_ops; NDevSetDirty(dev); for (total = 0; count; count -= written, total += written) { written = dops->pwrite(dev, (const char*)b + total, count, pos + total); /* If everything ok, continue. */ if (written > 0) continue; /* * If nothing written or error return number of bytes written. */ if (!written || total) break; /* Nothing written and error, return error status. */ total = written; break; } ret = total; out: return ret; }
static s64 ntfs_device_uefi_io_seek(struct ntfs_device *dev, s64 offset, int whence) { struct _uefi_fd *fd = DEV_FD(dev); ntfs_log_trace("dev %p, offset %li, whence %i\n", dev, offset, whence); //AsciiPrint("ntfs_device_uefi_io_seek\n\r"); // Get the device driver descriptor if (!fd) { errno = EBADF; return -1; } // Set the current position on the device (in bytes) switch(whence) { case SEEK_SET: fd->pos = MIN(MAX(offset, 0), fd->len); break; case SEEK_CUR: fd->pos = MIN(MAX(fd->pos + offset, 0), fd->len); break; case SEEK_END: fd->pos = MIN(MAX(fd->len + offset, 0), fd->len); break; } return 0; }
/** * On success return STATUS_OK or STATUS_KEEP_SEARCHING. * On error return STATUS_ERROR. */ static int ntfs_ib_insert(ntfs_index_context *icx, INDEX_ENTRY *ie, VCN new_vcn) { INDEX_BLOCK *ib; u32 idx_size, allocated_size; int err = STATUS_ERROR; VCN old_vcn; ntfs_log_trace("Entering\n"); ib = ntfs_malloc(icx->block_size); if (!ib) return -1; old_vcn = ntfs_icx_parent_vcn(icx); if (ntfs_ib_read(icx, old_vcn, ib)) goto err_out; idx_size = le32_to_cpu(ib->index.index_length); allocated_size = le32_to_cpu(ib->index.allocated_size); /* FIXME: sizeof(VCN) should be included only if ie has no VCN */ if (idx_size + le16_to_cpu(ie->length) + sizeof(VCN) > allocated_size) { err = ntfs_ib_split(icx, ib); if (err == STATUS_OK) err = STATUS_KEEP_SEARCHING; goto err_out; } if (ntfs_ih_insert(&ib->index, ie, new_vcn, ntfs_icx_parent_pos(icx))) goto err_out; if (ntfs_ib_write(icx, ib)) goto err_out; err = STATUS_OK; err_out: free(ib); return err; }
/** * ntfs_index_ctx_get - allocate and initialize a new index context * @ni: ntfs inode with which to initialize the context * @name: name of the which context describes * @name_len: length of the index name * * Allocate a new index context, initialize it with @ni and return it. * Return NULL if allocation failed. */ ntfs_index_context *ntfs_index_ctx_get(ntfs_inode *ni, ntfschar *name, u32 name_len) { ntfs_index_context *icx; ntfs_log_trace("Entering\n"); if (!ni) { errno = EINVAL; return NULL; } if (ni->nr_extents == -1) ni = ni->base_ni; icx = ntfs_calloc(sizeof(ntfs_index_context)); if (icx) *icx = (ntfs_index_context) { .ni = ni, .name = name, .name_len = name_len, }; return icx; }
static int ntfs_ia_add(ntfs_index_context *icx) { ntfs_log_trace("Entering\n"); if (ntfs_ibm_add(icx)) return -1; if (!ntfs_attr_exist(icx->ni, AT_INDEX_ALLOCATION, icx->name, icx->name_len)) { if (ntfs_attr_add(icx->ni, AT_INDEX_ALLOCATION, icx->name, icx->name_len, NULL, 0)) { ntfs_log_perror("Failed to add AT_INDEX_ALLOCATION"); return -1; } } icx->ia_na = ntfs_ia_open(icx, icx->ni); if (!icx->ia_na) return -1; return 0; }
/** * ntfs_index_add_filename - add filename to directory index * @ni: ntfs inode describing directory to which index add filename * @fn: FILE_NAME attribute to add * @mref: reference of the inode which @fn describes * * Return 0 on success or -1 on error with errno set to the error code. */ int ntfs_index_add_filename(ntfs_inode *ni, FILE_NAME_ATTR *fn, MFT_REF mref) { INDEX_ENTRY *ie; ntfs_index_context *icx; int fn_size, ie_size, err, ret = -1; ntfs_log_trace("Entering\n"); if (!ni || !fn) { ntfs_log_error("Invalid arguments.\n"); errno = EINVAL; return -1; } fn_size = (fn->file_name_length * sizeof(ntfschar)) + sizeof(FILE_NAME_ATTR); ie_size = (sizeof(INDEX_ENTRY_HEADER) + fn_size + 7) & ~7; ie = ntfs_calloc(ie_size); if (!ie) return -1; ie->indexed_file = cpu_to_le64(mref); ie->length = cpu_to_le16(ie_size); ie->key_length = cpu_to_le16(fn_size); memcpy(&ie->key, fn, fn_size); icx = ntfs_index_ctx_get(ni, NTFS_INDEX_I30, 4); if (!icx) goto out; ret = ntfs_ie_add(icx, ie); err = errno; ntfs_index_ctx_put(icx); errno = err; out: free(ie); return ret; }
static int ntfs_ib_read(ntfs_index_context *icx, VCN vcn, INDEX_BLOCK *dst) { s64 pos, ret; ntfs_log_trace("vcn: %lld\n", (long long)vcn); pos = ntfs_ib_vcn_to_pos(icx, vcn); ret = ntfs_attr_mst_pread(icx->ia_na, pos, 1, icx->block_size, (u8 *)dst); if (ret != 1) { if (ret == -1) ntfs_log_perror("Failed to read index block"); else ntfs_log_error("Failed to read full index block at " "%lld\n", (long long)pos); return -1; } if (ntfs_ia_check(icx, dst, vcn)) return -1; return 0; }
static int ntfs_device_uefi_io_stat(struct ntfs_device *dev, struct stat *buf) { // Get the device driver descriptor struct _uefi_fd *fd = DEV_FD(dev); mode_t mode; ntfs_log_trace("dev %p, buf %p\n", dev, buf); if (!fd) { errno = EBADF; return -1; } // Short circuit cases were we don't actually have to do anything if (!buf) return 0; // Build the device mode mode = (S_IFBLK) | (S_IRUSR | S_IRGRP | S_IROTH) | ((!NDevReadOnly(dev)) ? (S_IWUSR | S_IWGRP | S_IWOTH) : 0); // Zero out the stat buffer memset(buf, 0, sizeof(struct stat)); // Build the device stats buf->st_dev = 0;//fd->interface->ioType; buf->st_ino = fd->ino; buf->st_mode = mode; buf->st_rdev = 0; //fd->interface->ioType; buf->st_blksize = fd->sectorSize; buf->st_blocks = fd->sectorCount; return 0; }
int ntfs_fstat_r (struct _reent *r, int fd, struct stat *st) { ntfs_log_trace("fd %p\n", fd); ntfs_file_state* file = STATE(fd); int ret = 0; // Sanity check if (!file || !file->vd || !file->ni || !file->data_na) { r->_errno = EINVAL; return -1; } // Short circuit cases were we don't actually have to do anything if (!st) return 0; // Get the file stats ret = ntfsStat(file->vd, file->ni, st); if (ret) r->_errno = errno; return ret; }
DIR_ITER *ntfs_diropen_r (struct _reent *r, DIR_ITER *dirState, const char *path) { ntfs_log_trace("dirState %p, path %s\n", dirState, path); ntfs_dir_state* dir = STATE(dirState); s64 position = 0; // Get the volume descriptor for this path dir->vd = ntfsGetVolume(path); if (!dir->vd) { r->_errno = ENODEV; return NULL; } // Lock ntfsLock(dir->vd); // Find the directory dir->ni = ntfsOpenEntry(dir->vd, path); if (!dir->ni) { ntfsUnlock(dir->vd); r->_errno = ENOENT; return NULL; } // Ensure that this directory is indeed a directory if (!(dir->ni->mrec->flags && MFT_RECORD_IS_DIRECTORY)) { ntfsCloseEntry(dir->vd, dir->ni); ntfsUnlock(dir->vd); r->_errno = ENOTDIR; return NULL; } // Read the directory dir->first = dir->current = NULL; if (ntfs_readdir(dir->ni, &position, dirState, (ntfs_filldir_t)ntfs_readdir_filler)) { ntfsCloseDir(dir); ntfsUnlock(dir->vd); r->_errno = errno; return NULL; } // Move to the first entry in the directory dir->current = dir->first; // Update directory times ntfsUpdateTimes(dir->vd, dir->ni, NTFS_UPDATE_ATIME); // Insert the directory into the double-linked FILO list of open directories if (dir->vd->firstOpenDir) { dir->nextOpenDir = dir->vd->firstOpenDir; dir->vd->firstOpenDir->prevOpenDir = dir; } else { dir->nextOpenDir = NULL; } dir->prevOpenDir = NULL; dir->vd->cwd_ni = dir->ni; dir->vd->firstOpenDir = dir; dir->vd->openDirCount++; // Unlock ntfsUnlock(dir->vd); return dirState; }
int ntfs_statvfs_r (struct _reent *r, const char *path, struct statvfs *buf) { ntfs_log_trace("path %s, buf %p\n", path, buf); ntfs_vd *vd = NULL; s64 size; int delta_bits; // Get the volume descriptor for this path vd = ntfsGetVolume(path); if (!vd) { r->_errno = ENODEV; return -1; } // Short circuit cases were we don't actually have to do anything if (!buf) return 0; // Lock ntfsLock(vd); // Zero out the stat buffer memset(buf, 0, sizeof(struct statvfs)); if(ntfs_volume_get_free_space(vd->vol) < 0) { ntfsUnlock(vd); return -1; } // File system block size buf->f_bsize = vd->vol->cluster_size; // Fundamental file system block size buf->f_frsize = vd->vol->cluster_size; // Total number of blocks on file system in units of f_frsize buf->f_blocks = vd->vol->nr_clusters; // Free blocks available for all and for non-privileged processes size = MAX(vd->vol->free_clusters, 0); buf->f_bfree = buf->f_bavail = size; // Free inodes on the free space delta_bits = vd->vol->cluster_size_bits - vd->vol->mft_record_size_bits; if (delta_bits >= 0) size <<= delta_bits; else size >>= -delta_bits; // Number of inodes at this point in time buf->f_files = (vd->vol->mftbmp_na->allocated_size << 3) + size; // Free inodes available for all and for non-privileged processes size += vd->vol->free_mft_records; buf->f_ffree = buf->f_favail = MAX(size, 0); // File system id buf->f_fsid = vd->id; // Bit mask of f_flag values. buf->f_flag = (NVolReadOnly(vd->vol) ? ST_RDONLY : 0); // Maximum length of filenames buf->f_namemax = NTFS_MAX_NAME_LEN; // Unlock ntfsUnlock(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; }
static int ntfs_device_uefi_io_open(struct ntfs_device *dev, int flags) { NTFS_BOOT_SECTOR *boot; EFI_DISK_IO_PROTOCOL *DiskIo; NTFS_VOLUME *Volume; struct _uefi_fd *fd = DEV_FD(dev); Volume = fd->interface; ntfs_log_trace("dev %p, flags %i\n", dev, flags); // Get the device driver descriptor if (!fd) { errno = EBADF; return -1; } // Get the device interface DiskIo = Volume->DiskIo; if (!DiskIo) { errno = ENODEV; return -1; } // Start the device interface and ensure that it is inserted //if (!interface->startup()) { // ntfs_log_perror("device failed to start\n"); // errno = EIO; // return -1; // } //if (!interface->isInserted()) { // ntfs_log_perror("device media is not inserted\n"); // errno = EIO; // return -1; //} // Check that the device isn't already open (used by another volume?) if (NDevOpen(dev)) { //AsciiPrint("ntfs_device_uefi_io_open...BUSY\n\r"); ntfs_log_perror("device is busy (already open)\n"); errno = EBUSY; return -1; } // Check that there is a valid NTFS boot sector at the start of the device boot = (NTFS_BOOT_SECTOR *) ntfs_alloc(MAX_SECTOR_SIZE); if(boot == NULL) { //AsciiPrint("ntfs_device_uefi_io_open...ENOMEM\n\r"); errno = ENOMEM; return -1; } if (DiskIo->ReadDisk(DiskIo, Volume->MediaId, 0, sizeof(NTFS_BOOT_SECTOR), boot) != EFI_SUCCESS) { //AsciiPrint("DiskIo ptr %x\n\r", DiskIo); //AsciiPrint("interface ptr %x\n\r", fd->interface); //AsciiPrint("DiskIo->ReadDisk(%x,%x,%x)\n\r", fd->interface->MediaId, fd->startSector * fd->sectorSize, sizeof(NTFS_BOOT_SECTOR)); //AsciiPrint("Sector size: %x\n\r", fd->sectorSize); //AsciiPrint("ntfs_device_uefi_io_open...read failure boot sector\n\r"); ntfs_log_perror("read failure @ sector %x\n", fd->startSector); errno = EIO; ntfs_free(boot); return -1; } if (!ntfs_boot_sector_is_ntfs(boot)) { //AsciiPrint("ntfs_device_uefi_io_open...EINVALIDPART\n\r"); errno = EINVALPART; ntfs_free(boot); return -1; } // Parse the boot sector fd->hiddenSectors = le32_to_cpu(boot->bpb.hidden_sectors); fd->sectorSize = le16_to_cpu(boot->bpb.bytes_per_sector); fd->sectorCount = sle64_to_cpu(boot->number_of_sectors); fd->pos = 0; fd->len = (fd->sectorCount * fd->sectorSize); fd->ino = le64_to_cpu(boot->volume_serial_number); // Free memory for boot sector ntfs_free(boot); // Mark the device as read-only (if required) if (flags & O_RDONLY) { NDevSetReadOnly(dev); } // cache disabled! fd->cache = NULL; //_NTFS_cache_constructor(fd->cachePageCount, fd->cachePageSize, interface, fd->startSector + fd->sectorCount, fd->sectorSize); // Mark the device as open NDevSetBlock(dev); NDevSetOpen(dev); //AsciiPrint("ntfs_device_uefi_io_open...success\n\r"); return 0; }
static int ntfs_device_uefi_io_ioctl(struct ntfs_device *dev, int request, void *argp) { struct _uefi_fd *fd = DEV_FD(dev); ntfs_log_trace("dev %p, request %i, argp %p\n", dev, request, argp); // Get the device driver descriptor if (!fd) { errno = EBADF; return -1; } // Figure out which i/o control was requested switch (request) { // Get block device size (sectors) #if defined(BLKGETSIZE) case BLKGETSIZE: { *(u32*)argp = fd->sectorCount; return 0; } #endif // Get block device size (bytes) #if defined(BLKGETSIZE64) case BLKGETSIZE64: { *(u64*)argp = (fd->sectorCount * fd->sectorSize); return 0; } #endif // Get hard drive geometry #if defined(HDIO_GETGEO) case HDIO_GETGEO: { struct hd_geometry *geo = (struct hd_geometry*)argp; geo->sectors = 0; geo->heads = 0; geo->cylinders = 0; geo->start = fd->hiddenSectors; return -1; } #endif // Get block device sector size (bytes) #if defined(BLKSSZGET) case BLKSSZGET: { *(int*)argp = fd->sectorSize; return 0; } #endif // Set block device block size (bytes) #if defined(BLKBSZSET) case BLKBSZSET: { int sectorSize = *(int*)argp; fd->sectorSize = sectorSize; return 0; } #endif // Unimplemented ioctrl default: { ntfs_log_perror("Unimplemented ioctrl %i\n", request); errno = EOPNOTSUPP; return -1; } } return 0; }
static s64 ntfs_device_uefi_io_writebytes(struct ntfs_device *dev, s64 offset, s64 count, const void *buf) { struct _uefi_fd *fd = DEV_FD(dev); sec_t sec_start; sec_t sec_count; u32 buffer_offset; u8 *buffer; ntfs_log_trace("dev %p, offset %l, count %l\n", dev, offset, count); // Get the device driver descriptor if (!fd) { errno = EBADF; return -1; } // Get the device interface //const DISC_INTERFACE* interface = fd->interface; //if (!interface) { // errno = ENODEV; // return -1; //} // Check that the device can be written to if (NDevReadOnly(dev)) { errno = EROFS; return -1; } if(count < 0 || offset < 0) { errno = EROFS; return -1; } if(count == 0) return 0; sec_start = (sec_t) fd->startSector; sec_count = 1; buffer_offset = (u32) (offset % fd->sectorSize); buffer = NULL; // Determine the range of sectors required for this write if (offset > 0) { sec_start += (sec_t) offset / fd->sectorSize; } if ((buffer_offset+count) > fd->sectorSize) { sec_count = (sec_t) ((buffer_offset+count) / fd->sectorSize); if (((buffer_offset+count) % fd->sectorSize) != 0) sec_count++; } // If this write happens to be on the sector boundaries then do the write straight to disc if((buffer_offset == 0) && (count % fd->sectorSize == 0)) { // Write to the device ntfs_log_trace("direct write to sector %d (%d sector(s) long)\n", sec_start, sec_count); if (!ntfs_device_uefi_io_writesectors(dev, sec_start, sec_count, buf)) { ntfs_log_perror("direct write failure @ sector %d (%d sector(s) long)\n", sec_start, sec_count); errno = EIO; return -1; } // Else write from a buffer aligned to the sector boundaries } else { // Allocate a buffer to hold the write data buffer = (u8 *) ntfs_alloc(sec_count * fd->sectorSize); if (!buffer) { errno = ENOMEM; return -1; } // Read the first and last sectors of the buffer from disc (if required) // NOTE: This is done because the data does not line up with the sector boundaries, // we just read in the buffer edges where the data overlaps with the rest of the disc if(buffer_offset != 0) { if (!ntfs_device_uefi_io_readsectors(dev, sec_start, 1, buffer)) { ntfs_log_perror("read failure @ sector %d\n", sec_start); ntfs_free(buffer); errno = EIO; return -1; } } if((buffer_offset+count) % fd->sectorSize != 0) { if (!ntfs_device_uefi_io_readsectors(dev, sec_start + sec_count - 1, 1, buffer + ((sec_count-1) * fd->sectorSize))) { ntfs_log_perror("read failure @ sector %d\n", sec_start + sec_count - 1); ntfs_free(buffer); errno = EIO; return -1; } } // Copy the data into the write buffer memcpy(buffer + buffer_offset, buf, count); // Write to the device ntfs_log_trace("buffered write to sector %d (%d sector(s) long)\n", sec_start, sec_count); if (!ntfs_device_uefi_io_writesectors(dev, sec_start, sec_count, buffer)) { ntfs_log_perror("buffered write failure @ sector %d\n", sec_start); ntfs_free(buffer); errno = EIO; return -1; } // Free the buffer ntfs_free(buffer); } // Mark the device as dirty (if we actually wrote anything) NDevSetDirty(dev); return count; }
static s64 ntfs_device_uefi_io_readbytes(struct ntfs_device *dev, s64 offset, s64 count, void *buf) { struct _uefi_fd *fd = DEV_FD(dev); sec_t sec_start = (sec_t) fd->startSector; sec_t sec_count = 1; u32 buffer_offset = (u32) (offset % fd->sectorSize); u8 *buffer = NULL; //const DISC_INTERFACE* interface; ntfs_log_trace("dev %p, offset %li, count %li\n", dev, offset, count); // Get the device driver descriptor if (!fd) { //AsciiPrint("ntfs_device_uefi_io_readbytes EBADF\n\r"); ntfs_log_perror("EBADF"); errno = EBADF; return -1; } //// Get the device interface //interface = fd->interface; //if (!interface) { // errno = ENODEV; // return -1; //} if(offset < 0) { //AsciiPrint("ntfs_device_uefi_io_readbytes EROFS\n\r"); errno = EROFS; ntfs_log_perror("EROFS"); return -1; } if(!count) return 0; // Determine the range of sectors required for this read if (offset > 0) { sec_start += (sec_t) offset / fd->sectorSize; } if (buffer_offset+count > fd->sectorSize) { sec_count = (sec_t) (buffer_offset+count) / fd->sectorSize; if (((buffer_offset+count) % fd->sectorSize) != 0) sec_count++; } // If this read happens to be on the sector boundaries then do the read straight into the destination buffer if((buffer_offset == 0) && (count % fd->sectorSize == 0)) { // Read from the device ntfs_log_trace("direct read from sector %d (%d sector(s) long)\n", sec_start, sec_count); if (!ntfs_device_uefi_io_readsectors(dev, sec_start, sec_count, buf)) { ntfs_log_perror("direct read failure @ sector %d (%d sector(s) long)\n", sec_start, sec_count); //AsciiPrint("ntfs_device_uefi_io_readbytes EIO\n\r"); errno = EIO; return -1; } // Else read into a buffer and copy over only what was requested } else { // Allocate a buffer to hold the read data buffer = (u8*)ntfs_alloc(sec_count * fd->sectorSize); if (!buffer) { errno = ENOMEM; return -1; } // Read from the device ntfs_log_trace("buffered read from sector %d (%d sector(s) long)\n", sec_start, sec_count); ntfs_log_trace("count: %d sec_count:%d fd->sectorSize: %d )\n", (u32)count, (u32)sec_count,(u32)fd->sectorSize); if (!ntfs_device_uefi_io_readsectors(dev, sec_start, sec_count, buffer)) { //AsciiPrint("ntfs_device_uefi_io_readbytes buffered read failure\n\r"); ntfs_log_perror("buffered read failure @ sector %d (%d sector(s) long)\n", sec_start, sec_count); ntfs_free(buffer); errno = EIO; return -1; } // Copy what was requested to the destination buffer memcpy(buf, buffer + buffer_offset, count); ntfs_free(buffer); } //ntfs_log_perror("Read %d sectors", count); return count; }
static int ntfs_ir_reparent(ntfs_index_context *icx) { ntfs_attr_search_ctx *ctx = NULL; INDEX_ROOT *ir; INDEX_ENTRY *ie; INDEX_BLOCK *ib = NULL; VCN new_ib_vcn; int ix_root_size; int ret = STATUS_ERROR; ntfs_log_trace("Entering\n"); ir = ntfs_ir_lookup2(icx->ni, icx->name, icx->name_len); if (!ir) goto out; if ((ir->index.ih_flags & NODE_MASK) == SMALL_INDEX) if (ntfs_ia_add(icx)) goto out; new_ib_vcn = ntfs_ibm_get_free(icx); if (new_ib_vcn == -1) goto out; ir = ntfs_ir_lookup2(icx->ni, icx->name, icx->name_len); if (!ir) goto clear_bmp; ib = ntfs_ir_to_ib(ir, new_ib_vcn); if (ib == NULL) { ntfs_log_perror("Failed to move index root to index block"); goto clear_bmp; } if (ntfs_ib_write(icx, ib)) goto clear_bmp; retry : ir = ntfs_ir_lookup(icx->ni, icx->name, icx->name_len, &ctx); if (!ir) goto clear_bmp; ntfs_ir_nill(ir); ie = ntfs_ie_get_first(&ir->index); ie->ie_flags |= INDEX_ENTRY_NODE; ie->length = cpu_to_le16(sizeof(INDEX_ENTRY_HEADER) + sizeof(VCN)); ir->index.ih_flags = LARGE_INDEX; ir->index.index_length = cpu_to_le32(le32_to_cpu(ir->index.entries_offset) + le16_to_cpu(ie->length)); ir->index.allocated_size = ir->index.index_length; ix_root_size = sizeof(INDEX_ROOT) - sizeof(INDEX_HEADER) + le32_to_cpu(ir->index.allocated_size); if (ntfs_resident_attr_value_resize(ctx->mrec, ctx->attr, ix_root_size)) { /* * When there is no space to build a non-resident * index, we may have to move the root to an extent */ if ((errno == ENOSPC) && !ctx->al_entry && !ntfs_inode_add_attrlist(icx->ni)) { ntfs_attr_put_search_ctx(ctx); ctx = (ntfs_attr_search_ctx*)NULL; ir = ntfs_ir_lookup(icx->ni, icx->name, icx->name_len, &ctx); if (ir && !ntfs_attr_record_move_away(ctx, ix_root_size - le32_to_cpu(ctx->attr->value_length))) { ntfs_attr_put_search_ctx(ctx); ctx = (ntfs_attr_search_ctx*)NULL; goto retry; } } /* FIXME: revert index root */ goto clear_bmp; } /* * FIXME: do it earlier if we have enough space in IR (should always), * so in error case we wouldn't lose the IB. */ ntfs_ie_set_vcn(ie, new_ib_vcn); ret = STATUS_OK; err_out: free(ib); ntfs_attr_put_search_ctx(ctx); out: return ret; clear_bmp: ntfs_ibm_clear(icx, new_ib_vcn); goto err_out; }
//static size_t ntfs_attr_to_sectors(ntfs_attr *na, const s64 pos, s64 count, uint32_t *sec_out, uint32_t *size_out, int max, uint32_t *s_count, u32 sector_size) static s64 ntfs_attr_to_sectors(ntfs_attr *na, const s64 pos, s64 count, uint32_t *sec_out, uint32_t *size_out, int max, uint32_t *s_count, u32 sector_size) { s64 br, to_read, ofs, total, total2, max_read, max_init; ntfs_volume *vol; runlist_element *rl; uint64_t cur_sector, prev_sector; if (*s_count == 0) { prev_sector = 0xFFFFFFFF; } else { prev_sector = sec_out[*s_count-1]; if (prev_sector != 0xFFFFFFFF) { prev_sector += size_out[*s_count]-1; } } if (*s_count >= max) return 0; vol = na->ni->vol; if (!count) return 0; max_read = na->data_size; max_init = na->initialized_size; if (pos + count > max_read) { if (pos >= max_read) return 0; count = max_read - pos; } total = total2 = 0; /* Zero out reads beyond initialized size. */ if (pos + count > max_init) { if (pos >= max_init) { //memset(b, 0, count); return count; } total2 = pos + count - max_init; count -= total2; //memset((u8*)b + count, 0, total2); } /* Find the runlist element containing the vcn. */ rl = ntfs_attr_find_vcn(na, pos >> vol->cluster_size_bits); if (!rl) { /* * If the vcn is not present it is an out of bounds read. * However, we already truncated the read to the data_size, * so getting this here is an error. */ if (errno == ENOENT) { errno = EIO; ntfs_log_perror("%s: Failed to find VCN #1", __FUNCTION__); } return -1; } ofs = pos - (rl->vcn << vol->cluster_size_bits); for (; count && *s_count < max; rl++, ofs = 0) { if (rl->lcn == LCN_RL_NOT_MAPPED) { rl = ntfs_attr_find_vcn(na, rl->vcn); if (!rl) { if (errno == ENOENT) { errno = EIO; ntfs_log_perror("%s: Failed to find VCN #2", __FUNCTION__); } goto rl_err_out; } /* Needed for case when runs merged. */ ofs = pos + total - (rl->vcn << vol->cluster_size_bits); } if (!rl->length) { errno = EIO; ntfs_log_perror("%s: Zero run length", __FUNCTION__); goto rl_err_out; } if (rl->lcn < (LCN)0) { if (rl->lcn != (LCN)LCN_HOLE) { ntfs_log_perror("%s: Bad run (%lld)", __FUNCTION__, (long long)rl->lcn); goto rl_err_out; } /* It is a hole, just zero the matching @b range. */ to_read = min(count, (rl->length << vol->cluster_size_bits) - ofs); if (to_read < sector_size) to_read = sector_size; if (*s_count == 0) { prev_sector = sec_out[0] = 0xFFFFFFFF; size_out[0] = to_read / sector_size; (*s_count)++; } else { if (prev_sector == 0xFFFFFFFF) { size_out[*s_count-1] += (to_read / sector_size); } else { prev_sector = sec_out[*s_count] = 0xFFFFFFFF; size_out[*s_count] = to_read / sector_size; (*s_count)++; } } /* Update progress counters. */ total += to_read; count -= to_read; //b = (u8*)b + to_read; continue; } /* It is a real lcn, read it into @dst. */ to_read = min(count, (rl->length << vol->cluster_size_bits) - ofs); ntfs_log_trace("Reading %lld bytes from vcn %lld, lcn %lld, ofs" " %lld.\n", (long long)to_read, (long long)rl->vcn, (long long)rl->lcn, (long long)ofs); br = to_read; /*ntfs_pread(vol->dev, (rl->lcn << vol->cluster_size_bits) + ofs, to_read, b);*/ cur_sector = (rl->lcn << vol->cluster_size_bits) + ofs; cur_sector /= sector_size; if (to_read % sector_size) { to_read = to_read + sector_size - (to_read % sector_size); } if (prev_sector == cur_sector) { size_out[*s_count-1] += (to_read / sector_size); prev_sector += (to_read / sector_size); } else { sec_out[*s_count] = cur_sector; size_out[*s_count] = to_read / sector_size; prev_sector = sec_out[*s_count] + size_out[*s_count]; (*s_count)++; } /* If everything ok, update progress counters and continue. */ if (br > 0) { total += br; count -= br; //b = (u8*)b + br; } if (br == to_read) continue; if (total) return total; if (!br) errno = EIO; ntfs_log_perror("%s: ntfs_pread failed", __FUNCTION__); return -1; } /* Finally, return the number of bytes read. */ return total + total2; rl_err_out: if (total) return total; errno = EIO; return -1; }
ssize_t ntfs_write_r(struct _reent *r, int fd, const char *ptr, size_t len) { ntfs_log_trace("fd %p, ptr %p, len %u\n", (void *) fd, ptr, len); ntfs_file_state* file = STATE(((intptr_t)(s64)fd)); //ntfs_file_state* file = STATE(((s64) fd)); ssize_t written = 0; off_t old_pos = 0; // Sanity check if (!file || !file->vd || !file->ni || !file->data_na) { r->_errno = EINVAL; return -1; } // Short circuit cases where we don't actually have to do anything if (!ptr || len <= 0) { return 0; } // 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; } // If we are in append mode, backup the current position and move to the end of the file if (file->append) { old_pos = file->pos; file->pos = file->len; } // Write to the files data atrribute while (len) { ssize_t ret = ntfs_attr_pwrite(file->data_na, file->pos, len, ptr); if (ret <= 0) { ntfsUnlock(file->vd); r->_errno = errno; return -1; } len -= ret; file->pos += ret; written += ret; } // If we are in append mode, restore the current position to were it was prior to this write if (file->append) { file->pos = old_pos; } // Mark the file for archiving (if we actually wrote something) if (written) file->ni->flags |= FILE_ATTR_ARCHIVE; // Update the files data length file->len = file->data_na->data_size; // Unlock ntfsUnlock(file->vd); return written; }
/** * Find a key in the index block. * * Return values: * STATUS_OK with errno set to ESUCCESS if we know for sure that the * entry exists and @ie_out points to this entry. * STATUS_NOT_FOUND with errno set to ENOENT if we know for sure the * entry doesn't exist and @ie_out is the insertion point. * STATUS_KEEP_SEARCHING if we can't answer the above question and * @vcn will contain the node index block. * STATUS_ERROR with errno set if on unexpected error during lookup. */ static int ntfs_ie_lookup(const void *key, const int key_len, ntfs_index_context *icx, INDEX_HEADER *ih, VCN *vcn, INDEX_ENTRY **ie_out) { INDEX_ENTRY *ie; u8 *index_end; int rc, item = 0; ntfs_log_trace("Entering\n"); index_end = ntfs_ie_get_end(ih); /* * Loop until we exceed valid memory (corruption case) or until we * reach the last entry. */ for (ie = ntfs_ie_get_first(ih); ; ie = ntfs_ie_get_next(ie)) { /* Bounds checks. */ if ((u8 *)ie + sizeof(INDEX_ENTRY_HEADER) > index_end || (u8 *)ie + le16_to_cpu(ie->length) > index_end) { errno = ERANGE; ntfs_log_error("Index entry out of bounds in inode " "%llu.\n", (unsigned long long)icx->ni->mft_no); return STATUS_ERROR; } /* * The last entry cannot contain a key. It can however contain * a pointer to a child node in the B+tree so we just break out. */ if (ntfs_ie_end(ie)) break; /* * Not a perfect match, need to do full blown collation so we * know which way in the B+tree we have to go. */ if (!icx->collate) { ntfs_log_error("Collation function not defined\n"); errno = EOPNOTSUPP; return STATUS_ERROR; } rc = icx->collate(icx->ni->vol, key, key_len, &ie->key, le16_to_cpu(ie->key_length)); if (rc == NTFS_COLLATION_ERROR) { ntfs_log_error("Collation error. Perhaps a filename " "contains invalid characters?\n"); errno = ERANGE; return STATUS_ERROR; } /* * If @key collates before the key of the current entry, there * is definitely no such key in this index but we might need to * descend into the B+tree so we just break out of the loop. */ if (rc == -1) break; if (!rc) { *ie_out = ie; errno = 0; icx->parent_pos[icx->pindex] = item; return STATUS_OK; } item++; } /* * We have finished with this index block without success. Check for the * presence of a child node and if not present return with errno ENOENT, * otherwise we will keep searching in another index block. */ if (!(ie->ie_flags & INDEX_ENTRY_NODE)) { ntfs_log_debug("Index entry wasn't found.\n"); *ie_out = ie; errno = ENOENT; return STATUS_NOT_FOUND; } /* Get the starting vcn of the index_block holding the child node. */ *vcn = ntfs_ie_get_vcn(ie); if (*vcn < 0) { errno = EINVAL; ntfs_log_perror("Negative vcn in inode %llu", (unsigned long long)icx->ni->mft_no); return STATUS_ERROR; } ntfs_log_trace("Parent entry number %d\n", item); icx->parent_pos[icx->pindex] = item; return STATUS_KEEP_SEARCHING; }
static int ntfs_index_rm_node(ntfs_index_context *icx) { int entry_pos, pindex; VCN vcn; INDEX_BLOCK *ib = NULL; INDEX_ENTRY *ie_succ, *ie, *entry = icx->entry; INDEX_HEADER *ih; u32 new_size; int delta, ret = STATUS_ERROR; ntfs_log_trace("Entering\n"); if (!icx->ia_na) { icx->ia_na = ntfs_ia_open(icx, icx->ni); if (!icx->ia_na) return STATUS_ERROR; } ib = ntfs_malloc(icx->block_size); if (!ib) return STATUS_ERROR; ie_succ = ntfs_ie_get_next(icx->entry); entry_pos = icx->parent_pos[icx->pindex]++; pindex = icx->pindex; descend: vcn = ntfs_ie_get_vcn(ie_succ); if (ntfs_ib_read(icx, vcn, ib)) goto out; ie_succ = ntfs_ie_get_first(&ib->index); if (ntfs_icx_parent_inc(icx)) goto out; icx->parent_vcn[icx->pindex] = vcn; icx->parent_pos[icx->pindex] = 0; if ((ib->index.ih_flags & NODE_MASK) == INDEX_NODE) goto descend; if (ntfs_ih_zero_entry(&ib->index)) { errno = EIO; ntfs_log_perror("Empty index block"); goto out; } ie = ntfs_ie_dup(ie_succ); if (!ie) goto out; if (ntfs_ie_add_vcn(&ie)) goto out2; ntfs_ie_set_vcn(ie, ntfs_ie_get_vcn(icx->entry)); if (icx->is_in_root) ih = &icx->ir->index; else ih = &icx->ib->index; delta = le16_to_cpu(ie->length) - le16_to_cpu(icx->entry->length); new_size = le32_to_cpu(ih->index_length) + delta; if (delta > 0) { if (icx->is_in_root) { ret = ntfs_ir_make_space(icx, new_size); if (ret != STATUS_OK) goto out2; ih = &icx->ir->index; entry = ntfs_ie_get_by_pos(ih, entry_pos); } else if (new_size > le32_to_cpu(ih->allocated_size)) { icx->pindex = pindex; ret = ntfs_ib_split(icx, icx->ib); if (ret == STATUS_OK) ret = STATUS_KEEP_SEARCHING; goto out2; } } ntfs_ie_delete(ih, entry); ntfs_ie_insert(ih, ie, entry); if (icx->is_in_root) { if (ntfs_ir_truncate(icx, new_size)) goto out2; } else if (ntfs_icx_ib_write(icx)) goto out2; ntfs_ie_delete(&ib->index, ie_succ); if (ntfs_ih_zero_entry(&ib->index)) { if (ntfs_index_rm_leaf(icx)) goto out2; } else if (ntfs_ib_write(icx, ib)) goto out2; ret = STATUS_OK; out2: free(ie); out: free(ib); return ret; }
/* JPA static */ int ntfs_ie_add(ntfs_index_context *icx, INDEX_ENTRY *ie) { INDEX_HEADER *ih; int allocated_size, new_size; int ret = STATUS_ERROR; #ifdef DEBUG /* removed by JPA to make function usable for security indexes char *fn; fn = ntfs_ie_filename_get(ie); ntfs_log_trace("file: '%s'\n", fn); ntfs_attr_name_free(&fn); */ #endif while (1) { if (!ntfs_index_lookup(&ie->key, le16_to_cpu(ie->key_length), icx)) { errno = EEXIST; ntfs_log_perror("Index already have such entry"); goto err_out; } if (errno != ENOENT) { ntfs_log_perror("Failed to find place for new entry"); goto err_out; } if (icx->is_in_root) ih = &icx->ir->index; else ih = &icx->ib->index; allocated_size = le32_to_cpu(ih->allocated_size); new_size = le32_to_cpu(ih->index_length) + le16_to_cpu(ie->length); if (new_size <= allocated_size) break; ntfs_log_trace("index block sizes: allocated: %d needed: %d\n", allocated_size, new_size); if (icx->is_in_root) { if (ntfs_ir_make_space(icx, new_size) == STATUS_ERROR) goto err_out; } else { if (ntfs_ib_split(icx, icx->ib) == STATUS_ERROR) goto err_out; } ntfs_inode_mark_dirty(icx->actx->ntfs_ino); ntfs_index_ctx_reinit(icx); } ntfs_ie_insert(ih, ie, icx->entry); ntfs_index_entry_mark_dirty(icx); ret = STATUS_OK; err_out: ntfs_log_trace("%s\n", ret ? "Failed" : "Done"); return ret; }
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_index_lookup - find a key in an index and return its index entry * @key: [IN] key for which to search in the index * @key_len: [IN] length of @key in bytes * @icx: [IN/OUT] context describing the index and the returned entry * * Before calling ntfs_index_lookup(), @icx must have been obtained from a * call to ntfs_index_ctx_get(). * * Look for the @key in the index specified by the index lookup context @icx. * ntfs_index_lookup() walks the contents of the index looking for the @key. * * If the @key is found in the index, 0 is returned and @icx is setup to * describe the index entry containing the matching @key. @icx->entry is the * index entry and @icx->data and @icx->data_len are the index entry data and * its length in bytes, respectively. * * If the @key is not found in the index, -1 is returned, errno = ENOENT and * @icx is setup to describe the index entry whose key collates immediately * after the search @key, i.e. this is the position in the index at which * an index entry with a key of @key would need to be inserted. * * If an error occurs return -1, set errno to error code and @icx is left * untouched. * * When finished with the entry and its data, call ntfs_index_ctx_put() to free * the context and other associated resources. * * If the index entry was modified, call ntfs_index_entry_mark_dirty() before * the call to ntfs_index_ctx_put() to ensure that the changes are written * to disk. */ int ntfs_index_lookup(const void *key, const int key_len, ntfs_index_context *icx) { VCN old_vcn, vcn; ntfs_inode *ni = icx->ni; INDEX_ROOT *ir; INDEX_ENTRY *ie; INDEX_BLOCK *ib = NULL; int ret, err = 0; ntfs_log_trace("Entering\n"); if (!key || key_len <= 0) { errno = EINVAL; ntfs_log_perror("key: %p key_len: %d", key, key_len); return -1; } ir = ntfs_ir_lookup(ni, icx->name, icx->name_len, &icx->actx); if (!ir) { if (errno == ENOENT) errno = EIO; return -1; } icx->block_size = le32_to_cpu(ir->index_block_size); if (icx->block_size < NTFS_BLOCK_SIZE) { errno = EINVAL; ntfs_log_perror("Index block size (%d) is smaller than the " "sector size (%d)", icx->block_size, NTFS_BLOCK_SIZE); goto err_out; } if (ni->vol->cluster_size <= icx->block_size) icx->vcn_size_bits = ni->vol->cluster_size_bits; else icx->vcn_size_bits = NTFS_BLOCK_SIZE_BITS; /* get the appropriate collation function */ icx->collate = ntfs_get_collate_function(ir->collation_rule); if (!icx->collate) { err = errno = EOPNOTSUPP; ntfs_log_perror("Unknown collation rule 0x%x", (unsigned)le32_to_cpu(ir->collation_rule)); goto err_out; } old_vcn = VCN_INDEX_ROOT_PARENT; /* * FIXME: check for both ir and ib that the first index entry is * within the index block. */ ret = ntfs_ie_lookup(key, key_len, icx, &ir->index, &vcn, &ie); if (ret == STATUS_ERROR) { err = errno; goto err_out; } icx->ir = ir; if (ret != STATUS_KEEP_SEARCHING) { /* STATUS_OK or STATUS_NOT_FOUND */ err = errno; icx->is_in_root = TRUE; icx->parent_vcn[icx->pindex] = old_vcn; goto done; } /* Child node present, descend into it. */ icx->ia_na = ntfs_ia_open(icx, ni); if (!icx->ia_na) goto err_out; ib = ntfs_malloc(icx->block_size); if (!ib) { err = errno; goto err_out; } descend_into_child_node: icx->parent_vcn[icx->pindex] = old_vcn; if (ntfs_icx_parent_inc(icx)) { err = errno; goto err_out; } old_vcn = vcn; ntfs_log_debug("Descend into node with VCN %lld\n", (long long)vcn); if (ntfs_ib_read(icx, vcn, ib)) goto err_out; ret = ntfs_ie_lookup(key, key_len, icx, &ib->index, &vcn, &ie); if (ret != STATUS_KEEP_SEARCHING) { err = errno; if (ret == STATUS_ERROR) goto err_out; /* STATUS_OK or STATUS_NOT_FOUND */ icx->is_in_root = FALSE; icx->ib = ib; icx->parent_vcn[icx->pindex] = vcn; goto done; } if ((ib->index.ih_flags & NODE_MASK) == LEAF_NODE) { ntfs_log_error("Index entry with child node found in a leaf " "node in inode 0x%llx.\n", (unsigned long long)ni->mft_no); goto err_out; } goto descend_into_child_node; err_out: free(ib); if (!err) err = EIO; errno = err; return -1; done: icx->entry = ie; icx->data = (u8 *)ie + offsetof(INDEX_ENTRY, key); icx->data_len = le16_to_cpu(ie->key_length); ntfs_log_trace("Done.\n"); if (err) { errno = err; return -1; } return 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; }
int ntfs_dirnext_r (struct _reent *r, DIR_ITER *dirState, char *filename, struct stat *filestat) { ntfs_log_trace("dirState %p, filename %p, filestat %p\n", dirState, filename, filestat); ntfs_dir_state* dir = STATE(dirState); ntfs_inode *ni = NULL; // Sanity check if (!dir || !dir->vd || !dir->ni) { r->_errno = EBADF; return -1; } // Lock ntfsLock(dir->vd); // Check that there is a entry waiting to be fetched if (!dir->current) { ntfsUnlock(dir->vd); r->_errno = ENOENT; return -1; } // Fetch the current entry //strcpy(filename, dir->current->name); strcpy(filename, dir->current->name); if(filestat != NULL) { if(strcmp(filename, ".") == 0 || strcmp(filename, "..") == 0) { memset(filestat, 0, sizeof(struct stat)); filestat->st_mode = S_IFDIR; } else { ni = ntfsOpenEntry(dir->vd, filename); if (ni) { if(ntfsStat(dir->vd, ni, filestat)) { r->_errno = errno; ntfsUnlock(dir->vd); return -1; } ntfsCloseEntry(dir->vd, ni); } else { r->_errno = errno; ntfsUnlock(dir->vd); return -1; } } } // Move to the next entry in the directory dir->current = dir->current->next; // Update directory times ntfsUpdateTimes(dir->vd, dir->ni, NTFS_UPDATE_ATIME); ntfsUnlock(dir->vd); return 0; }