📄 ntfs-3g.c
字号:
/** * ntfs-3g - Third Generation NTFS Driver * * Copyright (c) 2005-2006 Yura Pakhuchiy * Copyright (c) 2005 Yuval Fledel * Copyright (c) 2006-2007 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 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>#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 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; int state; long free_clusters; long free_mft; unsigned int uid; unsigned int gid; unsigned int fmask; unsigned int dmask; ntfs_fuse_streams_interface streams; BOOL ro; BOOL show_sys_files; BOOL silent; BOOL force; BOOL debug; BOOL noatime; BOOL no_detach;} ntfs_fuse_context_t;typedef enum { NF_FreeClustersOutdate = (1 << 0), /* Information about amount of free clusters is outdated. */ NF_FreeMFTOutdate = (1 << 1), /* Information about amount of free MFT records is outdated. */} ntfs_fuse_state_bits;static struct options { char *mnt_point; /* Mount point */ char *options; /* Mount options */ char *device; /* Device to mount */ int quiet; /* Less output */ int verbose; /* Extra output */} 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://www.ntfs-3g.org/support.html#locale\n";static const char *fuse26_kmod_msg ="WARNING: Deficient Linux kernel detected. Some driver features are\n"" not available (swap file on NTFS, boot from NTFS by LILO), and\n"" unmount is not safe unless it's made sure the ntfs-3g process\n"" naturally terminates after calling 'umount'. If you wish this\n"" message to disappear then you should upgrade to at least kernel\n"" version 2.6.20, or request help from your distribution to fix\n"" the kernel problem. The below web page has more information:\n"" http://www.ntfs-3g.org/support.html#fuse26\n""\n";static __inline__ void ntfs_fuse_mark_free_space_outdated(void){ /* Mark information about free MFT record and clusters outdated. */ ctx->state |= (NF_FreeClustersOutdate | NF_FreeMFTOutdate);}/** * 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 __inline__ int ntfs_fuse_is_named_data_stream(const char *path){ if (strchr(path, ':') && ctx->streams == NF_STREAMS_INTERFACE_WINDOWS) return 1; return 0;}static long ntfs_fuse_get_nr_free_mft_records(ntfs_volume *vol, s64 numof_inode){ u8 *buf; long nr_free = 0; s64 br, total = 0; if (!(ctx->state & NF_FreeMFTOutdate)) return ctx->free_mft; buf = ntfs_malloc(vol->cluster_size); if (!buf) return -errno; while (1) { int i, j; br = ntfs_attr_pread(vol->mftbmp_na, total, vol->cluster_size, buf); if (br <= 0) break; total += br; for (i = 0; i < br; i++) for (j = 0; j < 8; j++) { if (--numof_inode < 0) break; if (!((buf[i] >> j) & 1)) nr_free++; } } free(buf); if (!total || br < 0) return -errno; ctx->free_mft = nr_free; ctx->state &= ~(NF_FreeMFTOutdate); return nr_free;}static long ntfs_fuse_get_nr_free_clusters(ntfs_volume *vol){ u8 *buf; long nr_free = 0; s64 br, total = 0; if (!(ctx->state & NF_FreeClustersOutdate)) return ctx->free_clusters; buf = ntfs_malloc(vol->cluster_size); if (!buf) return -errno; while (1) { int i, j; br = ntfs_attr_pread(vol->lcnbmp_na, total, vol->cluster_size, buf); if (br <= 0) break; total += br; for (i = 0; i < br; i++) for (j = 0; j < 8; j++) if (!((buf[i] >> j) & 1)) nr_free++; } free(buf); if (!total || br < 0) return -errno; ctx->free_clusters = nr_free; ctx->state &= ~(NF_FreeClustersOutdate); 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){ long size, delta_bits; u64 allocated_inodes; ntfs_volume *vol; vol = ctx->vol; if (!vol) return -ENODEV; /* Optimal transfer block size. */ sfs->f_bsize = vol->cluster_size; sfs->f_frsize = vol->cluster_size; /* * Total data blocks in file system in units of f_bsize and since * inodes are also stored in data blocs ($MFT is a file) this is just * the total clusters. */ sfs->f_blocks = vol->nr_clusters; /* Free data blocks in file system in units of f_bsize. */ size = ntfs_fuse_get_nr_free_clusters(vol); if (size < 0) size = 0; /* Free blocks avail to non-superuser, same as above on NTFS. */ 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 in file system (at this point in time). */ allocated_inodes = vol->mft_na->data_size >> vol->mft_record_size_bits; sfs->f_files = allocated_inodes + size; /* Free inodes in fs (based on current total count). */ size = ntfs_fuse_get_nr_free_mft_records(vol, allocated_inodes) + size; if (size < 0) size = 0; sfs->f_ffree = size; sfs->f_favail = 0; /* 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, 0); if (res < 0) return -errno; return res; } } else *path = stream_name_mbs; *stream_name = AT_UNNAMED; return 0;}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) * MAX_PATH && 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 (ni) ntfs_inode_close(ni); 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) * MAX_PATH) { 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 (ni) ntfs_inode_close(ni); free(path); if (stream_name_len) free(stream_name);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -