/** * ntfs_debug_runlist_dump - Dump a runlist. * @rl: * * Description... * * Returns: */ void ntfs_debug_runlist_dump(const runlist_element *rl) { int i = 0; const char *lcn_str[5] = { "LCN_HOLE ", "LCN_RL_NOT_MAPPED", "LCN_ENOENT ", "LCN_EINVAL ", "LCN_unknown " }; ntfs_log_debug("NTFS-fs DEBUG: Dumping runlist (values in hex):\n"); if (!rl) { ntfs_log_debug("Run list not present.\n"); return; } ntfs_log_debug("VCN LCN Run length\n"); do { LCN lcn = (rl + i)->lcn; if (lcn < (LCN)0) { int idx = -lcn - 1; if (idx > -LCN_EINVAL - 1) idx = 4; ntfs_log_debug("%-16llx %s %-16llx%s\n", rl[i].vcn, lcn_str[idx], rl[i].length, rl[i].length ? "" : " (runlist end)"); } else ntfs_log_debug("%-16llx %-16llx %-16llx%s\n", rl[i].vcn, rl[i].lcn, rl[i].length, rl[i].length ? "" : " (runlist end)"); } while (rl[i++].length); }
/** * ntfs_device_size_get - return the size of a device in blocks * @dev: open device * @block_size: block size in bytes in which to return the result * * Return the number of @block_size sized blocks in the device described by the * open device @dev. * * Adapted from e2fsutils-1.19, Copyright (C) 1995 Theodore Ts'o. * * On error return -1 with errno set to the error code. */ s64 ntfs_device_size_get(struct ntfs_device *dev, int block_size) { s64 high, low; if (!dev || block_size <= 0 || (block_size - 1) & block_size) { errno = EINVAL; return -1; } #ifdef BLKGETSIZE64 { u64 size; if (dev->d_ops->ioctl(dev, BLKGETSIZE64, &size) >= 0) { ntfs_log_debug("BLKGETSIZE64 nr bytes = %llu (0x%llx)\n", (unsigned long long)size, (unsigned long long)size); return (s64)size / block_size; } } #endif #ifdef BLKGETSIZE { unsigned long size; if (dev->d_ops->ioctl(dev, BLKGETSIZE, &size) >= 0) { ntfs_log_debug("BLKGETSIZE nr 512 byte blocks = %lu (0x%lx)\n", size, size); return (s64)size * 512 / block_size; } } #endif #ifdef FDGETPRM { struct floppy_struct this_floppy; if (dev->d_ops->ioctl(dev, FDGETPRM, &this_floppy) >= 0) { ntfs_log_debug("FDGETPRM nr 512 byte blocks = %lu (0x%lx)\n", (unsigned long)this_floppy.size, (unsigned long)this_floppy.size); return (s64)this_floppy.size * 512 / block_size; } } #endif /* * We couldn't figure it out by using a specialized ioctl, * so do binary search to find the size of the device. */ low = 0LL; for (high = 1024LL; !ntfs_device_offset_valid(dev, high); high <<= 1) low = high; while (low < high - 1LL) { const s64 mid = (low + high) / 2; if (!ntfs_device_offset_valid(dev, mid)) low = mid; else high = mid; } dev->d_ops->seek(dev, 0LL, SEEK_SET); return (low + 1LL) / block_size; }
/** * ntfs_device_block_size_set - set block size of a device * @dev: open device * @block_size: block size to set @dev to * * On success, return 0. * On error return -1 with errno set to the error code. * * The following error codes are defined: * EINVAL Input parameter error * EOPNOTSUPP System does not support BLKBSZSET ioctl * ENOTTY @dev is a file or a device not supporting BLKBSZSET */ int ntfs_device_block_size_set(struct ntfs_device *dev, int block_size __attribute__((unused))) { if (!dev) { errno = EINVAL; return -1; } #ifdef BLKBSZSET { size_t s_block_size = block_size; if (!dev->d_ops->ioctl(dev, BLKBSZSET, &s_block_size)) { ntfs_log_debug("Used BLKBSZSET to set block size to " "%d bytes.\n", block_size); return 0; } /* If not a block device, pretend it was successful. */ if (!NDevBlock(dev)) return 0; } #else /* If not a block device, pretend it was successful. */ if (!NDevBlock(dev)) return 0; errno = EOPNOTSUPP; #endif return -1; }
/** * ntfs_ucsncasecmp - compare two little endian Unicode strings, ignoring case * @s1: first string * @s2: second string * @n: maximum unicode characters to compare * @upcase: upcase table * @upcase_size: upcase table size in Unicode characters * * Compare the first @n characters of the Unicode strings @s1 and @s2, * ignoring case. The strings in little endian format and appropriate * le16_to_cpu() conversion is performed on non-little endian machines. * * Each character is uppercased using the @upcase table before the comparison. * * The function returns an integer less than, equal to, or greater than zero * if @s1 (or the first @n Unicode characters thereof) is found, respectively, * to be less than, to match, or be greater than @s2. */ int ntfs_ucsncasecmp(const ntfschar *s1, const ntfschar *s2, size_t n, const ntfschar *upcase, const u32 upcase_size) { u16 c1, c2; size_t i; #ifdef DEBUG if (!s1 || !s2 || !upcase) { ntfs_log_debug("ntfs_wcsncasecmp() received NULL pointer!\n"); exit(1); } #endif for (i = 0; i < n; ++i) { if ((c1 = le16_to_cpu(s1[i])) < upcase_size) c1 = le16_to_cpu(upcase[c1]); if ((c2 = le16_to_cpu(s2[i])) < upcase_size) c2 = le16_to_cpu(upcase[c2]); if (c1 < c2) return -1; if (c1 > c2) return 1; if (!c1) break; } return 0; }
void ntfs_ie_filename_dump(INDEX_ENTRY *ie) { char *s; s = ntfs_ie_filename_get(ie); ntfs_log_debug("'%s' ", s); ntfs_attr_name_free(&s); }
/** * ntfs_device_sector_size_get - get sector size of a device * @dev: open device * * On success, return the sector size in bytes of the device @dev. * On error return -1 with errno set to the error code. * * The following error codes are defined: * EINVAL Input parameter error * EOPNOTSUPP System does not support BLKSSZGET ioctl * ENOTTY @dev is a file or a device not supporting BLKSSZGET */ int ntfs_device_sector_size_get(struct ntfs_device *dev) { if (!dev) { errno = EINVAL; return -1; } #ifdef BLKSSZGET { int sect_size = 0; if (!dev->d_ops->ioctl(dev, BLKSSZGET, §_size)) { ntfs_log_debug("BLKSSZGET sector size = %d bytes\n", sect_size); return sect_size; } } #elif defined(DIOCGSECTORSIZE) { /* FreeBSD */ size_t sect_size = 0; if (!dev->d_ops->ioctl(dev, DIOCGSECTORSIZE, §_size)) { ntfs_log_debug("DIOCGSECTORSIZE sector size = %d bytes\n", (int) sect_size); return sect_size; } } #elif defined(DKIOCGETBLOCKSIZE) { /* Mac OS X */ uint32_t sect_size = 0; if (!dev->d_ops->ioctl(dev, DKIOCGETBLOCKSIZE, §_size)) { ntfs_log_debug("DKIOCGETBLOCKSIZE sector size = %d bytes\n", (int) sect_size); return sect_size; } } #else errno = EOPNOTSUPP; #endif return -1; }
static int ntfs_device_uefi_io_close(struct ntfs_device *dev) { struct _uefi_fd *fd = DEV_FD(dev); ntfs_log_trace("dev %p\n", dev); //AsciiPrint("ntfs_device_uefi_io_close\n\r"); // Get the device driver descriptor if (!fd) { errno = EBADF; return -1; } // Check that the device is actually open if (!NDevOpen(dev)) { ntfs_log_perror("device is not open\n"); errno = EIO; return -1; } // Mark the device as closed NDevClearOpen(dev); NDevClearBlock(dev); // Flush the device (if dirty and not read-only) if (NDevDirty(dev) && !NDevReadOnly(dev)) { ntfs_log_debug("device is dirty, will now sync\n"); // ...? // Mark the device as clean NDevClearDirty(dev); } // Flush and destroy the cache (if required) if (fd->cache) { //_NTFS_cache_flush(fd->cache); //_NTFS_cache_destructor(fd->cache); } // Shutdown the device interface /*const DISC_INTERFACE* interface = fd->interface; if (interface) { interface->shutdown(); }*/ // Free the device driver private data ntfs_free(dev->d_private); dev->d_private = NULL; return 0; }
/** * ntfs_device_unix_io_open - Open a device and lock it exclusively * @dev: * @flags: * * Description... * * Returns: */ static int ntfs_device_unix_io_open(struct ntfs_device *dev, int flags) { struct flock flk; struct stat sbuf; int err; if (NDevOpen(dev)) { errno = EBUSY; return -1; } if (!(dev->d_private = ntfs_malloc(sizeof(int)))) return -1; *(int*)dev->d_private = open(dev->d_name, flags); if (*(int*)dev->d_private == -1) { err = errno; goto err_out; } /* Setup our read-only flag. */ if ((flags & O_RDWR) != O_RDWR) NDevSetReadOnly(dev); /* Acquire exclusive (mandatory) lock on the whole device. */ memset(&flk, 0, sizeof(flk)); if (NDevReadOnly(dev)) flk.l_type = F_RDLCK; else flk.l_type = F_WRLCK; flk.l_whence = SEEK_SET; flk.l_start = flk.l_len = 0LL; if (fcntl(DEV_FD(dev), F_SETLK, &flk)) { err = errno; ntfs_log_debug("ntfs_device_unix_io_open: Could not lock %s " "for %s\n", dev->d_name, NDevReadOnly(dev) ? "reading" : "writing"); if (close(DEV_FD(dev))) ntfs_log_perror("ntfs_device_unix_io_open: Warning: " "Could not close %s", dev->d_name); goto err_out; } /* Determine if device is a block device or not, ignoring errors. */ if (!fstat(DEV_FD(dev), &sbuf) && S_ISBLK(sbuf.st_mode)) NDevSetBlock(dev); /* Set our open flag. */ NDevSetOpen(dev); return 0; err_out: free(dev->d_private); dev->d_private = NULL; errno = err; return -1; }
/** * ntfs_names_collate - collate two Unicode names * @name1: first Unicode name to compare * @name1_len: length of first Unicode name to compare * @name2: second Unicode name to compare * @name2_len: length of second Unicode name to compare * @err_val: if @name1 contains an invalid character return this value * @ic: either CASE_SENSITIVE or IGNORE_CASE * @upcase: upcase table (ignored if @ic is CASE_SENSITIVE) * @upcase_len: upcase table size (ignored if @ic is CASE_SENSITIVE) * * ntfs_names_collate() collates two Unicode names and returns: * * -1 if the first name collates before the second one, * 0 if the names match, * 1 if the second name collates before the first one, or * @err_val if an invalid character is found in @name1 during the comparison. * * The following characters are considered invalid: '"', '*', '<', '>' and '?'. */ int ntfs_names_collate(const ntfschar *name1, const u32 name1_len, const ntfschar *name2, const u32 name2_len, const int err_val __attribute__((unused)), const IGNORE_CASE_BOOL ic, const ntfschar *upcase, const u32 upcase_len) { u32 cnt; ntfschar c1, c2; #ifdef DEBUG if (!name1 || !name2 || (ic && (!upcase || !upcase_len))) { ntfs_log_debug("ntfs_names_collate received NULL pointer!\n"); exit(1); } #endif for (cnt = 0; cnt < min(name1_len, name2_len); ++cnt) { c1 = le16_to_cpu(*name1); name1++; c2 = le16_to_cpu(*name2); name2++; if (ic) { if (c1 < upcase_len) c1 = le16_to_cpu(upcase[c1]); if (c2 < upcase_len) c2 = le16_to_cpu(upcase[c2]); } #if 0 if (c1 < 64 && legal_ansi_char_array[c1] & 8) return err_val; #endif if (c1 < c2) return -1; if (c1 > c2) return 1; } if (name1_len < name2_len) return -1; if (name1_len == name2_len) return 0; /* name1_len > name2_len */ #if 0 c1 = le16_to_cpu(*name1); if (c1 < 64 && legal_ansi_char_array[c1] & 8) return err_val; #endif return 1; }
/** * ntfs_log_parse_option - Act upon command line options * @option: Option flag * * Delegate some of the work of parsing the command line. All the options begin * with "--log-". Options cause log levels to be enabled in @ntfs_log (the * global logging structure). * * Note: The "colour" option changes the logging handler. * * Returns: TRUE Option understood * FALSE Invalid log option */ BOOL1 ntfs_log_parse_option(const char *option) { if (strcmp(option, "--log-debug") == 0) { ntfs_log_set_levels(NTFS_LOG_LEVEL_DEBUG); return TRUE; } else if (strcmp(option, "--log-verbose") == 0) { ntfs_log_set_levels(NTFS_LOG_LEVEL_VERBOSE); return TRUE; } else if (strcmp(option, "--log-quiet") == 0) { ntfs_log_clear_levels(NTFS_LOG_LEVEL_QUIET); return TRUE; } else if (strcmp(option, "--log-trace") == 0) { ntfs_log_set_levels(NTFS_LOG_LEVEL_TRACE); return TRUE; } ntfs_log_debug("Unknown logging option '%s'\n", option); return FALSE; }
/** * ntfs_device_partition_start_sector_get - get starting sector of a partition * @dev: open device * * On success, return the starting sector of the partition @dev in the parent * block device of @dev. On error return -1 with errno set to the error code. * * The following error codes are defined: * EINVAL Input parameter error * EOPNOTSUPP System does not support HDIO_GETGEO ioctl * ENOTTY @dev is a file or a device not supporting HDIO_GETGEO */ s64 ntfs_device_partition_start_sector_get(struct ntfs_device *dev) { if (!dev) { errno = EINVAL; return -1; } #ifdef HDIO_GETGEO { struct hd_geometry geo; if (!dev->d_ops->ioctl(dev, HDIO_GETGEO, &geo)) { ntfs_log_debug("HDIO_GETGEO start_sect = %lu (0x%lx)\n", geo.start, geo.start); return geo.start; } } #else errno = EOPNOTSUPP; #endif return -1; }
/** * ntfs_device_sector_size_get - get sector size of a device * @dev: open device * * On success, return the sector size in bytes of the device @dev. * On error return -1 with errno set to the error code. * * The following error codes are defined: * EINVAL Input parameter error * EOPNOTSUPP System does not support BLKSSZGET ioctl * ENOTTY @dev is a file or a device not supporting BLKSSZGET */ int ntfs_device_sector_size_get(struct ntfs_device *dev) { if (!dev) { errno = EINVAL; return -1; } #ifdef BLKSSZGET { int sect_size = 0; if (!dev->d_ops->ioctl(dev, BLKSSZGET, §_size)) { ntfs_log_debug("BLKSSZGET sector size = %d bytes\n", sect_size); return sect_size; } } #else errno = EOPNOTSUPP; #endif return -1; }
/** * ntfs_device_sectors_per_track_get - get number of sectors per track of device * @dev: open device * * On success, return the number of sectors per track on the device @dev. On * error return -1 with errno set to the error code. * * The following error codes are defined: * EINVAL Input parameter error * EOPNOTSUPP System does not support HDIO_GETGEO ioctl * ENOTTY @dev is a file or a device not supporting HDIO_GETGEO */ int ntfs_device_sectors_per_track_get(struct ntfs_device *dev) { if (!dev) { errno = EINVAL; return -1; } #ifdef HDIO_GETGEO { struct hd_geometry geo; if (!dev->d_ops->ioctl(dev, HDIO_GETGEO, &geo)) { ntfs_log_debug("HDIO_GETGEO sectors_per_track = %u (0x%x)\n", (unsigned)geo.sectors, (unsigned)geo.sectors); return geo.sectors; } } #else errno = EOPNOTSUPP; #endif return -1; }
/** * ntfs_ucsncmp - compare two little endian Unicode strings * @s1: first string * @s2: second string * @n: maximum unicode characters to compare * * Compare the first @n characters of the Unicode strings @s1 and @s2, * The strings in little endian format and appropriate le16_to_cpu() * conversion is performed on non-little endian machines. * * The function returns an integer less than, equal to, or greater than zero * if @s1 (or the first @n Unicode characters thereof) is found, respectively, * to be less than, to match, or be greater than @s2. */ int ntfs_ucsncmp(const ntfschar *s1, const ntfschar *s2, size_t n) { ntfschar c1, c2; size_t i; #ifdef DEBUG if (!s1 || !s2) { ntfs_log_debug("ntfs_wcsncmp() received NULL pointer!\n"); exit(1); } #endif for (i = 0; i < n; ++i) { c1 = le16_to_cpu(s1[i]); c2 = le16_to_cpu(s2[i]); if (c1 < c2) return -1; if (c1 > c2) return 1; if (!c1) break; } return 0; }
/** * ntfs_device_size_get - return the size of a device in blocks * @dev: open device * @block_size: block size in bytes in which to return the result * * Return the number of @block_size sized blocks in the device described by the * open device @dev. * * Adapted from e2fsutils-1.19, Copyright (C) 1995 Theodore Ts'o. * * On error return -1 with errno set to the error code. */ s64 ntfs_device_size_get(struct ntfs_device *dev, int block_size) { s64 high, low; if (!dev || block_size <= 0 || (block_size - 1) & block_size) { errno = EINVAL; return -1; } #ifdef BLKGETSIZE64 { u64 size; if (dev->d_ops->ioctl(dev, BLKGETSIZE64, &size) >= 0) { ntfs_log_debug("BLKGETSIZE64 nr bytes = %llu (0x%llx)\n", (unsigned long long)size, (unsigned long long)size); return (s64)size / block_size; } } #endif #ifdef BLKGETSIZE { unsigned long size; if (dev->d_ops->ioctl(dev, BLKGETSIZE, &size) >= 0) { ntfs_log_debug("BLKGETSIZE nr 512 byte blocks = %lu (0x%lx)\n", size, size); return (s64)size * 512 / block_size; } } #endif #ifdef FDGETPRM { struct floppy_struct this_floppy; if (dev->d_ops->ioctl(dev, FDGETPRM, &this_floppy) >= 0) { ntfs_log_debug("FDGETPRM nr 512 byte blocks = %lu (0x%lx)\n", (unsigned long)this_floppy.size, (unsigned long)this_floppy.size); return (s64)this_floppy.size * 512 / block_size; } } #endif #ifdef DIOCGMEDIASIZE { /* FreeBSD */ off_t size; if (dev->d_ops->ioctl(dev, DIOCGMEDIASIZE, &size) >= 0) { ntfs_log_debug("DIOCGMEDIASIZE nr bytes = %llu (0x%llx)\n", (unsigned long long)size, (unsigned long long)size); return (s64)size / block_size; } } #endif #ifdef DKIOCGETBLOCKCOUNT { /* Mac OS X */ uint64_t blocks; int sector_size; sector_size = ntfs_device_sector_size_get(dev); if (sector_size >= 0 && dev->d_ops->ioctl(dev, DKIOCGETBLOCKCOUNT, &blocks) >= 0) { ntfs_log_debug("DKIOCGETBLOCKCOUNT nr blocks = %llu (0x%llx)\n", (unsigned long long) blocks, (unsigned long long) blocks); return blocks * sector_size / block_size; } } #endif #ifdef __HAIKU__ { off_t size = 0; partition_info partitionInfo; device_geometry geometry; if (dev->d_ops->ioctl(dev, B_GET_PARTITION_INFO, &partitionInfo) == 0) size = partitionInfo.size; else if (dev->d_ops->ioctl(dev, B_GET_GEOMETRY, &geometry) == 0) { size = (off_t)geometry.cylinder_count * geometry.sectors_per_track * geometry.head_count * geometry.bytes_per_sector; } if (size > 0) return (s64)size / block_size; } #endif /* * We couldn't figure it out by using a specialized ioctl, * so do binary search to find the size of the device. */ low = 0LL; for (high = 1024LL; !ntfs_device_offset_valid(dev, high); high <<= 1) low = high; while (low < high - 1LL) { const s64 mid = (low + high) / 2; if (!ntfs_device_offset_valid(dev, mid)) low = mid; else high = mid; } dev->d_ops->seek(dev, 0LL, SEEK_SET); return (low + 1LL) / block_size; }
/** * 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; }
/** * 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; }
/** * ntfs_boot_sector_parse - setup an ntfs volume from an ntfs boot sector * @vol: ntfs_volume to setup * @bs: buffer containing ntfs boot sector to parse * * Parse the ntfs bootsector @bs and setup the ntfs volume @vol with the * obtained values. * * Return 0 on success or -1 on error with errno set to the error code EINVAL. */ int ntfs_boot_sector_parse(ntfs_volume *vol, const NTFS_BOOT_SECTOR *bs) { s64 sectors; u8 sectors_per_cluster; s8 c; /* We return -1 with errno = EINVAL on error. */ errno = EINVAL; vol->sector_size = le16_to_cpu(bs->bpb.bytes_per_sector); vol->sector_size_bits = ffs(vol->sector_size) - 1; ntfs_log_debug("SectorSize = 0x%x\n", vol->sector_size); ntfs_log_debug("SectorSizeBits = %u\n", vol->sector_size_bits); /* * The bounds checks on mft_lcn and mft_mirr_lcn (i.e. them being * below or equal the number_of_clusters) really belong in the * ntfs_boot_sector_is_ntfs but in this way we can just do this once. */ sectors_per_cluster = bs->bpb.sectors_per_cluster; ntfs_log_debug("SectorsPerCluster = 0x%x\n", sectors_per_cluster); if (sectors_per_cluster & (sectors_per_cluster - 1)) { ntfs_log_error("sectors_per_cluster (%d) is not a power of 2." "\n", sectors_per_cluster); return -1; } sectors = sle64_to_cpu(bs->number_of_sectors); ntfs_log_debug("NumberOfSectors = %lld\n", (long long)sectors); if (!sectors) { ntfs_log_error("Volume size is set to zero.\n"); return -1; } if (vol->dev->d_ops->seek(vol->dev, (sectors - 1) << vol->sector_size_bits, SEEK_SET) == -1) { ntfs_log_perror("Failed to read last sector (%lld)", (long long)sectors); ntfs_log_error("%s", last_sector_error); return -1; } vol->nr_clusters = sectors >> (ffs(sectors_per_cluster) - 1); vol->mft_lcn = sle64_to_cpu(bs->mft_lcn); vol->mftmirr_lcn = sle64_to_cpu(bs->mftmirr_lcn); ntfs_log_debug("MFT LCN = %lld\n", (long long)vol->mft_lcn); ntfs_log_debug("MFTMirr LCN = %lld\n", (long long)vol->mftmirr_lcn); if (vol->mft_lcn > vol->nr_clusters || vol->mftmirr_lcn > vol->nr_clusters) { ntfs_log_error("$MFT LCN (%lld) or $MFTMirr LCN (%lld) is " "greater than the number of clusters (%lld).\n", (long long)vol->mft_lcn, (long long)vol->mftmirr_lcn, (long long)vol->nr_clusters); return -1; } vol->cluster_size = sectors_per_cluster * vol->sector_size; if (vol->cluster_size & (vol->cluster_size - 1)) { ntfs_log_error("cluster_size (%d) is not a power of 2.\n", vol->cluster_size); return -1; } vol->cluster_size_bits = ffs(vol->cluster_size) - 1; /* * Need to get the clusters per mft record and handle it if it is * negative. Then calculate the mft_record_size. A value of 0x80 is * illegal, thus signed char is actually ok! */ c = bs->clusters_per_mft_record; ntfs_log_debug("ClusterSize = 0x%x\n", (unsigned)vol->cluster_size); ntfs_log_debug("ClusterSizeBits = %u\n", vol->cluster_size_bits); ntfs_log_debug("ClustersPerMftRecord = 0x%x\n", c); /* * When clusters_per_mft_record is negative, it means that it is to * be taken to be the negative base 2 logarithm of the mft_record_size * min bytes. Then: * mft_record_size = 2^(-clusters_per_mft_record) bytes. */ if (c < 0) vol->mft_record_size = 1 << -c; else vol->mft_record_size = c << vol->cluster_size_bits; if (vol->mft_record_size & (vol->mft_record_size - 1)) { ntfs_log_error("mft_record_size (%d) is not a power of 2.\n", vol->mft_record_size); return -1; } vol->mft_record_size_bits = ffs(vol->mft_record_size) - 1; ntfs_log_debug("MftRecordSize = 0x%x\n", (unsigned)vol->mft_record_size); ntfs_log_debug("MftRecordSizeBits = %u\n", vol->mft_record_size_bits); /* Same as above for INDX record. */ c = bs->clusters_per_index_record; ntfs_log_debug("ClustersPerINDXRecord = 0x%x\n", c); if (c < 0) vol->indx_record_size = 1 << -c; else vol->indx_record_size = c << vol->cluster_size_bits; vol->indx_record_size_bits = ffs(vol->indx_record_size) - 1; ntfs_log_debug("INDXRecordSize = 0x%x\n", (unsigned)vol->indx_record_size); ntfs_log_debug("INDXRecordSizeBits = %u\n", vol->indx_record_size_bits); /* * Work out the size of the MFT mirror in number of mft records. If the * cluster size is less than or equal to the size taken by four mft * records, the mft mirror stores the first four mft records. If the * cluster size is bigger than the size taken by four mft records, the * mft mirror contains as many mft records as will fit into one * cluster. */ if (vol->cluster_size <= 4 * vol->mft_record_size) vol->mftmirr_size = 4; else vol->mftmirr_size = vol->cluster_size / vol->mft_record_size; return 0; }
ntfs_vd *ntfsMount (const char *name, struct _NTFS_VOLUME *interface, sec_t startSector, u32 cachePageCount, u32 cachePageSize, u32 flags) { ntfs_vd *vd = NULL; struct _uefi_fd *fd = NULL; const devoptab_t *mnt; //Print(L"ntfsMount %a\n", name); //CpuBreakpoint(); // Sanity check if (!name || !interface) { //Print(L"ntfsMount EINVAL\n"); errno = EINVAL; return false; } // Initialise ntfs-3g ntfsInit(); mnt = ntfsGetDevice(name, false); // Check that the requested mount name is free if (mnt) { //Print(L"ntfsMount EADDRINUSE\n"); errno = 99; //EADDRINUSE; return (ntfs_vd*) mnt->deviceData; // previous mnt data! } // Allocate the volume descriptor vd = (ntfs_vd*)ntfs_alloc(sizeof(ntfs_vd)); if (!vd) { //Print(L"ntfsMount ENOMEM\n"); errno = ENOMEM; return false; } else { //Print(L"ntfsMount ntfs_vd!\n"); } // Setup the volume descriptor //Print(L"vd.id! [%x]\n", vd); vd->id = 0;//interface->ioType; //Print(L"vd.flags!\n"); vd->flags = 0; vd->uid = 0; vd->gid = 0; vd->fmask = 0; vd->dmask = 0; vd->atime = ((flags & NTFS_UPDATE_ACCESS_TIMES) ? ATIME_ENABLED : ATIME_DISABLED); vd->showHiddenFiles = (flags & NTFS_SHOW_HIDDEN_FILES); vd->showSystemFiles = (flags & NTFS_SHOW_SYSTEM_FILES); //Print(L"invoking ntfs_alloc!\n"); // Allocate the device driver descriptor fd = (struct _uefi_fd *)ntfs_alloc(sizeof(struct _uefi_fd)); if (!fd) { //Print(L"ntfsMount ENOMEM(2)\n"); ntfs_free(vd); errno = ENOMEM; return false; } else { //Print(L"ntfs_alloc uefi_fd\n"); } // Setup the device driver descriptor fd->interface = interface; fd->startSector = startSector; fd->sectorSize = 0x200; fd->sectorCount = 0x200; fd->cachePageCount = cachePageCount; fd->cachePageSize = cachePageSize; // Allocate the device driver vd->dev = ntfs_device_alloc(name, 0, &ntfs_device_uefi_io_ops, fd); if (!vd->dev) { //Print(L"ntfsMount ntfs_device_alloc failed\n"); ntfs_free(fd); ntfs_free(vd); return false; } //Print(L"ntfs_device_alloc success\n"); // Build the mount flags if (flags & NTFS_READ_ONLY) vd->flags |= NTFS_MNT_RDONLY; if (flags & NTFS_RECOVER) vd->flags |= NTFS_MNT_RECOVER; if (flags & NTFS_IGNORE_HIBERFILE) vd->flags |= NTFS_MNT_IGNORE_HIBERFILE; if (vd->flags & NTFS_MNT_RDONLY) ntfs_log_debug("Mounting \"%s\" as read-only\n", name); // Mount the device //Print(L"Invoking ntfs_device_mount\n"); vd->vol = ntfs_device_mount(vd->dev, vd->flags); if (!vd->vol) { switch(ntfs_volume_error(errno)) { case NTFS_VOLUME_NOT_NTFS: errno = EINVALPART; break; case NTFS_VOLUME_CORRUPT: errno = EINVALPART; break; case NTFS_VOLUME_HIBERNATED: errno = EHIBERNATED; break; case NTFS_VOLUME_UNCLEAN_UNMOUNT: errno = EDIRTY; break; default: errno = EINVAL; break; } ntfs_device_free(vd->dev); ntfs_free(vd); //Print(L"ntfsMount ntfs_device_mount FAILED (%x)\n", errno); return NULL; } if (flags & NTFS_IGNORE_CASE) ntfs_set_ignore_case(vd->vol); // Initialise the volume descriptor if (ntfsInitVolume(vd)) { ntfs_umount(vd->vol, true); ntfs_free(vd); //Print(L"ntfsMount ntfsInitVolume failed\n"); return NULL; } // Add the device to the devoptab table if (ntfsAddDevice(name, vd)) { //Print(L"ntfsMount ntfsAddDevice failed\n"); ntfsDeinitVolume(vd); ntfs_umount(vd->vol, true); ntfs_free(vd); return NULL; } //Print(L"ntfsMount done.\n"); return vd; }
static int ntfs_device_get_geo(struct ntfs_device *dev) { int err; if (!dev) { errno = EINVAL; return -1; } err = EOPNOTSUPP; #ifdef ENABLE_HD { hd_data_t *hddata; hd_t *hd, *devlist, *partlist = NULL; str_list_t *names; hd_res_t *res; const int d_name_len = strlen(dev->d_name) + 1; int done = 0; hddata = calloc(1, sizeof(*hddata)); if (!hddata) { err = ENOMEM; goto skip_hd; } /* List all "disk" class devices on the system. */ devlist = hd_list(hddata, hw_disk, 1, NULL); if (!devlist) { free(hddata); err = ENOMEM; goto skip_hd; } /* * Loop over each disk device looking for the device with the * same unix name as @dev. */ for (hd = devlist; hd; hd = hd->next) { if (hd->unix_dev_name && !strncmp(dev->d_name, hd->unix_dev_name, d_name_len)) goto got_hd; if (hd->unix_dev_name2 && !strncmp(dev->d_name, hd->unix_dev_name2, d_name_len)) goto got_hd; for (names = hd->unix_dev_names; names; names = names->next) { if (names->str && !strncmp(dev->d_name, names->str, d_name_len)) goto got_hd; } } /* * Device was not a whole disk device. Unless it is a file it * is likely to be a partition device. List all "partition" * class devices on the system. */ partlist = hd_list(hddata, hw_partition, 1, NULL); for (hd = partlist; hd; hd = hd->next) { if (hd->unix_dev_name && !strncmp(dev->d_name, hd->unix_dev_name, d_name_len)) goto got_part_hd; if (hd->unix_dev_name2 && !strncmp(dev->d_name, hd->unix_dev_name2, d_name_len)) goto got_part_hd; for (names = hd->unix_dev_names; names; names = names->next) { if (names->str && !strncmp(dev->d_name, names->str, d_name_len)) goto got_part_hd; } } /* Failed to find the device. Stop trying and clean up. */ goto end_hd; got_part_hd: /* Get the whole block device the partition device is on. */ hd = hd_get_device_by_idx(hddata, hd->attached_to); if (!hd) goto end_hd; got_hd: /* * @hd is now the whole block device either being formatted or * that the partition being formatted is on. * * Loop over each resource of the disk device looking for the * BIOS legacy geometry obtained from EDD which is what Windows * needs to boot. */ for (res = hd->res; res; res = res->next) { /* geotype 3 is BIOS legacy. */ if (res->any.type != res_disk_geo || res->disk_geo.geotype != 3) continue; dev->d_heads = res->disk_geo.heads; dev->d_sectors_per_track = res->disk_geo.sectors; done = 1; } end_hd: if (partlist) hd_free_hd_list(partlist); hd_free_hd_list(devlist); hd_free_hd_data(hddata); free(hddata); if (done) { ntfs_log_debug("EDD/BIOD legacy heads = %u, sectors " "per track = %u\n", dev->d_heads, dev->d_sectors_per_track); return 0; } } skip_hd: #endif #ifdef HDIO_GETGEO { struct hd_geometry geo; if (!dev->d_ops->ioctl(dev, HDIO_GETGEO, &geo)) { dev->d_heads = geo.heads; dev->d_sectors_per_track = geo.sectors; ntfs_log_debug("HDIO_GETGEO heads = %u, sectors per " "track = %u\n", dev->d_heads, dev->d_sectors_per_track); return 0; } err = errno; } #endif errno = err; return -1; }
/** * ntfs_fek_import_from_raw */ static ntfs_fek *ntfs_fek_import_from_raw(u8 *fek_buf, unsigned fek_size) { ntfs_fek *fek; u32 key_size, wanted_key_size, gcry_algo; int gcry_mode; gcry_error_t err; ntfs_desx_ctx *ctx; key_size = le32_to_cpup(fek_buf); ntfs_log_debug("key_size 0x%x\n", key_size); if (key_size + 16 > fek_size) { ntfs_log_debug("Invalid FEK. It was probably decrypted with " "the incorrect RSA key."); errno = EINVAL; return NULL; } fek = malloc(((((sizeof(*fek) + 7) & ~7) + key_size + 7) & ~7) + sizeof(gcry_cipher_hd_t)); if (!fek) { errno = ENOMEM; return NULL; } ctx = &fek->desx_ctx; fek->alg_id = *(le32*)(fek_buf + 8); //ntfs_log_debug("alg_id 0x%x\n", le32_to_cpu(fek->alg_id)); fek->key_data = (u8*)fek + ((sizeof(*fek) + 7) & ~7); memcpy(fek->key_data, fek_buf + 16, key_size); fek->des_gcry_cipher_hd_ptr = NULL; *(gcry_cipher_hd_t***)(fek->key_data + ((key_size + 7) & ~7)) = &fek->des_gcry_cipher_hd_ptr; switch (fek->alg_id) { case CALG_DESX: wanted_key_size = 16; gcry_algo = GCRY_CIPHER_DES; gcry_mode = GCRY_CIPHER_MODE_ECB; break; case CALG_3DES: wanted_key_size = 24; gcry_algo = GCRY_CIPHER_3DES; gcry_mode = GCRY_CIPHER_MODE_CBC; break; case CALG_AES_256: wanted_key_size = 32; gcry_algo = GCRY_CIPHER_AES256; gcry_mode = GCRY_CIPHER_MODE_CBC; break; default: wanted_key_size = 8; gcry_algo = GCRY_CIPHER_DES; gcry_mode = GCRY_CIPHER_MODE_CBC; if (fek->alg_id == CALG_DES) ntfs_log_error("DES is not supported at present\n"); else ntfs_log_error("Unknown crypto algorithm 0x%x\n", le32_to_cpu(fek->alg_id)); ntfs_log_error(". Please email %s and say that you saw this " "message. We will then try to implement " "support for this algorithm.\n", NTFS_DEV_LIST); err = EOPNOTSUPP; goto out; } if (key_size != wanted_key_size) { ntfs_log_error("%s key of %u bytes but needed size is %u " "bytes, assuming corrupt or incorrect key. " "Aborting.\n", gcry_cipher_algo_name(gcry_algo), (unsigned)key_size, (unsigned)wanted_key_size); err = EIO; goto out; } err = gcry_cipher_open(&fek->gcry_cipher_hd, gcry_algo, gcry_mode, 0); if (err != GPG_ERR_NO_ERROR) { ntfs_log_error("gcry_cipher_open() failed: %s\n", gcry_strerror(err)); err = EINVAL; goto out; } if (fek->alg_id == CALG_DESX) { err = ntfs_desx_key_expand(fek->key_data, (u32*)ctx->des_key, &ctx->out_whitening, &ctx->in_whitening); if (err == GPG_ERR_NO_ERROR) err = gcry_cipher_setkey(fek->gcry_cipher_hd, ctx->des_key, 8); } else { err = gcry_cipher_setkey(fek->gcry_cipher_hd, fek->key_data, key_size); } if (err != GPG_ERR_NO_ERROR) { ntfs_log_error("gcry_cipher_setkey() failed: %s\n", gcry_strerror(err)); gcry_cipher_close(fek->gcry_cipher_hd); err = EINVAL; goto out; } return fek; out: free(fek); errno = err; return NULL; }
/** * ntfs_boot_sector_is_ntfs - check if buffer contains a valid ntfs boot sector * @b: buffer containing putative boot sector to analyze * @silent: if zero, output progress messages to stderr * * Check if the buffer @b contains a valid ntfs boot sector. The buffer @b * must be at least 512 bytes in size. * * If @silent is zero, output progress messages to stderr. Otherwise, do not * output any messages (except when configured with --enable-debug in which * case warning/debug messages may be displayed). * * Return TRUE if @b contains a valid ntfs boot sector and FALSE if not. */ BOOL ntfs_boot_sector_is_ntfs(NTFS_BOOT_SECTOR *b) { u32 i; BOOL ret = FALSE; ntfs_log_debug("Beginning bootsector check.\n"); ntfs_log_debug("Checking OEMid, NTFS signature.\n"); if (b->oem_id != cpu_to_le64(0x202020205346544eULL)) { /* "NTFS " */ ntfs_log_error("NTFS signature is missing.\n"); goto not_ntfs; } ntfs_log_debug("Checking bytes per sector.\n"); if (le16_to_cpu(b->bpb.bytes_per_sector) < 256 || le16_to_cpu(b->bpb.bytes_per_sector) > 4096) { ntfs_log_error("Unexpected bytes per sector value (%d).\n", le16_to_cpu(b->bpb.bytes_per_sector)); goto not_ntfs; } ntfs_log_debug("Checking sectors per cluster.\n"); switch (b->bpb.sectors_per_cluster) { case 1: case 2: case 4: case 8: case 16: case 32: case 64: case 128: break; default: ntfs_log_error("Unexpected sectors per cluster value (%d).\n", b->bpb.sectors_per_cluster); goto not_ntfs; } ntfs_log_debug("Checking cluster size.\n"); i = (u32)le16_to_cpu(b->bpb.bytes_per_sector) * b->bpb.sectors_per_cluster; if (i > 65536) { ntfs_log_error("Unexpected cluster size (%d).\n", i); goto not_ntfs; } ntfs_log_debug("Checking reserved fields are zero.\n"); if (le16_to_cpu(b->bpb.reserved_sectors) || le16_to_cpu(b->bpb.root_entries) || le16_to_cpu(b->bpb.sectors) || le16_to_cpu(b->bpb.sectors_per_fat) || le32_to_cpu(b->bpb.large_sectors) || b->bpb.fats) { ntfs_log_error("Reserved fields aren't zero " "(%d, %d, %d, %d, %d, %d).\n", le16_to_cpu(b->bpb.reserved_sectors), le16_to_cpu(b->bpb.root_entries), le16_to_cpu(b->bpb.sectors), le16_to_cpu(b->bpb.sectors_per_fat), le32_to_cpu(b->bpb.large_sectors), b->bpb.fats); goto not_ntfs; } ntfs_log_debug("Checking clusters per mft record.\n"); if ((u8)b->clusters_per_mft_record < 0xe1 || (u8)b->clusters_per_mft_record > 0xf7) { switch (b->clusters_per_mft_record) { case 1: case 2: case 4: case 8: case 0x10: case 0x20: case 0x40: break; default: ntfs_log_error("Unexpected clusters per mft record " "(%d).\n", b->clusters_per_mft_record); goto not_ntfs; } } ntfs_log_debug("Checking clusters per index block.\n"); if ((u8)b->clusters_per_index_record < 0xe1 || (u8)b->clusters_per_index_record > 0xf7) { switch (b->clusters_per_index_record) { case 1: case 2: case 4: case 8: case 0x10: case 0x20: case 0x40: break; default: ntfs_log_error("Unexpected clusters per index record " "(%d).\n", b->clusters_per_index_record); goto not_ntfs; } } if (b->end_of_sector_marker != cpu_to_le16(0xaa55)) ntfs_log_debug("Warning: Bootsector has invalid end of sector " "marker.\n"); ntfs_log_debug("Bootsector check completed successfully.\n"); ret = TRUE; not_ntfs: return ret; }
/** * ntfs_df_array_fek_get */ static ntfs_fek *ntfs_df_array_fek_get(EFS_DF_ARRAY_HEADER *df_array, ntfs_rsa_private_key rsa_key, char *thumbprint, int thumbprint_size) { EFS_DF_HEADER *df_header; EFS_DF_CREDENTIAL_HEADER *df_cred; EFS_DF_CERT_THUMBPRINT_HEADER *df_cert; u8 *fek_buf; ntfs_fek *fek; u32 df_count, fek_size; unsigned i; df_count = le32_to_cpu(df_array->df_count); if (!df_count) ntfs_log_error("There are no elements in the DF array.\n"); df_header = (EFS_DF_HEADER*)(df_array + 1); for (i = 0; i < df_count; i++, df_header = (EFS_DF_HEADER*)( (u8*)df_header + le32_to_cpu(df_header->df_length))) { df_cred = (EFS_DF_CREDENTIAL_HEADER*)((u8*)df_header + le32_to_cpu(df_header->cred_header_offset)); if (df_cred->type != NTFS_CRED_TYPE_CERT_THUMBPRINT) { ntfs_log_debug("Credential type is not certificate " "thumbprint, skipping DF entry.\n"); continue; } df_cert = (EFS_DF_CERT_THUMBPRINT_HEADER*)((u8*)df_cred + le32_to_cpu( df_cred->cert_thumbprint_header_offset)); if ((int)le32_to_cpu(df_cert->thumbprint_size) != thumbprint_size) { ntfs_log_error("Thumbprint size %d is not valid " "(should be %d), skipping this DF " "entry.\n", le32_to_cpu(df_cert->thumbprint_size), thumbprint_size); continue; } if (memcmp((u8*)df_cert + le32_to_cpu(df_cert->thumbprint_offset), thumbprint, thumbprint_size)) { ntfs_log_debug("Thumbprints do not match, skipping " "this DF entry.\n"); continue; } /* * The thumbprints match so this is probably the DF entry * matching the RSA key. Try to decrypt the FEK with it. */ fek_size = le32_to_cpu(df_header->fek_size); fek_buf = (u8*)df_header + le32_to_cpu(df_header->fek_offset); /* Decrypt the FEK. Note: This is done in place. */ fek_size = ntfs_raw_fek_decrypt(fek_buf, fek_size, rsa_key); if (fek_size) { /* Convert the FEK to our internal format. */ fek = ntfs_fek_import_from_raw(fek_buf, fek_size); if (fek) return fek; ntfs_log_error("Failed to convert the decrypted file " "encryption key to internal format.\n"); } else ntfs_log_error("Failed to decrypt the file " "encryption key.\n"); } return NULL; }
/** * ntfs_ucstombs - convert a little endian Unicode string to a multibyte string * @ins: input Unicode string buffer * @ins_len: length of input string in Unicode characters * @outs: on return contains the (allocated) output multibyte string * @outs_len: length of output buffer in bytes * * Convert the input little endian, 2-byte Unicode string @ins, of length * @ins_len into the multibyte string format dictated by the current locale. * * If *@outs is NULL, the function allocates the string and the caller is * responsible for calling free(*@outs); when finished with it. * * On success the function returns the number of bytes written to the output * string *@outs (>= 0), not counting the terminating NULL byte. If the output * string buffer was allocated, *@outs is set to it. * * On error, -1 is returned, and errno is set to the error code. The following * error codes can be expected: * EINVAL Invalid arguments (e.g. @ins or @outs is NULL). * EILSEQ The input string cannot be represented as a multibyte * sequence according to the current locale. * ENAMETOOLONG Destination buffer is too small for input string. * ENOMEM Not enough memory to allocate destination buffer. */ int ntfs_ucstombs(const ntfschar *ins, const int ins_len, char **outs, int outs_len) { char *mbs; wchar_t wc; int i, o, mbs_len; int cnt = 0; #ifdef HAVE_MBSINIT mbstate_t mbstate; #endif if (!ins || !outs) { errno = EINVAL; return -1; } mbs = *outs; mbs_len = outs_len; if (mbs && !mbs_len) { errno = ENAMETOOLONG; return -1; } if (!mbs) { mbs_len = (ins_len + 1) * MB_CUR_MAX; mbs = (char*)malloc(mbs_len); if (!mbs) return -1; } #ifdef HAVE_MBSINIT memset(&mbstate, 0, sizeof(mbstate)); #else wctomb(NULL, 0); #endif for (i = o = 0; i < ins_len; i++) { /* Reallocate memory if necessary or abort. */ if ((int)(o + MB_CUR_MAX) > mbs_len) { char *tc; if (mbs == *outs) { errno = ENAMETOOLONG; return -1; } tc = (char*)malloc((mbs_len + 64) & ~63); if (!tc) goto err_out; memcpy(tc, mbs, mbs_len); mbs_len = (mbs_len + 64) & ~63; free(mbs); mbs = tc; } /* Convert the LE Unicode character to a CPU wide character. */ wc = (wchar_t)le16_to_cpu(ins[i]); if (!wc) break; /* Convert the CPU endian wide character to multibyte. */ #ifdef HAVE_MBSINIT cnt = wcrtomb(mbs + o, wc, &mbstate); #else cnt = wctomb(mbs + o, wc); #endif if (cnt == -1) goto err_out; if (cnt <= 0) { ntfs_log_debug("Eeek. cnt <= 0, cnt = %i\n", cnt); errno = EINVAL; goto err_out; } o += cnt; } #ifdef HAVE_MBSINIT /* Make sure we are back in the initial state. */ if (!mbsinit(&mbstate)) { ntfs_log_debug("Eeek. mbstate not in initial state!\n"); errno = EILSEQ; goto err_out; } #endif /* Now write the NULL character. */ mbs[o] = '\0'; if (*outs != mbs) *outs = mbs; return o; err_out: if (mbs != *outs) { int eo = errno; free(mbs); errno = eo; } return -1; }
/* * ntfs_names_full_collate() fully collate two Unicode names * * @name1: first Unicode name to compare * @name1_len: length of first Unicode name to compare * @name2: second Unicode name to compare * @name2_len: length of second Unicode name to compare * @ic: either CASE_SENSITIVE or IGNORE_CASE * @upcase: upcase table (ignored if @ic is CASE_SENSITIVE) * @upcase_len: upcase table size (ignored if @ic is CASE_SENSITIVE) * * -1 if the first name collates before the second one, * 0 if the names match, * 1 if the second name collates before the first one, or * */ int ntfs_names_full_collate(const ntfschar *name1, const u32 name1_len, const ntfschar *name2, const u32 name2_len, const IGNORE_CASE_BOOL ic, const ntfschar *upcase, const u32 upcase_len) { u32 cnt; u16 c1, c2; u16 u1, u2; #ifdef DEBUG if (!name1 || !name2 || (ic && (!upcase || !upcase_len))) { ntfs_log_debug("ntfs_names_collate received NULL pointer!\n"); exit(1); } #endif cnt = min(name1_len, name2_len); if (cnt > 0) { if (ic == CASE_SENSITIVE) { while (--cnt && (*name1 == *name2)) { name1++; name2++; } u1 = c1 = le16_to_cpu(*name1); u2 = c2 = le16_to_cpu(*name2); if (u1 < upcase_len) u1 = le16_to_cpu(upcase[u1]); if (u2 < upcase_len) u2 = le16_to_cpu(upcase[u2]); if ((u1 == u2) && cnt) do { name1++; u1 = le16_to_cpu(*name1); name2++; u2 = le16_to_cpu(*name2); if (u1 < upcase_len) u1 = le16_to_cpu(upcase[u1]); if (u2 < upcase_len) u2 = le16_to_cpu(upcase[u2]); } while ((u1 == u2) && --cnt); if (u1 < u2) return -1; if (u1 > u2) return 1; if (name1_len < name2_len) return -1; if (name1_len > name2_len) return 1; if (c1 < c2) return -1; if (c1 > c2) return 1; } else { do { u1 = c1 = le16_to_cpu(*name1); name1++; u2 = c2 = le16_to_cpu(*name2); name2++; if (u1 < upcase_len) u1 = le16_to_cpu(upcase[u1]); if (u2 < upcase_len) u2 = le16_to_cpu(upcase[u2]); } while ((u1 == u2) && --cnt); if (u1 < u2) return -1; if (u1 > u2) return 1; if (name1_len < name2_len) return -1; if (name1_len > name2_len) return 1; } } else { if (name1_len < name2_len) return -1; if (name1_len > name2_len) return 1; } return 0; }
bool ntfsMount (const char *name, const DISC_INTERFACE *interface, sec_t startSector, u32 cachePageCount, u32 cachePageSize, u32 flags) { ntfs_vd *vd = NULL; gekko_fd *fd = NULL; // Sanity check if (!name || !interface) { errno = EINVAL; return -1; } // Initialise ntfs-3g ntfsInit(); // Check that the requested mount name is free if (ntfsGetDevice(name, false)) { errno = EADDRINUSE; return false; } // Check that we can at least read from this device if (!(interface->features & FEATURE_MEDIUM_CANREAD)) { errno = EPERM; return false; } // Allocate the volume descriptor vd = (ntfs_vd*)ntfs_alloc(sizeof(ntfs_vd)); if (!vd) { errno = ENOMEM; return false; } // Setup the volume descriptor vd->id = interface->ioType; vd->flags = 0; vd->uid = 0; vd->gid = 0; vd->fmask = 0; vd->dmask = 0; vd->atime = ((flags & NTFS_UPDATE_ACCESS_TIMES) ? ATIME_ENABLED : ATIME_DISABLED); vd->showHiddenFiles = (flags & NTFS_SHOW_HIDDEN_FILES); vd->showSystemFiles = (flags & NTFS_SHOW_SYSTEM_FILES); // Allocate the device driver descriptor fd = (gekko_fd*)ntfs_alloc(sizeof(gekko_fd)); if (!fd) { ntfs_free(vd); errno = ENOMEM; return false; } // Setup the device driver descriptor fd->interface = interface; fd->startSector = startSector; fd->sectorSize = 0; fd->sectorCount = 0; fd->cachePageCount = cachePageCount; fd->cachePageSize = cachePageSize; // Allocate the device driver vd->dev = ntfs_device_alloc(name, 0, &ntfs_device_gekko_io_ops, fd); if (!vd->dev) { ntfs_free(fd); ntfs_free(vd); return false; } // Build the mount flags if (flags & NTFS_READ_ONLY) vd->flags |= MS_RDONLY; else { if (!(interface->features & FEATURE_MEDIUM_CANWRITE)) vd->flags |= MS_RDONLY; if ((interface->features & FEATURE_MEDIUM_CANREAD) && (interface->features & FEATURE_MEDIUM_CANWRITE)) vd->flags |= MS_EXCLUSIVE; } if (flags & NTFS_RECOVER) vd->flags |= MS_RECOVER; if (flags & NTFS_IGNORE_HIBERFILE) vd->flags |= MS_IGNORE_HIBERFILE; if (vd->flags & MS_RDONLY) ntfs_log_debug("Mounting \"%s\" as read-only\n", name); // Mount the device vd->vol = ntfs_device_mount(vd->dev, vd->flags); if (!vd->vol) { switch(ntfs_volume_error(errno)) { case NTFS_VOLUME_NOT_NTFS: errno = EINVALPART; break; case NTFS_VOLUME_CORRUPT: errno = EINVALPART; break; case NTFS_VOLUME_HIBERNATED: errno = EHIBERNATED; break; case NTFS_VOLUME_UNCLEAN_UNMOUNT: errno = EDIRTY; break; default: errno = EINVAL; break; } ntfs_device_free(vd->dev); ntfs_free(vd); return false; } // Initialise the volume descriptor if (ntfsInitVolume(vd)) { ntfs_umount(vd->vol, true); ntfs_free(vd); return false; } // Add the device to the devoptab table if (ntfsAddDevice(name, vd)) { ntfsDeinitVolume(vd); ntfs_umount(vd->vol, true); ntfs_free(vd); return false; } return true; }
int ntfsFindPartitions (const DISC_INTERFACE *interface, sec_t **partitions) { MASTER_BOOT_RECORD mbr; PARTITION_RECORD *partition = NULL; sec_t partition_starts[NTFS_MAX_PARTITIONS] = {0}; int partition_count = 0; sec_t part_lba = 0; int i; union { u8 buffer[BYTES_PER_SECTOR]; MASTER_BOOT_RECORD mbr; EXTENDED_BOOT_RECORD ebr; NTFS_BOOT_SECTOR boot; } sector; // Sanity check if (!interface) { errno = EINVAL; return -1; } if (!partitions) return 0; // Initialise ntfs-3g ntfsInit(); // Start the device and check that it is inserted if (!interface->startup()) { errno = EIO; return -1; } if (!interface->isInserted()) { return 0; } // Read the first sector on the device if (!interface->readSectors(0, 1, §or.buffer)) { errno = EIO; return -1; } // If this is the devices master boot record if (sector.mbr.signature == MBR_SIGNATURE) { memcpy(&mbr, §or, sizeof(MASTER_BOOT_RECORD)); ntfs_log_debug("Valid Master Boot Record found\n"); // Search the partition table for all NTFS partitions (max. 4 primary partitions) for (i = 0; i < 4; i++) { partition = &mbr.partitions[i]; part_lba = le32_to_cpu(mbr.partitions[i].lba_start); ntfs_log_debug("Partition %i: %s, sector %d, type 0x%x\n", i + 1, partition->status == PARTITION_STATUS_BOOTABLE ? "bootable (active)" : "non-bootable", part_lba, partition->type); // Figure out what type of partition this is switch (partition->type) { // Ignore empty partitions case PARTITION_TYPE_EMPTY: continue; // NTFS partition case PARTITION_TYPE_NTFS: { ntfs_log_debug("Partition %i: Claims to be NTFS\n", i + 1); // Read and validate the NTFS partition if (interface->readSectors(part_lba, 1, §or)) { if (sector.boot.oem_id == NTFS_OEM_ID) { ntfs_log_debug("Partition %i: Valid NTFS boot sector found\n", i + 1); if (partition_count < NTFS_MAX_PARTITIONS) { partition_starts[partition_count] = part_lba; partition_count++; } } else { ntfs_log_debug("Partition %i: Invalid NTFS boot sector, not actually NTFS\n", i + 1); } } break; } // DOS 3.3+ or Windows 95 extended partition case PARTITION_TYPE_DOS33_EXTENDED: case PARTITION_TYPE_WIN95_EXTENDED: { ntfs_log_debug("Partition %i: Claims to be Extended\n", i + 1); // Walk the extended partition chain, finding all NTFS partitions within it sec_t ebr_lba = part_lba; sec_t next_erb_lba = 0; do { // Read and validate the extended boot record if (interface->readSectors(ebr_lba + next_erb_lba, 1, §or)) { if (sector.ebr.signature == EBR_SIGNATURE) { ntfs_log_debug("Logical Partition @ %d: type 0x%x\n", ebr_lba + next_erb_lba, sector.ebr.partition.status == PARTITION_STATUS_BOOTABLE ? "bootable (active)" : "non-bootable", sector.ebr.partition.type); // Get the start sector of the current partition // and the next extended boot record in the chain part_lba = ebr_lba + next_erb_lba + le32_to_cpu(sector.ebr.partition.lba_start); next_erb_lba = le32_to_cpu(sector.ebr.next_ebr.lba_start); // Check if this partition has a valid NTFS boot record if (interface->readSectors(part_lba, 1, §or)) { if (sector.boot.oem_id == NTFS_OEM_ID) { ntfs_log_debug("Logical Partition @ %d: Valid NTFS boot sector found\n", part_lba); if(sector.ebr.partition.type != PARTITION_TYPE_NTFS) { ntfs_log_warning("Logical Partition @ %d: Is NTFS but type is 0x%x; 0x%x was expected\n", part_lba, sector.ebr.partition.type, PARTITION_TYPE_NTFS); } if (partition_count < NTFS_MAX_PARTITIONS) { partition_starts[partition_count] = part_lba; partition_count++; } } } } else { next_erb_lba = 0; } } } while (next_erb_lba); break; } // Unknown or unsupported partition type default: { // Check if this partition has a valid NTFS boot record anyway, // it might be misrepresented due to a lazy partition editor if (interface->readSectors(part_lba, 1, §or)) { if (sector.boot.oem_id == NTFS_OEM_ID) { ntfs_log_debug("Partition %i: Valid NTFS boot sector found\n", i + 1); if(partition->type != PARTITION_TYPE_NTFS) { ntfs_log_warning("Partition %i: Is NTFS but type is 0x%x; 0x%x was expected\n", i + 1, partition->type, PARTITION_TYPE_NTFS); } if (partition_count < NTFS_MAX_PARTITIONS) { partition_starts[partition_count] = part_lba; partition_count++; } } } break; } } } // Else it is assumed this device has no master boot record } else { ntfs_log_debug("No Master Boot Record was found!\n"); // As a last-ditched effort, search the first 64 sectors of the device for stray NTFS partitions for (i = 0; i < 64; i++) { if (interface->readSectors(i, 1, §or)) { if (sector.boot.oem_id == NTFS_OEM_ID) { ntfs_log_debug("Valid NTFS boot sector found at sector %d!\n", i); if (partition_count < NTFS_MAX_PARTITIONS) { partition_starts[partition_count] = i; partition_count++; } } } } } // Shutdown the device /*interface->shutdown();*/ // Return the found partitions (if any) if (partition_count > 0) { *partitions = (sec_t*)ntfs_alloc(sizeof(sec_t) * partition_count); if (*partitions) { memcpy(*partitions, &partition_starts, sizeof(sec_t) * partition_count); return partition_count; } } return 0; }
/** * ntfs_boot_sector_is_ntfs - check if buffer contains a valid ntfs boot sector * @b: buffer containing putative boot sector to analyze * @silent: if zero, output progress messages to stderr * * Check if the buffer @b contains a valid ntfs boot sector. The buffer @b * must be at least 512 bytes in size. * * If @silent is zero, output progress messages to stderr. Otherwise, do not * output any messages (except when configured with --enable-debug in which * case warning/debug messages may be displayed). * * Return TRUE if @b contains a valid ntfs boot sector and FALSE if not. */ BOOL ntfs_boot_sector_is_ntfs(NTFS_BOOT_SECTOR *b, const BOOL silent __attribute__((unused))) { u32 i; ntfs_log_debug("\nBeginning bootsector check...\n"); /* Calculate the checksum. Note, this is just a simple addition of all u32 values in the bootsector starting at the beginning and finishing at the offset of the checksum itself (i.e. not including the checksum...). */ if ((void*)b < (void*)&b->checksum) { u32 *u = (u32 *)b; u32 *bi = (u32 *)(&b->checksum); ntfs_log_debug("Calculating bootsector checksum... "); for (i = 0; u < bi; ++u) i += le32_to_cpup(u); if (le32_to_cpu(b->checksum) && le32_to_cpu(b->checksum) != i) goto not_ntfs; ntfs_log_debug("OK\n"); } /* Check OEMidentifier is "NTFS " */ ntfs_log_debug("Checking OEMid... "); if (b->oem_id != cpu_to_le64(0x202020205346544eULL)) /* "NTFS " */ goto not_ntfs; ntfs_log_debug("OK\n"); /* Check bytes per sector value is between 256 and 4096. */ ntfs_log_debug("Checking bytes per sector... "); if (le16_to_cpu(b->bpb.bytes_per_sector) < 0x100 || le16_to_cpu(b->bpb.bytes_per_sector) > 0x1000) goto not_ntfs; ntfs_log_debug("OK\n"); /* Check sectors per cluster value is valid. */ ntfs_log_debug("Checking sectors per cluster... "); switch (b->bpb.sectors_per_cluster) { case 1: case 2: case 4: case 8: case 16: case 32: case 64: case 128: break; default: goto not_ntfs; } ntfs_log_debug("OK\n"); /* Check the cluster size is not above 65536 bytes. */ ntfs_log_debug("Checking cluster size... "); if ((u32)le16_to_cpu(b->bpb.bytes_per_sector) * b->bpb.sectors_per_cluster > 0x10000) goto not_ntfs; ntfs_log_debug("OK\n"); /* Check reserved/unused fields are really zero. */ ntfs_log_debug("Checking reserved fields are zero... "); if (le16_to_cpu(b->bpb.reserved_sectors) || le16_to_cpu(b->bpb.root_entries) || le16_to_cpu(b->bpb.sectors) || le16_to_cpu(b->bpb.sectors_per_fat) || le32_to_cpu(b->bpb.large_sectors) || b->bpb.fats) goto not_ntfs; ntfs_log_debug("OK\n"); /* Check clusters per file mft record value is valid. */ ntfs_log_debug("Checking clusters per mft record... "); if ((u8)b->clusters_per_mft_record < 0xe1 || (u8)b->clusters_per_mft_record > 0xf7) { switch (b->clusters_per_mft_record) { case 1: case 2: case 4: case 8: case 0x10: case 0x20: case 0x40: break; default: goto not_ntfs; } } ntfs_log_debug("OK\n"); /* Check clusters per index block value is valid. */ ntfs_log_debug("Checking clusters per index block... "); if ((u8)b->clusters_per_index_record < 0xe1 || (u8)b->clusters_per_index_record > 0xf7) { switch (b->clusters_per_index_record) { case 1: case 2: case 4: case 8: case 0x10: case 0x20: case 0x40: break; default: goto not_ntfs; } } ntfs_log_debug("OK\n"); if (b->end_of_sector_marker != cpu_to_le16(0xaa55)) ntfs_log_debug("Warning: Bootsector has invalid end of sector marker.\n"); ntfs_log_debug("Bootsector check completed successfully.\n"); return TRUE; not_ntfs: ntfs_log_debug("FAILED\n"); ntfs_log_debug("Bootsector check failed. Aborting...\n"); return FALSE; }