📄 ntfs-3g.c
字号:
/** * ntfs-3g - Third Generation NTFS Driver * * Copyright (c) 2005-2007 Yura Pakhuchiy * Copyright (c) 2005 Yuval Fledel * Copyright (c) 2006-2008 Szabolcs Szakacsits * * This file is originated from the Linux-NTFS project. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program (in the main directory of the NTFS-3G * distribution in the file COPYING); if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */#include "config.h"#include <fuse.h>#if !defined(FUSE_VERSION) || (FUSE_VERSION < 26)#error "***********************************************************"#error "* *"#error "* Compilation requires at least FUSE version 2.6.0! *"#error "* *"#error "***********************************************************"#endif#ifdef FUSE_INTERNAL#define FUSE_TYPE "integrated FUSE"#else#define FUSE_TYPE "external FUSE"#endif#ifdef HAVE_STDIO_H#include <stdio.h>#endif#ifdef HAVE_STRING_H#include <string.h>#endif#ifdef HAVE_ERRNO_H#include <errno.h>#endif#ifdef HAVE_FCNTL_H#include <fcntl.h>#endif#ifdef HAVE_UNISTD_H#include <unistd.h>#endif#ifdef HAVE_STDLIB_H#include <stdlib.h>#endif#ifdef HAVE_LOCALE_H#include <locale.h>#endif#include <signal.h>#ifdef HAVE_LIMITS_H#include <limits.h>#endif#include <getopt.h>#include <syslog.h>#include <sys/wait.h>#ifdef HAVE_SETXATTR#include <sys/xattr.h>#endif#include "attrib.h"#include "inode.h"#include "volume.h"#include "dir.h"#include "unistr.h"#include "layout.h"#include "index.h"#include "utils.h"#include "version.h"#include "ntfstime.h"#include "misc.h"#ifndef PATH_MAX#define PATH_MAX 4096#endiftypedef enum { FSTYPE_NONE, FSTYPE_UNKNOWN, FSTYPE_FUSE, FSTYPE_FUSEBLK} fuse_fstype;typedef enum { ATIME_ENABLED, ATIME_DISABLED, ATIME_RELATIVE} ntfs_atime_t;typedef struct { fuse_fill_dir_t filler; void *buf;} ntfs_fuse_fill_context_t;typedef enum { NF_STREAMS_INTERFACE_NONE, /* No access to named data streams. */ NF_STREAMS_INTERFACE_XATTR, /* Map named data streams to xattrs. */ NF_STREAMS_INTERFACE_WINDOWS, /* "file:stream" interface. */} ntfs_fuse_streams_interface;typedef struct { ntfs_volume *vol; unsigned int uid; unsigned int gid; unsigned int fmask; unsigned int dmask; ntfs_fuse_streams_interface streams; ntfs_atime_t atime; BOOL ro; BOOL show_sys_files; BOOL silent; BOOL force; BOOL hiberfile; BOOL debug; BOOL no_detach; BOOL blkdev; BOOL mounted; struct fuse_chan *fc;} ntfs_fuse_context_t;static struct options { char *mnt_point; /* Mount point */ char *options; /* Mount options */ char *device; /* Device to mount */} opts;static const char *EXEC_NAME = "ntfs-3g";static char def_opts[] = "silent,allow_other,nonempty,";static ntfs_fuse_context_t *ctx;static u32 ntfs_sequence;static const char *locale_msg ="WARNING: Couldn't set locale to '%s' thus some file names may not\n"" be correct or visible. Please see the potential solution at\n"" http://ntfs-3g.org/support.html#locale\n";static const char *usage_msg = "\n""%s %s %s %d - Third Generation NTFS Driver\n""\n""Copyright (C) 2006-2008 Szabolcs Szakacsits\n""Copyright (C) 2005-2007 Yura Pakhuchiy\n""\n""Usage: %s <device|image_file> <mount_point> [-o option[,...]]\n""\n""Options: ro (read-only mount), force, remove_hiberfile, locale=,\n" " uid=, gid=, umask=, fmask=, dmask=, streams_interface=.\n"" Please see the details in the manual.\n""\n""Example: ntfs-3g /dev/sda1 /mnt/win -o force\n""\n""%s";#ifdef FUSE_INTERNALint drop_privs(void);int restore_privs(void);#else/* * setuid and setgid root ntfs-3g denies to start with external FUSE, * therefore the below functions are no-op in such case. */static int drop_privs(void) { return 0; }static int restore_privs(void) { return 0; }static const char *setuid_msg ="Mount is denied because setuid and setgid root ntfs-3g is insecure with the\n""external FUSE library. Either remove the setuid/setgid bit from the binary\n""or rebuild NTFS-3G with integrated FUSE support and make it setuid root.\n""Please see more information at http://ntfs-3g.org/support.html#unprivileged\n";static const char *unpriv_fuseblk_msg ="Unprivileged user can not mount NTFS block devices using the external FUSE\n""library. Either mount the volume as root, or rebuild NTFS-3G with integrated\n""FUSE support and make it setuid root. Please see more information at\n""http://ntfs-3g.org/support.html#unprivileged\n";#endif /** * ntfs_fuse_is_named_data_stream - check path to be to named data stream * @path: path to check * * Returns 1 if path is to named data stream or 0 otherwise. */static int ntfs_fuse_is_named_data_stream(const char *path){ if (strchr(path, ':') && ctx->streams == NF_STREAMS_INTERFACE_WINDOWS) return 1; return 0;}static void ntfs_fuse_update_times(ntfs_inode *ni, ntfs_time_update_flags mask){ if (ctx->atime == ATIME_DISABLED) mask &= ~NTFS_UPDATE_ATIME; else if (ctx->atime == ATIME_RELATIVE && mask == NTFS_UPDATE_ATIME && ni->last_access_time >= ni->last_data_change_time && ni->last_access_time >= ni->last_mft_change_time) return; ntfs_inode_update_times(ni, mask);}static s64 ntfs_get_nr_free_mft_records(ntfs_volume *vol){ ntfs_attr *na = vol->mftbmp_na; s64 nr_free = ntfs_attr_get_free_bits(na); if (nr_free >= 0) nr_free += (na->allocated_size - na->data_size) << 3; return nr_free;}/** * ntfs_fuse_statfs - return information about mounted NTFS volume * @path: ignored (but fuse requires it) * @sfs: statfs structure in which to return the information * * Return information about the mounted NTFS volume @sb in the statfs structure * pointed to by @sfs (this is initialized with zeros before ntfs_statfs is * called). We interpret the values to be correct of the moment in time at * which we are called. Most values are variable otherwise and this isn't just * the free values but the totals as well. For example we can increase the * total number of file nodes if we run out and we can keep doing this until * there is no more space on the volume left at all. * * This code based on ntfs_statfs from ntfs kernel driver. * * Returns 0 on success or -errno on error. */static int ntfs_fuse_statfs(const char *path __attribute__((unused)), struct statvfs *sfs){ s64 size; int delta_bits; ntfs_volume *vol; vol = ctx->vol; if (!vol) return -ENODEV; /* File system block size, used for optimal transfer block size. */ sfs->f_bsize = vol->cluster_size; /* Fundamental file system block size, used as the unit. */ sfs->f_frsize = vol->cluster_size; /* * Total number of blocks on file system in units of f_frsize. * Since inodes are also stored in blocks ($MFT is a file) hence * this is the number of clusters on the volume. */ sfs->f_blocks = vol->nr_clusters; /* Free blocks available for all and for non-privileged processes. */ size = vol->free_clusters; if (size < 0) size = 0; sfs->f_bavail = sfs->f_bfree = size; /* Free inodes on the free space */ delta_bits = vol->cluster_size_bits - vol->mft_record_size_bits; if (delta_bits >= 0) size <<= delta_bits; else size >>= -delta_bits; /* Number of inodes at this point in time. */ sfs->f_files = (vol->mftbmp_na->allocated_size << 3) + size; /* Free inodes available for all and for non-privileged processes. */ size += vol->free_mft_records; if (size < 0) size = 0; sfs->f_ffree = sfs->f_favail = size; /* Maximum length of filenames. */ sfs->f_namemax = NTFS_MAX_NAME_LEN; return 0;}/** * ntfs_fuse_parse_path - split path to path and stream name. * @org_path: path to split * @path: pointer to buffer in which parsed path saved * @stream_name: pointer to buffer where stream name in unicode saved * * This function allocates buffers for @*path and @*stream, user must free them * after use. * * Return values: * <0 Error occurred, return -errno; * 0 No stream name, @*stream is not allocated and set to AT_UNNAMED. * >0 Stream name length in unicode characters. */static int ntfs_fuse_parse_path(const char *org_path, char **path, ntfschar **stream_name){ char *stream_name_mbs; int res; stream_name_mbs = strdup(org_path); if (!stream_name_mbs) return -errno; if (ctx->streams == NF_STREAMS_INTERFACE_WINDOWS) { *path = strsep(&stream_name_mbs, ":"); if (stream_name_mbs) { *stream_name = NULL; res = ntfs_mbstoucs(stream_name_mbs, stream_name); if (res < 0) return -errno; return res; } } else *path = stream_name_mbs; *stream_name = AT_UNNAMED; return 0;}static void set_fuse_error(int *err){ if (!*err) *err = -errno;}static int ntfs_fuse_getattr(const char *org_path, struct stat *stbuf){ int res = 0; ntfs_inode *ni; ntfs_attr *na; ntfs_volume *vol; char *path = NULL; ntfschar *stream_name; int stream_name_len; vol = ctx->vol; stream_name_len = ntfs_fuse_parse_path(org_path, &path, &stream_name); if (stream_name_len < 0) return stream_name_len; memset(stbuf, 0, sizeof(struct stat)); ni = ntfs_pathname_to_inode(vol, NULL, path); if (!ni) { res = -errno; goto exit; } if (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY && !stream_name_len) { /* Directory. */ stbuf->st_mode = S_IFDIR | (0777 & ~ctx->dmask); na = ntfs_attr_open(ni, AT_INDEX_ALLOCATION, NTFS_INDEX_I30, 4); if (na) { stbuf->st_size = na->data_size; stbuf->st_blocks = na->allocated_size >> 9; ntfs_attr_close(na); } stbuf->st_nlink = 1; /* Make find(1) work */ } else { /* Regular or Interix (INTX) file. */ stbuf->st_mode = S_IFREG; stbuf->st_size = ni->data_size; /* * Temporary fix to make ActiveSync work via Samba 3.0. * See more on the ntfs-3g-devel list. */ stbuf->st_blocks = (ni->allocated_size + 511) >> 9; stbuf->st_nlink = le16_to_cpu(ni->mrec->link_count); if (ni->flags & FILE_ATTR_SYSTEM || stream_name_len) { na = ntfs_attr_open(ni, AT_DATA, stream_name, stream_name_len); if (!na) { if (stream_name_len) res = -ENOENT; goto exit; } if (stream_name_len) { stbuf->st_size = na->data_size; stbuf->st_blocks = na->allocated_size >> 9; } /* Check whether it's Interix FIFO or socket. */ if (!(ni->flags & FILE_ATTR_HIDDEN) && !stream_name_len) { /* FIFO. */ if (na->data_size == 0) stbuf->st_mode = S_IFIFO; /* Socket link. */ if (na->data_size == 1) stbuf->st_mode = S_IFSOCK; } /* * Check whether it's Interix symbolic link, block or * character device. */ if (na->data_size <= sizeof(INTX_FILE_TYPES) + sizeof(ntfschar) * PATH_MAX && na->data_size > sizeof(INTX_FILE_TYPES) && !stream_name_len) { INTX_FILE *intx_file; intx_file = ntfs_malloc(na->data_size); if (!intx_file) { res = -errno; ntfs_attr_close(na); goto exit; } if (ntfs_attr_pread(na, 0, na->data_size, intx_file) != na->data_size) { res = -errno; free(intx_file); ntfs_attr_close(na); goto exit; } if (intx_file->magic == INTX_BLOCK_DEVICE && na->data_size == offsetof( INTX_FILE, device_end)) { stbuf->st_mode = S_IFBLK; stbuf->st_rdev = makedev(le64_to_cpu( intx_file->major), le64_to_cpu( intx_file->minor)); } if (intx_file->magic == INTX_CHARACTER_DEVICE && na->data_size == offsetof( INTX_FILE, device_end)) { stbuf->st_mode = S_IFCHR; stbuf->st_rdev = makedev(le64_to_cpu( intx_file->major), le64_to_cpu( intx_file->minor)); } if (intx_file->magic == INTX_SYMBOLIC_LINK) stbuf->st_mode = S_IFLNK; free(intx_file); } ntfs_attr_close(na); } stbuf->st_mode |= (0777 & ~ctx->fmask); } stbuf->st_uid = ctx->uid; stbuf->st_gid = ctx->gid; stbuf->st_ino = ni->mft_no; stbuf->st_atime = ni->last_access_time; stbuf->st_ctime = ni->last_mft_change_time; stbuf->st_mtime = ni->last_data_change_time;exit: if (ntfs_inode_close(ni)) set_fuse_error(&res); free(path); if (stream_name_len) free(stream_name); return res;}static int ntfs_fuse_readlink(const char *org_path, char *buf, size_t buf_size){ char *path; ntfschar *stream_name; ntfs_inode *ni = NULL; ntfs_attr *na = NULL; INTX_FILE *intx_file = NULL; int stream_name_len, res = 0; /* Get inode. */ stream_name_len = ntfs_fuse_parse_path(org_path, &path, &stream_name); if (stream_name_len < 0) return stream_name_len; if (stream_name_len > 0) { res = -EINVAL; goto exit; } ni = ntfs_pathname_to_inode(ctx->vol, NULL, path); if (!ni) { res = -errno; goto exit; } /* Sanity checks. */ if (!(ni->flags & FILE_ATTR_SYSTEM)) { res = -EINVAL; goto exit; } na = ntfs_attr_open(ni, AT_DATA, AT_UNNAMED, 0); if (!na) { res = -errno; goto exit; } if (na->data_size <= sizeof(INTX_FILE_TYPES)) { res = -EINVAL; goto exit; } if (na->data_size > sizeof(INTX_FILE_TYPES) + sizeof(ntfschar) * PATH_MAX) { res = -ENAMETOOLONG; goto exit; } /* Receive file content. */ intx_file = ntfs_malloc(na->data_size); if (!intx_file) { res = -errno; goto exit; } if (ntfs_attr_pread(na, 0, na->data_size, intx_file) != na->data_size) { res = -errno; goto exit; } /* Sanity check. */ if (intx_file->magic != INTX_SYMBOLIC_LINK) { res = -EINVAL; goto exit; } /* Convert link from unicode to local encoding. */ if (ntfs_ucstombs(intx_file->target, (na->data_size - offsetof(INTX_FILE, target)) / sizeof(ntfschar), &buf, buf_size) < 0) { res = -errno; goto exit; }exit: if (intx_file) free(intx_file); if (na) ntfs_attr_close(na); if (ntfs_inode_close(ni)) set_fuse_error(&res); free(path); if (stream_name_len) free(stream_name); return res;}static int ntfs_fuse_filler(ntfs_fuse_fill_context_t *fill_ctx, const ntfschar *name, const int name_len, const int name_type, const s64 pos __attribute__((unused)), const MFT_REF mref, const unsigned dt_type __attribute__((unused))){ char *filename = NULL; int ret = 0; if (name_type == FILE_NAME_DOS) return 0; if (ntfs_ucstombs(name, name_len, &filename, 0) < 0) { ntfs_log_perror("Skipping unrepresentable filename (inode %llu)", (unsigned long long)MREF(mref)); return 0; } if (ntfs_fuse_is_named_data_stream(filename)) { ntfs_log_error("Unable to access '%s' (inode %llu) with " "current named streams access interface.\n", filename, (unsigned long long)MREF(mref)); free(filename); return 0; } if (MREF(mref) == FILE_root || MREF(mref) >= FILE_first_user || ctx->show_sys_files) { struct stat st = { .st_ino = MREF(mref) }; if (dt_type == NTFS_DT_REG) st.st_mode = S_IFREG | (0777 & ~ctx->fmask); else if (dt_type == NTFS_DT_DIR) st.st_mode = S_IFDIR | (0777 & ~ctx->dmask); ret = fill_ctx->filler(fill_ctx->buf, filename, &st, 0); } free(filename); return ret;}static int ntfs_fuse_readdir(const char *path, void *buf, fuse_fill_dir_t filler, off_t offset __attribute__((unused)),
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -