📄 volume.c
字号:
ntfs_log_debug("Loading $Volume... "); vol->vol_ni = ntfs_inode_open(vol, FILE_Volume); if (!vol->vol_ni) { ntfs_log_debug(FAILED); ntfs_log_perror("Failed to open inode"); goto error_exit; } /* Get a search context for the $Volume/$VOLUME_INFORMATION lookup. */ ctx = ntfs_attr_get_search_ctx(vol->vol_ni, NULL); if (!ctx) { ntfs_log_debug(FAILED); ntfs_log_perror("Failed to allocate attribute search context"); goto error_exit; } /* Find the $VOLUME_INFORMATION attribute. */ if (ntfs_attr_lookup(AT_VOLUME_INFORMATION, AT_UNNAMED, 0, 0, 0, NULL, 0, ctx)) { ntfs_log_debug(FAILED); ntfs_log_debug("$VOLUME_INFORMATION attribute not found in " "$Volume?!?\n"); goto error_exit; } a = ctx->attr; /* Has to be resident. */ if (a->non_resident) { ntfs_log_debug(FAILED); ntfs_log_debug("Error: Attribute $VOLUME_INFORMATION must be " "resident (and it isn't)!\n"); errno = EIO; goto error_exit; } /* Get a pointer to the value of the attribute. */ vinf = (VOLUME_INFORMATION*)(le16_to_cpu(a->value_offset) + (char*)a); /* Sanity checks. */ if ((char*)vinf + le32_to_cpu(a->value_length) > (char*)ctx->mrec + le32_to_cpu(ctx->mrec->bytes_in_use) || le16_to_cpu(a->value_offset) + le32_to_cpu( a->value_length) > le32_to_cpu(a->length)) { ntfs_log_debug(FAILED); ntfs_log_debug("Error: Attribute $VOLUME_INFORMATION in $Volume is " "corrupt!\n"); errno = EIO; goto error_exit; } /* Setup vol from the volume information attribute value. */ vol->major_ver = vinf->major_ver; vol->minor_ver = vinf->minor_ver; /* Do not use le16_to_cpu() macro here as our VOLUME_FLAGS are defined using cpu_to_le16() macro and hence are consistent. */ vol->flags = vinf->flags; /* * Reinitialize the search context for the $Volume/$VOLUME_NAME lookup. */ ntfs_attr_reinit_search_ctx(ctx); if (ntfs_attr_lookup(AT_VOLUME_NAME, AT_UNNAMED, 0, 0, 0, NULL, 0, ctx)) { if (errno != ENOENT) { ntfs_log_debug(FAILED); ntfs_log_debug("Error: Lookup of $VOLUME_NAME attribute in " "$Volume failed. This probably means " "something is corrupt. Run chkdsk.\n"); goto error_exit; } /* * Attribute not present. This has been seen in the field. * Treat this the same way as if the attribute was present but * had zero length. */ vol->vol_name = malloc(1); if (!vol->vol_name) { ntfs_log_debug(FAILED); ntfs_log_debug("Error: Unable to allocate memory for volume " "name!\n"); goto error_exit; } vol->vol_name[0] = '\0'; } else { a = ctx->attr; /* Has to be resident. */ if (a->non_resident) { ntfs_log_debug(FAILED); ntfs_log_debug("Error: Attribute $VOLUME_NAME must be " "resident!\n"); errno = EIO; goto error_exit; } /* Get a pointer to the value of the attribute. */ vname = (ntfschar*)(le16_to_cpu(a->value_offset) + (char*)a); u = le32_to_cpu(a->value_length) / 2; /* * Convert Unicode volume name to current locale multibyte * format. */ vol->vol_name = NULL; if (ntfs_ucstombs(vname, u, &vol->vol_name, 0) == -1) { ntfs_log_perror("Error: Volume name could not be converted " "to current locale"); ntfs_log_debug("Forcing name into ASCII by replacing " "non-ASCII characters with underscores.\n"); vol->vol_name = malloc(u + 1); if (!vol->vol_name) { ntfs_log_debug(FAILED); ntfs_log_debug("Error: Unable to allocate memory for " "volume name!\n"); goto error_exit; } for (j = 0; j < (s32)u; j++) { ntfschar uc = le16_to_cpu(vname[j]); if (uc > 0xff) uc = (ntfschar)'_'; vol->vol_name[j] = (char)uc; } vol->vol_name[u] = '\0'; } } ntfs_log_debug(OK); ntfs_attr_put_search_ctx(ctx); ctx = NULL; /* Now load the attribute definitions from $AttrDef. */ ntfs_log_debug("Loading $AttrDef... "); ni = ntfs_inode_open(vol, FILE_AttrDef); if (!ni) { ntfs_log_debug(FAILED); ntfs_log_perror("Failed to open inode"); goto error_exit; } /* Get an ntfs attribute for $AttrDef/$DATA. */ na = ntfs_attr_open(ni, AT_DATA, AT_UNNAMED, 0); if (!na) { ntfs_log_debug(FAILED); ntfs_log_perror("Failed to open ntfs attribute"); goto error_exit; } /* Check we don't overflow 32-bits. */ if (na->data_size > 0xffffffffLL) { ntfs_log_debug(FAILED); ntfs_log_debug("Error: Attribute definition table is too big (max " "32-bit allowed).\n"); errno = EINVAL; goto error_exit; } vol->attrdef_len = na->data_size; vol->attrdef = (ATTR_DEF*)malloc(na->data_size); if (!vol->attrdef) { ntfs_log_debug(FAILED); ntfs_log_debug("Not enough memory to load $AttrDef.\n"); goto error_exit; } /* Read in the $DATA attribute value into the buffer. */ l = ntfs_attr_pread(na, 0, na->data_size, vol->attrdef); if (l != na->data_size) { ntfs_log_debug(FAILED); ntfs_log_debug("Amount of data read does not correspond to expected " "length!\n"); errno = EIO; goto error_exit; } /* Done with the $AttrDef mft record. */ ntfs_log_debug(OK); ntfs_attr_close(na); if (ntfs_inode_close(ni)) ntfs_log_perror("Failed to close inode, leaking memory"); /* * Check for dirty logfile and hibernated Windows. * We care only about read-write mounts. */ if (!(flags & MS_RDONLY)) { if (ntfs_volume_check_logfile(vol) < 0) goto error_exit; if (ntfs_volume_check_hiberfile(vol) < 0) goto error_exit; } return vol;io_error_exit: errno = EIO;error_exit: eo = errno; if (ctx) ntfs_attr_put_search_ctx(ctx); if (m) free(m); if (m2) free(m2); __ntfs_volume_release(vol); errno = eo; return NULL;}/** * ntfs_mount - open ntfs volume * @name: name of device/file to open * @flags: optional mount flags * * This function mounts an ntfs volume. @name should contain the name of the * device/file to mount as the ntfs volume. * * @flags is an optional second parameter. The same flags are used as for * the mount system call (man 2 mount). Currently only the following flags * are implemented: * MS_RDONLY - mount volume read-only * MS_NOATIME - do not update access time * * The function opens the device or file @name and verifies that it contains a * valid bootsector. Then, it allocates an ntfs_volume structure and initializes * some of the values inside the structure from the information stored in the * bootsector. It proceeds to load the necessary system files and completes * setting up the structure. * * Return the allocated volume structure on success and NULL on error with * errno set to the error code. * * Note, that a copy is made of @name, and hence it can be discarded as * soon as the function returns. */ntfs_volume *ntfs_mount(const char *name __attribute__((unused)), unsigned long flags __attribute__((unused))){#ifndef NO_NTFS_DEVICE_DEFAULT_IO_OPS struct ntfs_device *dev; ntfs_volume *vol; /* Allocate an ntfs_device structure. */ dev = ntfs_device_alloc(name, 0, &ntfs_device_default_io_ops, NULL); if (!dev) return NULL; /* Call ntfs_device_mount() to do the actual mount. */ vol = ntfs_device_mount(dev, flags); if (!vol) { int eo = errno; ntfs_device_free(dev); errno = eo; } return vol;#else /* * ntfs_mount() makes no sense if NO_NTFS_DEVICE_DEFAULT_IO_OPS is * defined as there are no device operations available in libntfs in * this case. */ errno = EOPNOTSUPP; return NULL;#endif}#ifndef __VISOPSYS__/** * ntfs_device_umount - close ntfs volume * @vol: address of ntfs_volume structure of volume to close * @force: if true force close the volume even if it is busy * * Deallocate all structures (including @vol itself) associated with the ntfs * volume @vol. * * Note it is up to the caller to destroy the device associated with the volume * being unmounted after this function returns. * * Return 0 on success. On error return -1 with errno set appropriately * (most likely to one of EAGAIN, EBUSY or EINVAL). The EAGAIN error means that * an operation is in progress and if you try the close later the operation * might be completed and the close succeed. * * If @force is true (i.e. not zero) this function will close the volume even * if this means that data might be lost. * * @vol must have previously been returned by a call to ntfs_device_mount(). * * @vol itself is deallocated and should no longer be dereferenced after this * function returns success. If it returns an error then nothing has been done * so it is safe to continue using @vol. */int ntfs_device_umount(ntfs_volume *vol, const BOOL force __attribute__((unused))){ if (!vol) { errno = EINVAL; return -1; } __ntfs_volume_release(vol); return 0;}/** * ntfs_umount - close ntfs volume * @vol: address of ntfs_volume structure of volume to close * @force: if true force close the volume even if it is busy * * Deallocate all structures (including @vol itself) associated with the ntfs * volume @vol. * * Return 0 on success. On error return -1 with errno set appropriately * (most likely to one of EAGAIN, EBUSY or EINVAL). The EAGAIN error means that * an operation is in progress and if you try the close later the operation * might be completed and the close succeed. * * If @force is true (i.e. not zero) this function will close the volume even * if this means that data might be lost. * * @vol must have previously been returned by a call to ntfs_mount(). * * @vol itself is deallocated and should no longer be dereferenced after this * function returns success. If it returns an error then nothing has been done * so it is safe to continue using @vol. */int ntfs_umount(ntfs_volume *vol, const BOOL force __attribute__((unused))){ struct ntfs_device *dev; if (!vol) { errno = EINVAL; return -1; } dev = vol->dev; __ntfs_volume_release(vol); ntfs_device_free(dev); return 0;}#ifdef HAVE_MNTENT_H#ifndef HAVE_REALPATH/** * realpath - If there is no realpath on the system */static char *realpath(const char *path, char *resolved_path){ strncpy(resolved_path, path, PATH_MAX); resolved_path[PATH_MAX] = '\0'; return resolved_path;}#endif/** * ntfs_mntent_check - desc * * If you are wanting to use this, you actually wanted to use * ntfs_check_if_mounted(), you just didn't realize. (-: * * See description of ntfs_check_if_mounted(), below. */static int ntfs_mntent_check(const char *file, unsigned long *mnt_flags){ struct mntent *mnt; char *real_file = NULL, *real_fsname = NULL; FILE *f; int err = 0; real_file = malloc(PATH_MAX + 1); if (!real_file) return -1; real_fsname = malloc(PATH_MAX + 1); if (!real_fsname) { err = errno; goto exit; } if (!realpath(file, real_file)) { err = errno; goto exit; } if (!(f = setmntent(MOUNTED, "r"))) { err = errno; goto exit; } while ((mnt = getmntent(f))) { if (!realpath(mnt->mnt_fsname, real_fsname)) continue; if (!strcmp(real_file, real_fsname)) break; } endmntent(f); if (!mnt) goto exit; *mnt_flags = NTFS_MF_MOUNTED; if (!strcmp(mnt->mnt_dir, "/")) *mnt_flags |= NTFS_MF_ISROOT;#ifdef HAVE_HASMNTOPT if (hasmntopt(mnt, "ro") && !hasmntopt(mnt, "rw")) *mnt_flags |= NTFS_MF_READONLY;#endifexit: free(real_file); free(real_fsname); if (err) { errno = err; return -1; } return 0;}#endif /* HAVE_MNTENT_H */#endif /* __VISOPSYS__ *//** * ntfs_check_if_mounted - check if an ntfs volume is currently mounted * @file: device file to check * @mnt_flags: pointer into which to return the ntfs mount flags (see volume.h) * * If the running system does not support the {set,get,end}mntent() calls, * just return 0 and set *@mnt_flags to zero. * * When the system does support the calls, ntfs_check_if_mounted() first tries * to find the device @file in /etc/mtab (or wherever this is kept on the * running system). If it is not found, assume the device is not mounted and * return 0 and set *@mnt_flags to zero. * * If the device @file is found, set the NTFS_MF_MOUNTED flags in *@mnt_flags. * * Further if @file is mounted as the file system root ("/"), set the flag * NTFS_MF_ISROOT in *@mnt_flags. * * Finally, check if the file system is mounted read-only, and if so set the * NTFS_MF_READONLY flag in *@mnt_flags. * * On success return 0 with *@mnt_flags set to the ntfs mount flags. * * On error return -1 with errno set to the error code. */int ntfs_check_if_mounted(const char *filenm __attribute__((unused)), unsigned long *mnt_flags){ *mnt_flags = 0;#ifdef HAVE_MNTENT_H return ntfs_mntent_check(filenm, mnt_flags);#else return 0;#endif}/** * ntfs_version_is_supported - check if NTFS version is supported. * @vol: ntfs volume whose version we're interested in. * * The function checks if the NTFS volume version is known or not. * Version 1.1 and 1.2 are used by Windows NT3.x and NT4. * Version 2.x is used by Windows 2000 Betas. * Version 3.0 is used by Windows 2000. * Version 3.1 is used by Windows XP, Windows Server 2003 and Longhorn. * * Return 0 if NTFS version is supported otherwise -1 with errno set. * * The following error codes are defined: * EOPNOTSUPP - Unknown NTFS version * EINVAL - Invalid argument */int ntfs_version_is_supported(ntfs_volume *vol){ u8 major, minor; if (!vol) { errno = EINVAL; return -1; } major = vol->major_ver; minor = vol->minor_ver; if (NTFS_V1_1(major, minor) || NTFS_V1_2(major, minor)) return 0; if (NTFS_V2_X(major, minor)) return 0; if (NTFS_V3_0(major, minor) || NTFS_V3_1(major, minor)) return 0; errno = EOPNOTSUPP; return -1;}/** * ntfs_logfile_reset - "empty" $LogFile data attribute value * @vol: ntfs volume whose $LogFile we intend to reset. * * Fill the value of the $LogFile data attribute, i.e. the contents of * the file, with 0xff's, thus marking the journal as empty. * * FIXME(?): We might need to zero the LSN field of every single mft * record as well. (But, first try without doing that and see what * happens, since chkdsk might pickup the pieces and do it for us...)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -