mirror of
https://github.com/wiidev/usbloadergx.git
synced 2024-11-05 19:15:07 +01:00
And of course I forgot libntfs.
This commit is contained in:
parent
3aa838dec9
commit
f3ce461dc6
4283
source/libntfs/acls.c
Normal file
4283
source/libntfs/acls.c
Normal file
File diff suppressed because it is too large
Load Diff
201
source/libntfs/acls.h
Normal file
201
source/libntfs/acls.h
Normal file
@ -0,0 +1,201 @@
|
|||||||
|
/*
|
||||||
|
*
|
||||||
|
* Copyright (c) 2007-2008 Jean-Pierre Andre
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 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
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef ACLS_H
|
||||||
|
#define ACLS_H
|
||||||
|
|
||||||
|
/*
|
||||||
|
* JPA configuration modes for security.c / acls.c
|
||||||
|
* should be moved to some config file
|
||||||
|
*/
|
||||||
|
|
||||||
|
#define FORCE_FORMAT_v1x 0 /* Insert security data as in NTFS v1.x */
|
||||||
|
#define OWNERFROMACL 1 /* Get the owner from ACL (not Windows owner) */
|
||||||
|
#define BUFSZ 1024 /* buffer size to read mapping file */
|
||||||
|
#define MAPPINGFILE ".NTFS-3G/UserMapping" /* default mapping file */
|
||||||
|
#define LINESZ 120 /* maximum useful size of a mapping line */
|
||||||
|
#define CACHE_PERMISSIONS_BITS 6 /* log2 of unitary allocation of permissions */
|
||||||
|
#define CACHE_PERMISSIONS_SIZE 262144 /* max cacheable permissions */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* JPA The following must be in some library...
|
||||||
|
* but did not found out where
|
||||||
|
*/
|
||||||
|
|
||||||
|
#define endian_rev16(x) (((x >> 8) & 255) | ((x & 255) << 8))
|
||||||
|
#define endian_rev32(x) (((x >> 24) & 255) | ((x >> 8) & 0xff00) \
|
||||||
|
| ((x & 0xff00) << 8) | ((x & 255) << 24))
|
||||||
|
|
||||||
|
#define cpu_to_be16(x) endian_rev16(cpu_to_le16(x))
|
||||||
|
#define cpu_to_be32(x) endian_rev32(cpu_to_le32(x))
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Macro definitions needed to share code with secaudit
|
||||||
|
*/
|
||||||
|
|
||||||
|
#define NTFS_FIND_USID(map,uid,buf) ntfs_find_usid(map,uid,buf)
|
||||||
|
#define NTFS_FIND_GSID(map,gid,buf) ntfs_find_gsid(map,gid,buf)
|
||||||
|
#define NTFS_FIND_USER(map,usid) ntfs_find_user(map,usid)
|
||||||
|
#define NTFS_FIND_GROUP(map,gsid) ntfs_find_group(map,gsid)
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Matching of ntfs permissions to Linux permissions
|
||||||
|
* these constants are adapted to endianness
|
||||||
|
* when setting, set them all
|
||||||
|
* when checking, check one is present
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* flags which are set to mean exec, write or read */
|
||||||
|
|
||||||
|
#define FILE_READ (FILE_READ_DATA)
|
||||||
|
#define FILE_WRITE (FILE_WRITE_DATA | FILE_APPEND_DATA \
|
||||||
|
| READ_CONTROL | FILE_WRITE_ATTRIBUTES | FILE_WRITE_EA)
|
||||||
|
#define FILE_EXEC (FILE_EXECUTE)
|
||||||
|
#define DIR_READ FILE_LIST_DIRECTORY
|
||||||
|
#define DIR_WRITE (FILE_ADD_FILE | FILE_ADD_SUBDIRECTORY | FILE_DELETE_CHILD \
|
||||||
|
| READ_CONTROL | FILE_WRITE_ATTRIBUTES | FILE_WRITE_EA)
|
||||||
|
#define DIR_EXEC (FILE_TRAVERSE)
|
||||||
|
|
||||||
|
/* flags tested for meaning exec, write or read */
|
||||||
|
/* tests for write allow for interpretation of a sticky bit */
|
||||||
|
|
||||||
|
#define FILE_GREAD (FILE_READ_DATA | GENERIC_READ)
|
||||||
|
#define FILE_GWRITE (FILE_WRITE_DATA | FILE_APPEND_DATA | GENERIC_WRITE)
|
||||||
|
#define FILE_GEXEC (FILE_EXECUTE | GENERIC_EXECUTE)
|
||||||
|
#define DIR_GREAD (FILE_LIST_DIRECTORY | GENERIC_READ)
|
||||||
|
#define DIR_GWRITE (FILE_ADD_FILE | FILE_ADD_SUBDIRECTORY | GENERIC_WRITE)
|
||||||
|
#define DIR_GEXEC (FILE_TRAVERSE | GENERIC_EXECUTE)
|
||||||
|
|
||||||
|
/* standard owner (and administrator) rights */
|
||||||
|
|
||||||
|
#define OWNER_RIGHTS (DELETE | READ_CONTROL | WRITE_DAC | WRITE_OWNER \
|
||||||
|
| SYNCHRONIZE \
|
||||||
|
| FILE_READ_ATTRIBUTES | FILE_WRITE_ATTRIBUTES \
|
||||||
|
| FILE_READ_EA | FILE_WRITE_EA)
|
||||||
|
|
||||||
|
/* standard world rights */
|
||||||
|
|
||||||
|
#define WORLD_RIGHTS (READ_CONTROL | FILE_READ_ATTRIBUTES | FILE_READ_EA \
|
||||||
|
| SYNCHRONIZE)
|
||||||
|
|
||||||
|
/* inheritance flags for files and directories */
|
||||||
|
|
||||||
|
#define FILE_INHERITANCE NO_PROPAGATE_INHERIT_ACE
|
||||||
|
#define DIR_INHERITANCE (OBJECT_INHERIT_ACE | CONTAINER_INHERIT_ACE)
|
||||||
|
|
||||||
|
/*
|
||||||
|
* To identify NTFS ACL meaning Posix ACL granted to root
|
||||||
|
* we use rights always granted to anybody, so they have no impact
|
||||||
|
* either on Windows or on Linux.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#define ROOT_OWNER_UNMARK SYNCHRONIZE /* ACL granted to root as owner */
|
||||||
|
#define ROOT_GROUP_UNMARK FILE_READ_EA /* ACL granted to root as group */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* A type large enough to hold any SID
|
||||||
|
*/
|
||||||
|
|
||||||
|
typedef char BIGSID[40];
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Struct to hold the input mapping file
|
||||||
|
* (private to this module)
|
||||||
|
*/
|
||||||
|
|
||||||
|
struct MAPLIST {
|
||||||
|
struct MAPLIST *next;
|
||||||
|
char *uidstr; /* uid text from the same record */
|
||||||
|
char *gidstr; /* gid text from the same record */
|
||||||
|
char *sidstr; /* sid text from the same record */
|
||||||
|
char maptext[LINESZ + 1];
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef int (*FILEREADER)(void *fileid, char *buf, size_t size, off_t pos);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Constants defined in acls.c
|
||||||
|
*/
|
||||||
|
|
||||||
|
extern const SID *adminsid;
|
||||||
|
extern const SID *worldsid;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Functions defined in acls.c
|
||||||
|
*/
|
||||||
|
|
||||||
|
BOOL ntfs_valid_descr(const char *securattr, unsigned int attrsz);
|
||||||
|
BOOL ntfs_valid_pattern(const SID *sid);
|
||||||
|
BOOL ntfs_valid_sid(const SID *sid);
|
||||||
|
BOOL ntfs_same_sid(const SID *first, const SID *second);
|
||||||
|
|
||||||
|
BOOL ntfs_is_user_sid(const SID *usid);
|
||||||
|
|
||||||
|
|
||||||
|
int ntfs_sid_size(const SID * sid);
|
||||||
|
unsigned int ntfs_attr_size(const char *attr);
|
||||||
|
|
||||||
|
const SID *ntfs_find_usid(const struct MAPPING *usermapping,
|
||||||
|
uid_t uid, SID *pdefsid);
|
||||||
|
const SID *ntfs_find_gsid(const struct MAPPING *groupmapping,
|
||||||
|
gid_t gid, SID *pdefsid);
|
||||||
|
uid_t ntfs_find_user(const struct MAPPING *usermapping, const SID *usid);
|
||||||
|
gid_t ntfs_find_group(const struct MAPPING *groupmapping, const SID * gsid);
|
||||||
|
const SID *ntfs_acl_owner(const char *secattr);
|
||||||
|
|
||||||
|
#if POSIXACLS
|
||||||
|
|
||||||
|
BOOL ntfs_valid_posix(const struct POSIX_SECURITY *pxdesc);
|
||||||
|
void ntfs_sort_posix(struct POSIX_SECURITY *pxdesc);
|
||||||
|
int ntfs_merge_mode_posix(struct POSIX_SECURITY *pxdesc, mode_t mode);
|
||||||
|
struct POSIX_SECURITY *ntfs_build_inherited_posix(
|
||||||
|
const struct POSIX_SECURITY *pxdesc, mode_t mode,
|
||||||
|
mode_t umask, BOOL isdir);
|
||||||
|
struct POSIX_SECURITY *ntfs_replace_acl(const struct POSIX_SECURITY *oldpxdesc,
|
||||||
|
const struct POSIX_ACL *newacl, int count, BOOL deflt);
|
||||||
|
struct POSIX_SECURITY *ntfs_build_permissions_posix(
|
||||||
|
struct MAPPING* const mapping[],
|
||||||
|
const char *securattr,
|
||||||
|
const SID *usid, const SID *gsid, BOOL isdir);
|
||||||
|
struct POSIX_SECURITY *ntfs_merge_descr_posix(const struct POSIX_SECURITY *first,
|
||||||
|
const struct POSIX_SECURITY *second);
|
||||||
|
char *ntfs_build_descr_posix(struct MAPPING* const mapping[],
|
||||||
|
struct POSIX_SECURITY *pxdesc,
|
||||||
|
int isdir, const SID *usid, const SID *gsid);
|
||||||
|
|
||||||
|
#endif /* POSIXACLS */
|
||||||
|
|
||||||
|
int ntfs_inherit_acl(const ACL *oldacl, ACL *newacl,
|
||||||
|
const SID *usid, const SID *gsid, BOOL fordir);
|
||||||
|
int ntfs_build_permissions(const char *securattr,
|
||||||
|
const SID *usid, const SID *gsid, BOOL isdir);
|
||||||
|
char *ntfs_build_descr(mode_t mode,
|
||||||
|
int isdir, const SID * usid, const SID * gsid);
|
||||||
|
struct MAPLIST *ntfs_read_mapping(FILEREADER reader, void *fileid);
|
||||||
|
struct MAPPING *ntfs_do_user_mapping(struct MAPLIST *firstitem);
|
||||||
|
struct MAPPING *ntfs_do_group_mapping(struct MAPLIST *firstitem);
|
||||||
|
void ntfs_free_mapping(struct MAPPING *mapping[]);
|
||||||
|
|
||||||
|
#endif /* ACLS_H */
|
||||||
|
|
5866
source/libntfs/attrib.c
Normal file
5866
source/libntfs/attrib.c
Normal file
File diff suppressed because it is too large
Load Diff
357
source/libntfs/attrib.h
Normal file
357
source/libntfs/attrib.h
Normal file
@ -0,0 +1,357 @@
|
|||||||
|
/*
|
||||||
|
* attrib.h - Exports for attribute handling. Originated from the Linux-NTFS project.
|
||||||
|
*
|
||||||
|
* Copyright (c) 2000-2004 Anton Altaparmakov
|
||||||
|
* Copyright (c) 2004-2005 Yura Pakhuchiy
|
||||||
|
* Copyright (c) 2006-2007 Szabolcs Szakacsits
|
||||||
|
*
|
||||||
|
* This program/include file 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/include file 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
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _NTFS_ATTRIB_H
|
||||||
|
#define _NTFS_ATTRIB_H
|
||||||
|
|
||||||
|
/* Forward declarations */
|
||||||
|
typedef struct _ntfs_attr ntfs_attr;
|
||||||
|
typedef struct _ntfs_attr_search_ctx ntfs_attr_search_ctx;
|
||||||
|
|
||||||
|
#include "types.h"
|
||||||
|
#include "inode.h"
|
||||||
|
#include "unistr.h"
|
||||||
|
#include "runlist.h"
|
||||||
|
#include "volume.h"
|
||||||
|
#include "debug.h"
|
||||||
|
#include "logging.h"
|
||||||
|
|
||||||
|
extern ntfschar AT_UNNAMED[];
|
||||||
|
extern ntfschar STREAM_SDS[];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* enum ntfs_lcn_special_values - special return values for ntfs_*_vcn_to_lcn()
|
||||||
|
*
|
||||||
|
* Special return values for ntfs_rl_vcn_to_lcn() and ntfs_attr_vcn_to_lcn().
|
||||||
|
*
|
||||||
|
* TODO: Describe them.
|
||||||
|
*/
|
||||||
|
typedef enum {
|
||||||
|
LCN_HOLE = -1, /* Keep this as highest value or die! */
|
||||||
|
LCN_RL_NOT_MAPPED = -2,
|
||||||
|
LCN_ENOENT = -3,
|
||||||
|
LCN_EINVAL = -4,
|
||||||
|
LCN_EIO = -5,
|
||||||
|
} ntfs_lcn_special_values;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* struct ntfs_attr_search_ctx - search context used in attribute search functions
|
||||||
|
* @mrec: buffer containing mft record to search
|
||||||
|
* @attr: attribute record in @mrec where to begin/continue search
|
||||||
|
* @is_first: if true lookup_attr() begins search with @attr, else after @attr
|
||||||
|
*
|
||||||
|
* Structure must be initialized to zero before the first call to one of the
|
||||||
|
* attribute search functions. Initialize @mrec to point to the mft record to
|
||||||
|
* search, and @attr to point to the first attribute within @mrec (not necessary
|
||||||
|
* if calling the _first() functions), and set @is_first to TRUE (not necessary
|
||||||
|
* if calling the _first() functions).
|
||||||
|
*
|
||||||
|
* If @is_first is TRUE, the search begins with @attr. If @is_first is FALSE,
|
||||||
|
* the search begins after @attr. This is so that, after the first call to one
|
||||||
|
* of the search attribute functions, we can call the function again, without
|
||||||
|
* any modification of the search context, to automagically get the next
|
||||||
|
* matching attribute.
|
||||||
|
*/
|
||||||
|
struct _ntfs_attr_search_ctx {
|
||||||
|
MFT_RECORD *mrec;
|
||||||
|
ATTR_RECORD *attr;
|
||||||
|
BOOL is_first;
|
||||||
|
ntfs_inode *ntfs_ino;
|
||||||
|
ATTR_LIST_ENTRY *al_entry;
|
||||||
|
ntfs_inode *base_ntfs_ino;
|
||||||
|
MFT_RECORD *base_mrec;
|
||||||
|
ATTR_RECORD *base_attr;
|
||||||
|
};
|
||||||
|
|
||||||
|
extern void ntfs_attr_reinit_search_ctx(ntfs_attr_search_ctx *ctx);
|
||||||
|
extern ntfs_attr_search_ctx *ntfs_attr_get_search_ctx(ntfs_inode *ni,
|
||||||
|
MFT_RECORD *mrec);
|
||||||
|
extern void ntfs_attr_put_search_ctx(ntfs_attr_search_ctx *ctx);
|
||||||
|
|
||||||
|
extern int ntfs_attr_lookup(const ATTR_TYPES type, const ntfschar *name,
|
||||||
|
const u32 name_len, const IGNORE_CASE_BOOL ic,
|
||||||
|
const VCN lowest_vcn, const u8 *val, const u32 val_len,
|
||||||
|
ntfs_attr_search_ctx *ctx);
|
||||||
|
|
||||||
|
extern int ntfs_attr_position(const ATTR_TYPES type, ntfs_attr_search_ctx *ctx);
|
||||||
|
|
||||||
|
extern ATTR_DEF *ntfs_attr_find_in_attrdef(const ntfs_volume *vol,
|
||||||
|
const ATTR_TYPES type);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ntfs_attrs_walk - syntactic sugar for walking all attributes in an inode
|
||||||
|
* @ctx: initialised attribute search context
|
||||||
|
*
|
||||||
|
* Syntactic sugar for walking attributes in an inode.
|
||||||
|
*
|
||||||
|
* Return 0 on success and -1 on error with errno set to the error code from
|
||||||
|
* ntfs_attr_lookup().
|
||||||
|
*
|
||||||
|
* Example: When you want to enumerate all attributes in an open ntfs inode
|
||||||
|
* @ni, you can simply do:
|
||||||
|
*
|
||||||
|
* int err;
|
||||||
|
* ntfs_attr_search_ctx *ctx = ntfs_attr_get_search_ctx(ni, NULL);
|
||||||
|
* if (!ctx)
|
||||||
|
* // Error code is in errno. Handle this case.
|
||||||
|
* while (!(err = ntfs_attrs_walk(ctx))) {
|
||||||
|
* ATTR_RECORD *attr = ctx->attr;
|
||||||
|
* // attr now contains the next attribute. Do whatever you want
|
||||||
|
* // with it and then just continue with the while loop.
|
||||||
|
* }
|
||||||
|
* if (err && errno != ENOENT)
|
||||||
|
* // Ooops. An error occurred! You should handle this case.
|
||||||
|
* // Now finished with all attributes in the inode.
|
||||||
|
*/
|
||||||
|
static __inline__ int ntfs_attrs_walk(ntfs_attr_search_ctx *ctx)
|
||||||
|
{
|
||||||
|
return ntfs_attr_lookup(AT_UNUSED, NULL, 0, CASE_SENSITIVE, 0,
|
||||||
|
NULL, 0, ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* struct ntfs_attr - ntfs in memory non-resident attribute structure
|
||||||
|
* @rl: if not NULL, the decompressed runlist
|
||||||
|
* @ni: base ntfs inode to which this attribute belongs
|
||||||
|
* @type: attribute type
|
||||||
|
* @name: Unicode name of the attribute
|
||||||
|
* @name_len: length of @name in Unicode characters
|
||||||
|
* @state: NTFS attribute specific flags describing this attribute
|
||||||
|
* @allocated_size: copy from the attribute record
|
||||||
|
* @data_size: copy from the attribute record
|
||||||
|
* @initialized_size: copy from the attribute record
|
||||||
|
* @compressed_size: copy from the attribute record
|
||||||
|
* @compression_block_size: size of a compression block (cb)
|
||||||
|
* @compression_block_size_bits: log2 of the size of a cb
|
||||||
|
* @compression_block_clusters: number of clusters per cb
|
||||||
|
*
|
||||||
|
* This structure exists purely to provide a mechanism of caching the runlist
|
||||||
|
* of an attribute. If you want to operate on a particular attribute extent,
|
||||||
|
* you should not be using this structure at all. If you want to work with a
|
||||||
|
* resident attribute, you should not be using this structure at all. As a
|
||||||
|
* fail-safe check make sure to test NAttrNonResident() and if it is false, you
|
||||||
|
* know you shouldn't be using this structure.
|
||||||
|
*
|
||||||
|
* If you want to work on a resident attribute or on a specific attribute
|
||||||
|
* extent, you should use ntfs_lookup_attr() to retrieve the attribute (extent)
|
||||||
|
* record, edit that, and then write back the mft record (or set the
|
||||||
|
* corresponding ntfs inode dirty for delayed write back).
|
||||||
|
*
|
||||||
|
* @rl is the decompressed runlist of the attribute described by this
|
||||||
|
* structure. Obviously this only makes sense if the attribute is not resident,
|
||||||
|
* i.e. NAttrNonResident() is true. If the runlist hasn't been decompressed yet
|
||||||
|
* @rl is NULL, so be prepared to cope with @rl == NULL.
|
||||||
|
*
|
||||||
|
* @ni is the base ntfs inode of the attribute described by this structure.
|
||||||
|
*
|
||||||
|
* @type is the attribute type (see layout.h for the definition of ATTR_TYPES),
|
||||||
|
* @name and @name_len are the little endian Unicode name and the name length
|
||||||
|
* in Unicode characters of the attribute, respectively.
|
||||||
|
*
|
||||||
|
* @state contains NTFS attribute specific flags describing this attribute
|
||||||
|
* structure. See ntfs_attr_state_bits above.
|
||||||
|
*/
|
||||||
|
struct _ntfs_attr {
|
||||||
|
runlist_element *rl;
|
||||||
|
ntfs_inode *ni;
|
||||||
|
ATTR_TYPES type;
|
||||||
|
ATTR_FLAGS data_flags;
|
||||||
|
ntfschar *name;
|
||||||
|
u32 name_len;
|
||||||
|
unsigned long state;
|
||||||
|
s64 allocated_size;
|
||||||
|
s64 data_size;
|
||||||
|
s64 initialized_size;
|
||||||
|
s64 compressed_size;
|
||||||
|
u32 compression_block_size;
|
||||||
|
u8 compression_block_size_bits;
|
||||||
|
u8 compression_block_clusters;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* enum ntfs_attr_state_bits - bits for the state field in the ntfs_attr
|
||||||
|
* structure
|
||||||
|
*/
|
||||||
|
typedef enum {
|
||||||
|
NA_Initialized, /* 1: structure is initialized. */
|
||||||
|
NA_NonResident, /* 1: Attribute is not resident. */
|
||||||
|
} ntfs_attr_state_bits;
|
||||||
|
|
||||||
|
#define test_nattr_flag(na, flag) test_bit(NA_##flag, (na)->state)
|
||||||
|
#define set_nattr_flag(na, flag) set_bit(NA_##flag, (na)->state)
|
||||||
|
#define clear_nattr_flag(na, flag) clear_bit(NA_##flag, (na)->state)
|
||||||
|
|
||||||
|
#define NAttrInitialized(na) test_nattr_flag(na, Initialized)
|
||||||
|
#define NAttrSetInitialized(na) set_nattr_flag(na, Initialized)
|
||||||
|
#define NAttrClearInitialized(na) clear_nattr_flag(na, Initialized)
|
||||||
|
|
||||||
|
#define NAttrNonResident(na) test_nattr_flag(na, NonResident)
|
||||||
|
#define NAttrSetNonResident(na) set_nattr_flag(na, NonResident)
|
||||||
|
#define NAttrClearNonResident(na) clear_nattr_flag(na, NonResident)
|
||||||
|
|
||||||
|
#define GenNAttrIno(func_name, flag) \
|
||||||
|
extern int NAttr##func_name(ntfs_attr *na); \
|
||||||
|
extern void NAttrSet##func_name(ntfs_attr *na); \
|
||||||
|
extern void NAttrClear##func_name(ntfs_attr *na);
|
||||||
|
|
||||||
|
GenNAttrIno(Compressed, FILE_ATTR_COMPRESSED)
|
||||||
|
GenNAttrIno(Encrypted, FILE_ATTR_ENCRYPTED)
|
||||||
|
GenNAttrIno(Sparse, FILE_ATTR_SPARSE_FILE)
|
||||||
|
#undef GenNAttrIno
|
||||||
|
|
||||||
|
/**
|
||||||
|
* union attr_val - Union of all known attribute values
|
||||||
|
*
|
||||||
|
* For convenience. Used in the attr structure.
|
||||||
|
*/
|
||||||
|
typedef union {
|
||||||
|
u8 _default; /* Unnamed u8 to serve as default when just using
|
||||||
|
a_val without specifying any of the below. */
|
||||||
|
STANDARD_INFORMATION std_inf;
|
||||||
|
ATTR_LIST_ENTRY al_entry;
|
||||||
|
FILE_NAME_ATTR filename;
|
||||||
|
OBJECT_ID_ATTR obj_id;
|
||||||
|
SECURITY_DESCRIPTOR_ATTR sec_desc;
|
||||||
|
VOLUME_NAME vol_name;
|
||||||
|
VOLUME_INFORMATION vol_inf;
|
||||||
|
DATA_ATTR data;
|
||||||
|
INDEX_ROOT index_root;
|
||||||
|
INDEX_BLOCK index_blk;
|
||||||
|
BITMAP_ATTR bmp;
|
||||||
|
REPARSE_POINT reparse;
|
||||||
|
EA_INFORMATION ea_inf;
|
||||||
|
EA_ATTR ea;
|
||||||
|
PROPERTY_SET property_set;
|
||||||
|
LOGGED_UTILITY_STREAM logged_util_stream;
|
||||||
|
EFS_ATTR_HEADER efs;
|
||||||
|
} attr_val;
|
||||||
|
|
||||||
|
extern void ntfs_attr_init(ntfs_attr *na, const BOOL non_resident,
|
||||||
|
const ATTR_FLAGS data_flags, const BOOL encrypted,
|
||||||
|
const BOOL sparse,
|
||||||
|
const s64 allocated_size, const s64 data_size,
|
||||||
|
const s64 initialized_size, const s64 compressed_size,
|
||||||
|
const u8 compression_unit);
|
||||||
|
|
||||||
|
/* warning : in the following "name" has to be freeable */
|
||||||
|
/* or one of constants AT_UNNAMED, NTFS_INDEX_I30 or STREAM_SDS */
|
||||||
|
extern ntfs_attr *ntfs_attr_open(ntfs_inode *ni, const ATTR_TYPES type,
|
||||||
|
ntfschar *name, u32 name_len);
|
||||||
|
extern void ntfs_attr_close(ntfs_attr *na);
|
||||||
|
|
||||||
|
extern s64 ntfs_attr_pread(ntfs_attr *na, const s64 pos, s64 count,
|
||||||
|
void *b);
|
||||||
|
extern s64 ntfs_attr_pwrite(ntfs_attr *na, const s64 pos, s64 count,
|
||||||
|
const void *b);
|
||||||
|
extern int ntfs_attr_pclose(ntfs_attr *na);
|
||||||
|
|
||||||
|
extern void *ntfs_attr_readall(ntfs_inode *ni, const ATTR_TYPES type,
|
||||||
|
ntfschar *name, u32 name_len, s64 *data_size);
|
||||||
|
|
||||||
|
extern s64 ntfs_attr_mst_pread(ntfs_attr *na, const s64 pos,
|
||||||
|
const s64 bk_cnt, const u32 bk_size, void *dst);
|
||||||
|
extern s64 ntfs_attr_mst_pwrite(ntfs_attr *na, const s64 pos,
|
||||||
|
s64 bk_cnt, const u32 bk_size, void *src);
|
||||||
|
|
||||||
|
extern int ntfs_attr_map_runlist(ntfs_attr *na, VCN vcn);
|
||||||
|
extern int ntfs_attr_map_whole_runlist(ntfs_attr *na);
|
||||||
|
|
||||||
|
extern LCN ntfs_attr_vcn_to_lcn(ntfs_attr *na, const VCN vcn);
|
||||||
|
extern runlist_element *ntfs_attr_find_vcn(ntfs_attr *na, const VCN vcn);
|
||||||
|
|
||||||
|
extern int ntfs_attr_size_bounds_check(const ntfs_volume *vol,
|
||||||
|
const ATTR_TYPES type, const s64 size);
|
||||||
|
extern int ntfs_attr_can_be_non_resident(const ntfs_volume *vol,
|
||||||
|
const ATTR_TYPES type);
|
||||||
|
extern int ntfs_attr_can_be_resident(const ntfs_volume *vol,
|
||||||
|
const ATTR_TYPES type);
|
||||||
|
int ntfs_attr_make_non_resident(ntfs_attr *na,
|
||||||
|
ntfs_attr_search_ctx *ctx);
|
||||||
|
extern int ntfs_make_room_for_attr(MFT_RECORD *m, u8 *pos, u32 size);
|
||||||
|
|
||||||
|
extern int ntfs_resident_attr_record_add(ntfs_inode *ni, ATTR_TYPES type,
|
||||||
|
ntfschar *name, u8 name_len, u8 *val, u32 size,
|
||||||
|
ATTR_FLAGS flags);
|
||||||
|
extern int ntfs_non_resident_attr_record_add(ntfs_inode *ni, ATTR_TYPES type,
|
||||||
|
ntfschar *name, u8 name_len, VCN lowest_vcn, int dataruns_size,
|
||||||
|
ATTR_FLAGS flags);
|
||||||
|
extern int ntfs_attr_record_rm(ntfs_attr_search_ctx *ctx);
|
||||||
|
|
||||||
|
extern int ntfs_attr_add(ntfs_inode *ni, ATTR_TYPES type,
|
||||||
|
ntfschar *name, u8 name_len, u8 *val, s64 size);
|
||||||
|
extern int ntfs_attr_set_flags(ntfs_inode *ni, ATTR_TYPES type,
|
||||||
|
ntfschar *name, u8 name_len, ATTR_FLAGS flags, ATTR_FLAGS mask);
|
||||||
|
extern int ntfs_attr_rm(ntfs_attr *na);
|
||||||
|
|
||||||
|
extern int ntfs_attr_record_resize(MFT_RECORD *m, ATTR_RECORD *a, u32 new_size);
|
||||||
|
|
||||||
|
extern int ntfs_resident_attr_value_resize(MFT_RECORD *m, ATTR_RECORD *a,
|
||||||
|
const u32 new_size);
|
||||||
|
|
||||||
|
extern int ntfs_attr_record_move_to(ntfs_attr_search_ctx *ctx, ntfs_inode *ni);
|
||||||
|
extern int ntfs_attr_record_move_away(ntfs_attr_search_ctx *ctx, int extra);
|
||||||
|
|
||||||
|
extern int ntfs_attr_update_mapping_pairs(ntfs_attr *na, VCN from_vcn);
|
||||||
|
|
||||||
|
extern int ntfs_attr_truncate(ntfs_attr *na, const s64 newsize);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* get_attribute_value_length - return the length of the value of an attribute
|
||||||
|
* @a: pointer to a buffer containing the attribute record
|
||||||
|
*
|
||||||
|
* Return the byte size of the attribute value of the attribute @a (as it
|
||||||
|
* would be after eventual decompression and filling in of holes if sparse).
|
||||||
|
* If we return 0, check errno. If errno is 0 the actual length was 0,
|
||||||
|
* otherwise errno describes the error.
|
||||||
|
*
|
||||||
|
* FIXME: Describe possible errnos.
|
||||||
|
*/
|
||||||
|
extern s64 ntfs_get_attribute_value_length(const ATTR_RECORD *a);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* get_attribute_value - return the attribute value of an attribute
|
||||||
|
* @vol: volume on which the attribute is present
|
||||||
|
* @a: attribute to get the value of
|
||||||
|
* @b: destination buffer for the attribute value
|
||||||
|
*
|
||||||
|
* Make a copy of the attribute value of the attribute @a into the destination
|
||||||
|
* buffer @b. Note, that the size of @b has to be at least equal to the value
|
||||||
|
* returned by get_attribute_value_length(@a).
|
||||||
|
*
|
||||||
|
* Return number of bytes copied. If this is zero check errno. If errno is 0
|
||||||
|
* then nothing was read due to a zero-length attribute value, otherwise
|
||||||
|
* errno describes the error.
|
||||||
|
*/
|
||||||
|
extern s64 ntfs_get_attribute_value(const ntfs_volume *vol,
|
||||||
|
const ATTR_RECORD *a, u8 *b);
|
||||||
|
|
||||||
|
extern void ntfs_attr_name_free(char **name);
|
||||||
|
extern char *ntfs_attr_name_get(const ntfschar *uname, const int uname_len);
|
||||||
|
extern int ntfs_attr_exist(ntfs_inode *ni, const ATTR_TYPES type,
|
||||||
|
ntfschar *name, u32 name_len);
|
||||||
|
extern int ntfs_attr_remove(ntfs_inode *ni, const ATTR_TYPES type,
|
||||||
|
ntfschar *name, u32 name_len);
|
||||||
|
extern s64 ntfs_attr_get_free_bits(ntfs_attr *na);
|
||||||
|
|
||||||
|
#endif /* defined _NTFS_ATTRIB_H */
|
||||||
|
|
5914
source/libntfs/attrib_frag.c
Normal file
5914
source/libntfs/attrib_frag.c
Normal file
File diff suppressed because it is too large
Load Diff
314
source/libntfs/attrlist.c
Normal file
314
source/libntfs/attrlist.c
Normal file
@ -0,0 +1,314 @@
|
|||||||
|
/**
|
||||||
|
* attrlist.c - Attribute list attribute handling code. Originated from the Linux-NTFS
|
||||||
|
* project.
|
||||||
|
*
|
||||||
|
* Copyright (c) 2004-2005 Anton Altaparmakov
|
||||||
|
* Copyright (c) 2004-2005 Yura Pakhuchiy
|
||||||
|
* Copyright (c) 2006 Szabolcs Szakacsits
|
||||||
|
*
|
||||||
|
* This program/include file 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/include file 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
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifdef HAVE_CONFIG_H
|
||||||
|
#include "config.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef HAVE_STRING_H
|
||||||
|
#include <string.h>
|
||||||
|
#endif
|
||||||
|
#ifdef HAVE_STDLIB_H
|
||||||
|
#include <stdlib.h>
|
||||||
|
#endif
|
||||||
|
#ifdef HAVE_ERRNO_H
|
||||||
|
#include <errno.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "types.h"
|
||||||
|
#include "layout.h"
|
||||||
|
#include "attrib.h"
|
||||||
|
#include "attrlist.h"
|
||||||
|
#include "debug.h"
|
||||||
|
#include "unistr.h"
|
||||||
|
#include "logging.h"
|
||||||
|
#include "misc.h"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ntfs_attrlist_need - check whether inode need attribute list
|
||||||
|
* @ni: opened ntfs inode for which perform check
|
||||||
|
*
|
||||||
|
* Check whether all are attributes belong to one MFT record, in that case
|
||||||
|
* attribute list is not needed.
|
||||||
|
*
|
||||||
|
* Return 1 if inode need attribute list, 0 if not, -1 on error with errno set
|
||||||
|
* to the error code. If function succeed errno set to 0. The following error
|
||||||
|
* codes are defined:
|
||||||
|
* EINVAL - Invalid arguments passed to function or attribute haven't got
|
||||||
|
* attribute list.
|
||||||
|
*/
|
||||||
|
int ntfs_attrlist_need(ntfs_inode *ni)
|
||||||
|
{
|
||||||
|
ATTR_LIST_ENTRY *ale;
|
||||||
|
|
||||||
|
if (!ni) {
|
||||||
|
ntfs_log_trace("Invalid arguments.\n");
|
||||||
|
errno = EINVAL;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
ntfs_log_trace("Entering for inode 0x%llx.\n", (long long) ni->mft_no);
|
||||||
|
|
||||||
|
if (!NInoAttrList(ni)) {
|
||||||
|
ntfs_log_trace("Inode haven't got attribute list.\n");
|
||||||
|
errno = EINVAL;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!ni->attr_list) {
|
||||||
|
ntfs_log_trace("Corrupt in-memory struct.\n");
|
||||||
|
errno = EINVAL;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
errno = 0;
|
||||||
|
ale = (ATTR_LIST_ENTRY *)ni->attr_list;
|
||||||
|
while ((u8*)ale < ni->attr_list + ni->attr_list_size) {
|
||||||
|
if (MREF_LE(ale->mft_reference) != ni->mft_no)
|
||||||
|
return 1;
|
||||||
|
ale = (ATTR_LIST_ENTRY *)((u8*)ale + le16_to_cpu(ale->length));
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ntfs_attrlist_entry_add - add an attribute list attribute entry
|
||||||
|
* @ni: opened ntfs inode, which contains that attribute
|
||||||
|
* @attr: attribute record to add to attribute list
|
||||||
|
*
|
||||||
|
* Return 0 on success and -1 on error with errno set to the error code. The
|
||||||
|
* following error codes are defined:
|
||||||
|
* EINVAL - Invalid arguments passed to function.
|
||||||
|
* ENOMEM - Not enough memory to allocate necessary buffers.
|
||||||
|
* EIO - I/O error occurred or damaged filesystem.
|
||||||
|
* EEXIST - Such attribute already present in attribute list.
|
||||||
|
*/
|
||||||
|
int ntfs_attrlist_entry_add(ntfs_inode *ni, ATTR_RECORD *attr)
|
||||||
|
{
|
||||||
|
ATTR_LIST_ENTRY *ale;
|
||||||
|
MFT_REF mref;
|
||||||
|
ntfs_attr *na = NULL;
|
||||||
|
ntfs_attr_search_ctx *ctx;
|
||||||
|
u8 *new_al;
|
||||||
|
int entry_len, entry_offset, err;
|
||||||
|
|
||||||
|
ntfs_log_trace("Entering for inode 0x%llx, attr 0x%x.\n",
|
||||||
|
(long long) ni->mft_no,
|
||||||
|
(unsigned) le32_to_cpu(attr->type));
|
||||||
|
|
||||||
|
if (!ni || !attr) {
|
||||||
|
ntfs_log_trace("Invalid arguments.\n");
|
||||||
|
errno = EINVAL;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
mref = MK_LE_MREF(ni->mft_no, le16_to_cpu(ni->mrec->sequence_number));
|
||||||
|
|
||||||
|
if (ni->nr_extents == -1)
|
||||||
|
ni = ni->base_ni;
|
||||||
|
|
||||||
|
if (!NInoAttrList(ni)) {
|
||||||
|
ntfs_log_trace("Attribute list isn't present.\n");
|
||||||
|
errno = ENOENT;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Determine size and allocate memory for new attribute list. */
|
||||||
|
entry_len = (sizeof(ATTR_LIST_ENTRY) + sizeof(ntfschar) *
|
||||||
|
attr->name_length + 7) & ~7;
|
||||||
|
new_al = ntfs_calloc(ni->attr_list_size + entry_len);
|
||||||
|
if (!new_al)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
/* Find place for the new entry. */
|
||||||
|
ctx = ntfs_attr_get_search_ctx(ni, NULL);
|
||||||
|
if (!ctx) {
|
||||||
|
err = errno;
|
||||||
|
goto err_out;
|
||||||
|
}
|
||||||
|
if (!ntfs_attr_lookup(attr->type, (attr->name_length) ? (ntfschar*)
|
||||||
|
((u8*)attr + le16_to_cpu(attr->name_offset)) :
|
||||||
|
AT_UNNAMED, attr->name_length, CASE_SENSITIVE,
|
||||||
|
(attr->non_resident) ? le64_to_cpu(attr->lowest_vcn) :
|
||||||
|
0, (attr->non_resident) ? NULL : ((u8*)attr +
|
||||||
|
le16_to_cpu(attr->value_offset)), (attr->non_resident) ?
|
||||||
|
0 : le32_to_cpu(attr->value_length), ctx)) {
|
||||||
|
/* Found some extent, check it to be before new extent. */
|
||||||
|
if (ctx->al_entry->lowest_vcn == attr->lowest_vcn) {
|
||||||
|
err = EEXIST;
|
||||||
|
ntfs_log_trace("Such attribute already present in the "
|
||||||
|
"attribute list.\n");
|
||||||
|
ntfs_attr_put_search_ctx(ctx);
|
||||||
|
goto err_out;
|
||||||
|
}
|
||||||
|
/* Add new entry after this extent. */
|
||||||
|
ale = (ATTR_LIST_ENTRY*)((u8*)ctx->al_entry +
|
||||||
|
le16_to_cpu(ctx->al_entry->length));
|
||||||
|
} else {
|
||||||
|
/* Check for real errors. */
|
||||||
|
if (errno != ENOENT) {
|
||||||
|
err = errno;
|
||||||
|
ntfs_log_trace("Attribute lookup failed.\n");
|
||||||
|
ntfs_attr_put_search_ctx(ctx);
|
||||||
|
goto err_out;
|
||||||
|
}
|
||||||
|
/* No previous extents found. */
|
||||||
|
ale = ctx->al_entry;
|
||||||
|
}
|
||||||
|
/* Don't need it anymore, @ctx->al_entry points to @ni->attr_list. */
|
||||||
|
ntfs_attr_put_search_ctx(ctx);
|
||||||
|
|
||||||
|
/* Determine new entry offset. */
|
||||||
|
entry_offset = ((u8 *)ale - ni->attr_list);
|
||||||
|
/* Set pointer to new entry. */
|
||||||
|
ale = (ATTR_LIST_ENTRY *)(new_al + entry_offset);
|
||||||
|
/* Zero it to fix valgrind warning. */
|
||||||
|
memset(ale, 0, entry_len);
|
||||||
|
/* Form new entry. */
|
||||||
|
ale->type = attr->type;
|
||||||
|
ale->length = cpu_to_le16(entry_len);
|
||||||
|
ale->name_length = attr->name_length;
|
||||||
|
ale->name_offset = offsetof(ATTR_LIST_ENTRY, name);
|
||||||
|
if (attr->non_resident)
|
||||||
|
ale->lowest_vcn = attr->lowest_vcn;
|
||||||
|
else
|
||||||
|
ale->lowest_vcn = 0;
|
||||||
|
ale->mft_reference = mref;
|
||||||
|
ale->instance = attr->instance;
|
||||||
|
memcpy(ale->name, (u8 *)attr + le16_to_cpu(attr->name_offset),
|
||||||
|
attr->name_length * sizeof(ntfschar));
|
||||||
|
|
||||||
|
/* Resize $ATTRIBUTE_LIST to new length. */
|
||||||
|
na = ntfs_attr_open(ni, AT_ATTRIBUTE_LIST, AT_UNNAMED, 0);
|
||||||
|
if (!na) {
|
||||||
|
err = errno;
|
||||||
|
ntfs_log_trace("Failed to open $ATTRIBUTE_LIST attribute.\n");
|
||||||
|
goto err_out;
|
||||||
|
}
|
||||||
|
if (ntfs_attr_truncate(na, ni->attr_list_size + entry_len)) {
|
||||||
|
err = errno;
|
||||||
|
ntfs_log_trace("$ATTRIBUTE_LIST resize failed.\n");
|
||||||
|
goto err_out;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Copy entries from old attribute list to new. */
|
||||||
|
memcpy(new_al, ni->attr_list, entry_offset);
|
||||||
|
memcpy(new_al + entry_offset + entry_len, ni->attr_list +
|
||||||
|
entry_offset, ni->attr_list_size - entry_offset);
|
||||||
|
|
||||||
|
/* Set new runlist. */
|
||||||
|
free(ni->attr_list);
|
||||||
|
ni->attr_list = new_al;
|
||||||
|
ni->attr_list_size = ni->attr_list_size + entry_len;
|
||||||
|
NInoAttrListSetDirty(ni);
|
||||||
|
/* Done! */
|
||||||
|
ntfs_attr_close(na);
|
||||||
|
return 0;
|
||||||
|
err_out:
|
||||||
|
if (na)
|
||||||
|
ntfs_attr_close(na);
|
||||||
|
free(new_al);
|
||||||
|
errno = err;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ntfs_attrlist_entry_rm - remove an attribute list attribute entry
|
||||||
|
* @ctx: attribute search context describing the attribute list entry
|
||||||
|
*
|
||||||
|
* Remove the attribute list entry @ctx->al_entry from the attribute list.
|
||||||
|
*
|
||||||
|
* Return 0 on success and -1 on error with errno set to the error code.
|
||||||
|
*/
|
||||||
|
int ntfs_attrlist_entry_rm(ntfs_attr_search_ctx *ctx)
|
||||||
|
{
|
||||||
|
u8 *new_al;
|
||||||
|
int new_al_len;
|
||||||
|
ntfs_inode *base_ni;
|
||||||
|
ntfs_attr *na;
|
||||||
|
ATTR_LIST_ENTRY *ale;
|
||||||
|
int err;
|
||||||
|
|
||||||
|
if (!ctx || !ctx->ntfs_ino || !ctx->al_entry) {
|
||||||
|
ntfs_log_trace("Invalid arguments.\n");
|
||||||
|
errno = EINVAL;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ctx->base_ntfs_ino)
|
||||||
|
base_ni = ctx->base_ntfs_ino;
|
||||||
|
else
|
||||||
|
base_ni = ctx->ntfs_ino;
|
||||||
|
ale = ctx->al_entry;
|
||||||
|
|
||||||
|
ntfs_log_trace("Entering for inode 0x%llx, attr 0x%x, lowest_vcn %lld.\n",
|
||||||
|
(long long) ctx->ntfs_ino->mft_no,
|
||||||
|
(unsigned) le32_to_cpu(ctx->al_entry->type),
|
||||||
|
(long long) le64_to_cpu(ctx->al_entry->lowest_vcn));
|
||||||
|
|
||||||
|
if (!NInoAttrList(base_ni)) {
|
||||||
|
ntfs_log_trace("Attribute list isn't present.\n");
|
||||||
|
errno = ENOENT;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Allocate memory for new attribute list. */
|
||||||
|
new_al_len = base_ni->attr_list_size - le16_to_cpu(ale->length);
|
||||||
|
new_al = ntfs_calloc(new_al_len);
|
||||||
|
if (!new_al)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
/* Reisze $ATTRIBUTE_LIST to new length. */
|
||||||
|
na = ntfs_attr_open(base_ni, AT_ATTRIBUTE_LIST, AT_UNNAMED, 0);
|
||||||
|
if (!na) {
|
||||||
|
err = errno;
|
||||||
|
ntfs_log_trace("Failed to open $ATTRIBUTE_LIST attribute.\n");
|
||||||
|
goto err_out;
|
||||||
|
}
|
||||||
|
if (ntfs_attr_truncate(na, new_al_len)) {
|
||||||
|
err = errno;
|
||||||
|
ntfs_log_trace("$ATTRIBUTE_LIST resize failed.\n");
|
||||||
|
goto err_out;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Copy entries from old attribute list to new. */
|
||||||
|
memcpy(new_al, base_ni->attr_list, (u8*)ale - base_ni->attr_list);
|
||||||
|
memcpy(new_al + ((u8*)ale - base_ni->attr_list), (u8*)ale + le16_to_cpu(
|
||||||
|
ale->length), new_al_len - ((u8*)ale - base_ni->attr_list));
|
||||||
|
|
||||||
|
/* Set new runlist. */
|
||||||
|
free(base_ni->attr_list);
|
||||||
|
base_ni->attr_list = new_al;
|
||||||
|
base_ni->attr_list_size = new_al_len;
|
||||||
|
NInoAttrListSetDirty(base_ni);
|
||||||
|
/* Done! */
|
||||||
|
ntfs_attr_close(na);
|
||||||
|
return 0;
|
||||||
|
err_out:
|
||||||
|
if (na)
|
||||||
|
ntfs_attr_close(na);
|
||||||
|
free(new_al);
|
||||||
|
errno = err;
|
||||||
|
return -1;
|
||||||
|
}
|
51
source/libntfs/attrlist.h
Normal file
51
source/libntfs/attrlist.h
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
/*
|
||||||
|
* attrlist.h - Exports for attribute list attribute handling.
|
||||||
|
* Originated from Linux-NTFS project.
|
||||||
|
*
|
||||||
|
* Copyright (c) 2004 Anton Altaparmakov
|
||||||
|
* Copyright (c) 2004 Yura Pakhuchiy
|
||||||
|
*
|
||||||
|
* This program/include file 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/include file 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
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _NTFS_ATTRLIST_H
|
||||||
|
#define _NTFS_ATTRLIST_H
|
||||||
|
|
||||||
|
#include "attrib.h"
|
||||||
|
|
||||||
|
extern int ntfs_attrlist_need(ntfs_inode *ni);
|
||||||
|
|
||||||
|
extern int ntfs_attrlist_entry_add(ntfs_inode *ni, ATTR_RECORD *attr);
|
||||||
|
extern int ntfs_attrlist_entry_rm(ntfs_attr_search_ctx *ctx);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ntfs_attrlist_mark_dirty - set the attribute list dirty
|
||||||
|
* @ni: ntfs inode which base inode contain dirty attribute list
|
||||||
|
*
|
||||||
|
* Set the attribute list dirty so it is written out later (at the latest at
|
||||||
|
* ntfs_inode_close() time).
|
||||||
|
*
|
||||||
|
* This function cannot fail.
|
||||||
|
*/
|
||||||
|
static __inline__ void ntfs_attrlist_mark_dirty(ntfs_inode *ni)
|
||||||
|
{
|
||||||
|
if (ni->nr_extents == -1)
|
||||||
|
NInoAttrListSetDirty(ni->base_ni);
|
||||||
|
else
|
||||||
|
NInoAttrListSetDirty(ni);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif /* defined _NTFS_ATTRLIST_H */
|
300
source/libntfs/bitmap.c
Normal file
300
source/libntfs/bitmap.c
Normal file
@ -0,0 +1,300 @@
|
|||||||
|
/**
|
||||||
|
* bitmap.c - Bitmap handling code. Originated from the Linux-NTFS project.
|
||||||
|
*
|
||||||
|
* Copyright (c) 2002-2006 Anton Altaparmakov
|
||||||
|
* Copyright (c) 2004-2005 Richard Russon
|
||||||
|
* Copyright (c) 2004-2008 Szabolcs Szakacsits
|
||||||
|
*
|
||||||
|
* This program/include file 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/include file 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
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifdef HAVE_CONFIG_H
|
||||||
|
#include "config.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef HAVE_STDLIB_H
|
||||||
|
#include <stdlib.h>
|
||||||
|
#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
|
||||||
|
|
||||||
|
#include "types.h"
|
||||||
|
#include "attrib.h"
|
||||||
|
#include "bitmap.h"
|
||||||
|
#include "debug.h"
|
||||||
|
#include "logging.h"
|
||||||
|
#include "misc.h"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ntfs_bit_set - set a bit in a field of bits
|
||||||
|
* @bitmap: field of bits
|
||||||
|
* @bit: bit to set
|
||||||
|
* @new_value: value to set bit to (0 or 1)
|
||||||
|
*
|
||||||
|
* Set the bit @bit in the @bitmap to @new_value. Ignore all errors.
|
||||||
|
*/
|
||||||
|
void ntfs_bit_set(u8 *bitmap, const u64 bit, const u8 new_value)
|
||||||
|
{
|
||||||
|
if (!bitmap || new_value > 1)
|
||||||
|
return;
|
||||||
|
if (!new_value)
|
||||||
|
bitmap[bit >> 3] &= ~(1 << (bit & 7));
|
||||||
|
else
|
||||||
|
bitmap[bit >> 3] |= (1 << (bit & 7));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ntfs_bit_get - get value of a bit in a field of bits
|
||||||
|
* @bitmap: field of bits
|
||||||
|
* @bit: bit to get
|
||||||
|
*
|
||||||
|
* Get and return the value of the bit @bit in @bitmap (0 or 1).
|
||||||
|
* Return -1 on error.
|
||||||
|
*/
|
||||||
|
char ntfs_bit_get(const u8 *bitmap, const u64 bit)
|
||||||
|
{
|
||||||
|
if (!bitmap)
|
||||||
|
return -1;
|
||||||
|
return (bitmap[bit >> 3] >> (bit & 7)) & 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ntfs_bit_get_and_set - get value of a bit in a field of bits and set it
|
||||||
|
* @bitmap: field of bits
|
||||||
|
* @bit: bit to get/set
|
||||||
|
* @new_value: value to set bit to (0 or 1)
|
||||||
|
*
|
||||||
|
* Return the value of the bit @bit and set it to @new_value (0 or 1).
|
||||||
|
* Return -1 on error.
|
||||||
|
*/
|
||||||
|
char ntfs_bit_get_and_set(u8 *bitmap, const u64 bit, const u8 new_value)
|
||||||
|
{
|
||||||
|
register u8 old_bit, shift;
|
||||||
|
|
||||||
|
if (!bitmap || new_value > 1)
|
||||||
|
return -1;
|
||||||
|
shift = bit & 7;
|
||||||
|
old_bit = (bitmap[bit >> 3] >> shift) & 1;
|
||||||
|
if (new_value != old_bit)
|
||||||
|
bitmap[bit >> 3] ^= 1 << shift;
|
||||||
|
return old_bit;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ntfs_bitmap_set_bits_in_run - set a run of bits in a bitmap to a value
|
||||||
|
* @na: attribute containing the bitmap
|
||||||
|
* @start_bit: first bit to set
|
||||||
|
* @count: number of bits to set
|
||||||
|
* @value: value to set the bits to (i.e. 0 or 1)
|
||||||
|
*
|
||||||
|
* Set @count bits starting at bit @start_bit in the bitmap described by the
|
||||||
|
* attribute @na to @value, where @value is either 0 or 1.
|
||||||
|
*
|
||||||
|
* On success return 0 and on error return -1 with errno set to the error code.
|
||||||
|
*/
|
||||||
|
static int ntfs_bitmap_set_bits_in_run(ntfs_attr *na, s64 start_bit,
|
||||||
|
s64 count, int value)
|
||||||
|
{
|
||||||
|
s64 bufsize, br;
|
||||||
|
u8 *buf, *lastbyte_buf;
|
||||||
|
int bit, firstbyte, lastbyte, lastbyte_pos, tmp, ret = -1;
|
||||||
|
|
||||||
|
if (!na || start_bit < 0 || count < 0) {
|
||||||
|
errno = EINVAL;
|
||||||
|
ntfs_log_perror("%s: Invalid argument (%p, %lld, %lld)",
|
||||||
|
__FUNCTION__, na, (long long)start_bit, (long long)count);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
bit = start_bit & 7;
|
||||||
|
if (bit)
|
||||||
|
firstbyte = 1;
|
||||||
|
else
|
||||||
|
firstbyte = 0;
|
||||||
|
|
||||||
|
/* Calculate the required buffer size in bytes, capping it at 8kiB. */
|
||||||
|
bufsize = ((count - (bit ? 8 - bit : 0) + 7) >> 3) + firstbyte;
|
||||||
|
if (bufsize > 8192)
|
||||||
|
bufsize = 8192;
|
||||||
|
|
||||||
|
buf = ntfs_malloc(bufsize);
|
||||||
|
if (!buf)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
/* Depending on @value, zero or set all bits in the allocated buffer. */
|
||||||
|
memset(buf, value ? 0xff : 0, bufsize);
|
||||||
|
|
||||||
|
/* If there is a first partial byte... */
|
||||||
|
if (bit) {
|
||||||
|
/* read it in... */
|
||||||
|
br = ntfs_attr_pread(na, start_bit >> 3, 1, buf);
|
||||||
|
if (br != 1) {
|
||||||
|
if (br >= 0)
|
||||||
|
errno = EIO;
|
||||||
|
goto free_err_out;
|
||||||
|
}
|
||||||
|
/* and set or clear the appropriate bits in it. */
|
||||||
|
while ((bit & 7) && count--) {
|
||||||
|
if (value)
|
||||||
|
*buf |= 1 << bit++;
|
||||||
|
else
|
||||||
|
*buf &= ~(1 << bit++);
|
||||||
|
}
|
||||||
|
/* Update @start_bit to the new position. */
|
||||||
|
start_bit = (start_bit + 7) & ~7;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Loop until @count reaches zero. */
|
||||||
|
lastbyte = 0;
|
||||||
|
lastbyte_buf = NULL;
|
||||||
|
bit = count & 7;
|
||||||
|
do {
|
||||||
|
/* If there is a last partial byte... */
|
||||||
|
if (count > 0 && bit) {
|
||||||
|
lastbyte_pos = ((count + 7) >> 3) + firstbyte;
|
||||||
|
if (!lastbyte_pos) {
|
||||||
|
// FIXME: Eeek! BUG!
|
||||||
|
ntfs_log_error("Lastbyte is zero. Leaving "
|
||||||
|
"inconsistent metadata.\n");
|
||||||
|
errno = EIO;
|
||||||
|
goto free_err_out;
|
||||||
|
}
|
||||||
|
/* and it is in the currently loaded bitmap window... */
|
||||||
|
if (lastbyte_pos <= bufsize) {
|
||||||
|
lastbyte_buf = buf + lastbyte_pos - 1;
|
||||||
|
|
||||||
|
/* read the byte in... */
|
||||||
|
br = ntfs_attr_pread(na, (start_bit + count) >>
|
||||||
|
3, 1, lastbyte_buf);
|
||||||
|
if (br != 1) {
|
||||||
|
// FIXME: Eeek! We need rollback! (AIA)
|
||||||
|
if (br >= 0)
|
||||||
|
errno = EIO;
|
||||||
|
ntfs_log_perror("Reading of last byte "
|
||||||
|
"failed (%lld). Leaving inconsistent "
|
||||||
|
"metadata", (long long)br);
|
||||||
|
goto free_err_out;
|
||||||
|
}
|
||||||
|
/* and set/clear the appropriate bits in it. */
|
||||||
|
while (bit && count--) {
|
||||||
|
if (value)
|
||||||
|
*lastbyte_buf |= 1 << --bit;
|
||||||
|
else
|
||||||
|
*lastbyte_buf &= ~(1 << --bit);
|
||||||
|
}
|
||||||
|
/* We don't want to come back here... */
|
||||||
|
bit = 0;
|
||||||
|
/* We have a last byte that we have handled. */
|
||||||
|
lastbyte = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Write the prepared buffer to disk. */
|
||||||
|
tmp = (start_bit >> 3) - firstbyte;
|
||||||
|
br = ntfs_attr_pwrite(na, tmp, bufsize, buf);
|
||||||
|
if (br != bufsize) {
|
||||||
|
// FIXME: Eeek! We need rollback! (AIA)
|
||||||
|
if (br >= 0)
|
||||||
|
errno = EIO;
|
||||||
|
ntfs_log_perror("Failed to write buffer to bitmap "
|
||||||
|
"(%lld != %lld). Leaving inconsistent metadata",
|
||||||
|
(long long)br, (long long)bufsize);
|
||||||
|
goto free_err_out;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Update counters. */
|
||||||
|
tmp = (bufsize - firstbyte - lastbyte) << 3;
|
||||||
|
if (firstbyte) {
|
||||||
|
firstbyte = 0;
|
||||||
|
/*
|
||||||
|
* Re-set the partial first byte so a subsequent write
|
||||||
|
* of the buffer does not have stale, incorrect bits.
|
||||||
|
*/
|
||||||
|
*buf = value ? 0xff : 0;
|
||||||
|
}
|
||||||
|
start_bit += tmp;
|
||||||
|
count -= tmp;
|
||||||
|
if (bufsize > (tmp = (count + 7) >> 3))
|
||||||
|
bufsize = tmp;
|
||||||
|
|
||||||
|
if (lastbyte && count != 0) {
|
||||||
|
// FIXME: Eeek! BUG!
|
||||||
|
ntfs_log_error("Last buffer but count is not zero "
|
||||||
|
"(%lld). Leaving inconsistent metadata.\n",
|
||||||
|
(long long)count);
|
||||||
|
errno = EIO;
|
||||||
|
goto free_err_out;
|
||||||
|
}
|
||||||
|
} while (count > 0);
|
||||||
|
|
||||||
|
ret = 0;
|
||||||
|
|
||||||
|
free_err_out:
|
||||||
|
free(buf);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ntfs_bitmap_set_run - set a run of bits in a bitmap
|
||||||
|
* @na: attribute containing the bitmap
|
||||||
|
* @start_bit: first bit to set
|
||||||
|
* @count: number of bits to set
|
||||||
|
*
|
||||||
|
* Set @count bits starting at bit @start_bit in the bitmap described by the
|
||||||
|
* attribute @na.
|
||||||
|
*
|
||||||
|
* On success return 0 and on error return -1 with errno set to the error code.
|
||||||
|
*/
|
||||||
|
int ntfs_bitmap_set_run(ntfs_attr *na, s64 start_bit, s64 count)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
ntfs_log_enter("Set from bit %lld, count %lld\n",
|
||||||
|
(long long)start_bit, (long long)count);
|
||||||
|
ret = ntfs_bitmap_set_bits_in_run(na, start_bit, count, 1);
|
||||||
|
ntfs_log_leave("\n");
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ntfs_bitmap_clear_run - clear a run of bits in a bitmap
|
||||||
|
* @na: attribute containing the bitmap
|
||||||
|
* @start_bit: first bit to clear
|
||||||
|
* @count: number of bits to clear
|
||||||
|
*
|
||||||
|
* Clear @count bits starting at bit @start_bit in the bitmap described by the
|
||||||
|
* attribute @na.
|
||||||
|
*
|
||||||
|
* On success return 0 and on error return -1 with errno set to the error code.
|
||||||
|
*/
|
||||||
|
int ntfs_bitmap_clear_run(ntfs_attr *na, s64 start_bit, s64 count)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
ntfs_log_enter("Clear from bit %lld, count %lld\n",
|
||||||
|
(long long)start_bit, (long long)count);
|
||||||
|
ret = ntfs_bitmap_set_bits_in_run(na, start_bit, count, 0);
|
||||||
|
ntfs_log_leave("\n");
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
96
source/libntfs/bitmap.h
Normal file
96
source/libntfs/bitmap.h
Normal file
@ -0,0 +1,96 @@
|
|||||||
|
/*
|
||||||
|
* bitmap.h - Exports for bitmap handling. Originated from the Linux-NTFS project.
|
||||||
|
*
|
||||||
|
* Copyright (c) 2000-2004 Anton Altaparmakov
|
||||||
|
* Copyright (c) 2004-2005 Richard Russon
|
||||||
|
*
|
||||||
|
* This program/include file 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/include file 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
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _NTFS_BITMAP_H
|
||||||
|
#define _NTFS_BITMAP_H
|
||||||
|
|
||||||
|
#include "types.h"
|
||||||
|
#include "attrib.h"
|
||||||
|
|
||||||
|
/*
|
||||||
|
* NOTES:
|
||||||
|
*
|
||||||
|
* - Operations are 8-bit only to ensure the functions work both on little
|
||||||
|
* and big endian machines! So don't make them 32-bit ops!
|
||||||
|
* - bitmap starts at bit = 0 and ends at bit = bitmap size - 1.
|
||||||
|
* - _Caller_ has to make sure that the bit to operate on is less than the
|
||||||
|
* size of the bitmap.
|
||||||
|
*/
|
||||||
|
|
||||||
|
extern void ntfs_bit_set(u8 *bitmap, const u64 bit, const u8 new_value);
|
||||||
|
extern char ntfs_bit_get(const u8 *bitmap, const u64 bit);
|
||||||
|
extern char ntfs_bit_get_and_set(u8 *bitmap, const u64 bit, const u8 new_value);
|
||||||
|
extern int ntfs_bitmap_set_run(ntfs_attr *na, s64 start_bit, s64 count);
|
||||||
|
extern int ntfs_bitmap_clear_run(ntfs_attr *na, s64 start_bit, s64 count);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ntfs_bitmap_set_bit - set a bit in a bitmap
|
||||||
|
* @na: attribute containing the bitmap
|
||||||
|
* @bit: bit to set
|
||||||
|
*
|
||||||
|
* Set the @bit in the bitmap described by the attribute @na.
|
||||||
|
*
|
||||||
|
* On success return 0 and on error return -1 with errno set to the error code.
|
||||||
|
*/
|
||||||
|
static __inline__ int ntfs_bitmap_set_bit(ntfs_attr *na, s64 bit)
|
||||||
|
{
|
||||||
|
return ntfs_bitmap_set_run(na, bit, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ntfs_bitmap_clear_bit - clear a bit in a bitmap
|
||||||
|
* @na: attribute containing the bitmap
|
||||||
|
* @bit: bit to clear
|
||||||
|
*
|
||||||
|
* Clear @bit in the bitmap described by the attribute @na.
|
||||||
|
*
|
||||||
|
* On success return 0 and on error return -1 with errno set to the error code.
|
||||||
|
*/
|
||||||
|
static __inline__ int ntfs_bitmap_clear_bit(ntfs_attr *na, s64 bit)
|
||||||
|
{
|
||||||
|
return ntfs_bitmap_clear_run(na, bit, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* rol32 - rotate a 32-bit value left
|
||||||
|
*
|
||||||
|
* @word: value to rotate
|
||||||
|
* @shift: bits to roll
|
||||||
|
*/
|
||||||
|
static __inline__ u32 ntfs_rol32(u32 word, unsigned int shift)
|
||||||
|
{
|
||||||
|
return (word << shift) | (word >> (32 - shift));
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* ror32 - rotate a 32-bit value right
|
||||||
|
*
|
||||||
|
* @word: value to rotate
|
||||||
|
* @shift: bits to roll
|
||||||
|
*/
|
||||||
|
static __inline__ u32 ntfs_ror32(u32 word, unsigned int shift)
|
||||||
|
{
|
||||||
|
return (word >> shift) | (word << (32 - shift));
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif /* defined _NTFS_BITMAP_H */
|
||||||
|
|
285
source/libntfs/bootsect.c
Normal file
285
source/libntfs/bootsect.c
Normal file
@ -0,0 +1,285 @@
|
|||||||
|
/**
|
||||||
|
* bootsect.c - Boot sector handling code. Originated from the Linux-NTFS project.
|
||||||
|
*
|
||||||
|
* Copyright (c) 2000-2006 Anton Altaparmakov
|
||||||
|
* Copyright (c) 2003-2008 Szabolcs Szakacsits
|
||||||
|
* Copyright (c) 2005 Yura Pakhuchiy
|
||||||
|
*
|
||||||
|
* This program/include file 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/include file 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
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifdef HAVE_CONFIG_H
|
||||||
|
#include "config.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef HAVE_STDIO_H
|
||||||
|
#include <stdio.h>
|
||||||
|
#endif
|
||||||
|
#ifdef HAVE_STDLIB_H
|
||||||
|
#include <stdlib.h>
|
||||||
|
#endif
|
||||||
|
#ifdef HAVE_STRING_H
|
||||||
|
#include <string.h>
|
||||||
|
#endif
|
||||||
|
#ifdef HAVE_ERRNO_H
|
||||||
|
#include <errno.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "compat.h"
|
||||||
|
#include "bootsect.h"
|
||||||
|
#include "debug.h"
|
||||||
|
#include "logging.h"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const char *last_sector_error =
|
||||||
|
"HINTS: Either the volume is a RAID/LDM but it wasn't setup yet,\n"
|
||||||
|
" or it was not setup correctly (e.g. by not using mdadm --build ...),\n"
|
||||||
|
" or a wrong device is tried to be mounted,\n"
|
||||||
|
" or the partition table is corrupt (partition is smaller than NTFS),\n"
|
||||||
|
" or the NTFS boot sector is corrupt (NTFS size is not valid).\n";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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;
|
||||||
|
}
|
||||||
|
|
42
source/libntfs/bootsect.h
Normal file
42
source/libntfs/bootsect.h
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
/*
|
||||||
|
* bootsect.h - Exports for bootsector record handling.
|
||||||
|
* Originated from the Linux-NTFS project.
|
||||||
|
*
|
||||||
|
* Copyright (c) 2000-2002 Anton Altaparmakov
|
||||||
|
* Copyright (c) 2006 Szabolcs Szakacsits
|
||||||
|
*
|
||||||
|
* This program/include file 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/include file 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
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _NTFS_BOOTSECT_H
|
||||||
|
#define _NTFS_BOOTSECT_H
|
||||||
|
|
||||||
|
#include "types.h"
|
||||||
|
#include "volume.h"
|
||||||
|
#include "layout.h"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ntfs_boot_sector_is_ntfs - check a boot sector for describing an ntfs volume
|
||||||
|
* @b: buffer containing the boot sector
|
||||||
|
*
|
||||||
|
* This function checks the boot sector in @b for describing a valid ntfs
|
||||||
|
* volume. Return TRUE if @b is a valid NTFS boot sector or FALSE otherwise.
|
||||||
|
*/
|
||||||
|
extern BOOL ntfs_boot_sector_is_ntfs(NTFS_BOOT_SECTOR *b);
|
||||||
|
extern int ntfs_boot_sector_parse(ntfs_volume *vol, const NTFS_BOOT_SECTOR *bs);
|
||||||
|
|
||||||
|
#endif /* defined _NTFS_BOOTSECT_H */
|
||||||
|
|
324
source/libntfs/cache.c
Normal file
324
source/libntfs/cache.c
Normal file
@ -0,0 +1,324 @@
|
|||||||
|
/*
|
||||||
|
cache.c
|
||||||
|
The cache is not visible to the user. It should be flushed
|
||||||
|
when any file is closed or changes are made to the filesystem.
|
||||||
|
|
||||||
|
This cache implements a least-used-page replacement policy. This will
|
||||||
|
distribute sectors evenly over the pages, so if less than the maximum
|
||||||
|
pages are used at once, they should all eventually remain in the cache.
|
||||||
|
This also has the benefit of throwing out old sectors, so as not to keep
|
||||||
|
too many stale pages around.
|
||||||
|
|
||||||
|
Copyright (c) 2006 Michael "Chishm" Chisholm
|
||||||
|
Copyright (c) 2009 shareese, rodries
|
||||||
|
|
||||||
|
Redistribution and use in source and binary forms, with or without modification,
|
||||||
|
are permitted provided that the following conditions are met:
|
||||||
|
|
||||||
|
1. Redistributions of source code must retain the above copyright notice,
|
||||||
|
this list of conditions and the following disclaimer.
|
||||||
|
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||||
|
this list of conditions and the following disclaimer in the documentation and/or
|
||||||
|
other materials provided with the distribution.
|
||||||
|
3. The name of the author may not be used to endorse or promote products derived
|
||||||
|
from this software without specific prior written permission.
|
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
||||||
|
WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||||
|
AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
|
||||||
|
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||||
|
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||||
|
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
||||||
|
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
#include <limits.h>
|
||||||
|
|
||||||
|
//#include "common.h"
|
||||||
|
#include "cache.h"
|
||||||
|
//#include "disc.h"
|
||||||
|
|
||||||
|
#include "mem_allocate.h"
|
||||||
|
#include <ogc/lwp_watchdog.h>
|
||||||
|
//#include "bit_ops.h"
|
||||||
|
//#include "file_allocation_table.h"
|
||||||
|
|
||||||
|
#define CACHE_FREE UINT_MAX
|
||||||
|
|
||||||
|
NTFS_CACHE* _NTFS_cache_constructor (unsigned int numberOfPages, unsigned int sectorsPerPage, const DISC_INTERFACE* discInterface, sec_t endOfPartition) {
|
||||||
|
NTFS_CACHE* cache;
|
||||||
|
unsigned int i;
|
||||||
|
NTFS_CACHE_ENTRY* cacheEntries;
|
||||||
|
|
||||||
|
if(numberOfPages==0 || sectorsPerPage==0) return NULL;
|
||||||
|
|
||||||
|
if (numberOfPages < 4) {
|
||||||
|
numberOfPages = 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sectorsPerPage < 32) {
|
||||||
|
sectorsPerPage = 32;
|
||||||
|
}
|
||||||
|
|
||||||
|
cache = (NTFS_CACHE*) ntfs_alloc (sizeof(NTFS_CACHE));
|
||||||
|
if (cache == NULL) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
cache->disc = discInterface;
|
||||||
|
cache->endOfPartition = endOfPartition;
|
||||||
|
cache->numberOfPages = numberOfPages;
|
||||||
|
cache->sectorsPerPage = sectorsPerPage;
|
||||||
|
|
||||||
|
|
||||||
|
cacheEntries = (NTFS_CACHE_ENTRY*) ntfs_alloc ( sizeof(NTFS_CACHE_ENTRY) * numberOfPages);
|
||||||
|
if (cacheEntries == NULL) {
|
||||||
|
ntfs_free (cache);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < numberOfPages; i++) {
|
||||||
|
cacheEntries[i].sector = CACHE_FREE;
|
||||||
|
cacheEntries[i].count = 0;
|
||||||
|
cacheEntries[i].last_access = 0;
|
||||||
|
cacheEntries[i].dirty = false;
|
||||||
|
cacheEntries[i].cache = (uint8_t*) ntfs_align ( sectorsPerPage * BYTES_PER_READ );
|
||||||
|
}
|
||||||
|
|
||||||
|
cache->cacheEntries = cacheEntries;
|
||||||
|
|
||||||
|
return cache;
|
||||||
|
}
|
||||||
|
|
||||||
|
void _NTFS_cache_destructor (NTFS_CACHE* cache) {
|
||||||
|
unsigned int i;
|
||||||
|
|
||||||
|
if(cache==NULL) return;
|
||||||
|
|
||||||
|
// Clear out cache before destroying it
|
||||||
|
_NTFS_cache_flush(cache);
|
||||||
|
|
||||||
|
// Free memory in reverse allocation order
|
||||||
|
for (i = 0; i < cache->numberOfPages; i++) {
|
||||||
|
ntfs_free (cache->cacheEntries[i].cache);
|
||||||
|
}
|
||||||
|
ntfs_free (cache->cacheEntries);
|
||||||
|
ntfs_free (cache);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static inline u64 accessTime(){ return gettime(); }
|
||||||
|
|
||||||
|
static NTFS_CACHE_ENTRY* _NTFS_cache_getPage(NTFS_CACHE *cache,sec_t sector)
|
||||||
|
{
|
||||||
|
unsigned int i;
|
||||||
|
NTFS_CACHE_ENTRY* cacheEntries = cache->cacheEntries;
|
||||||
|
unsigned int numberOfPages = cache->numberOfPages;
|
||||||
|
unsigned int sectorsPerPage = cache->sectorsPerPage;
|
||||||
|
|
||||||
|
unsigned int oldUsed = 0;
|
||||||
|
unsigned int oldAccess = UINT_MAX;
|
||||||
|
|
||||||
|
for(i=0;i<numberOfPages;i++) {
|
||||||
|
if(sector>=cacheEntries[i].sector && sector<(cacheEntries[i].sector + cacheEntries[i].count)) {
|
||||||
|
cacheEntries[i].last_access = accessTime();
|
||||||
|
return &(cacheEntries[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
if((cacheEntries[i].sector==CACHE_FREE || cacheEntries[i].last_access<oldAccess)) {
|
||||||
|
oldUsed = i;
|
||||||
|
oldAccess = cacheEntries[i].last_access;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(cacheEntries[oldUsed].dirty==true) {
|
||||||
|
if(!cache->disc->writeSectors(cacheEntries[oldUsed].sector,cacheEntries[oldUsed].count,cacheEntries[oldUsed].cache)) return NULL;
|
||||||
|
cacheEntries[oldUsed].dirty = false;
|
||||||
|
}
|
||||||
|
sector = (sector/sectorsPerPage)*sectorsPerPage; // align base sector to page size
|
||||||
|
sec_t next_page = sector + sectorsPerPage;
|
||||||
|
if(next_page > cache->endOfPartition) next_page = cache->endOfPartition;
|
||||||
|
|
||||||
|
if(!cache->disc->readSectors(sector,next_page-sector,cacheEntries[oldUsed].cache)) return NULL;
|
||||||
|
|
||||||
|
cacheEntries[oldUsed].sector = sector;
|
||||||
|
cacheEntries[oldUsed].count = next_page-sector;
|
||||||
|
cacheEntries[oldUsed].last_access = accessTime();
|
||||||
|
|
||||||
|
return &(cacheEntries[oldUsed]);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool _NTFS_cache_readSectors(NTFS_CACHE *cache,sec_t sector,sec_t numSectors,void *buffer)
|
||||||
|
{
|
||||||
|
sec_t sec;
|
||||||
|
sec_t secs_to_read;
|
||||||
|
NTFS_CACHE_ENTRY *entry;
|
||||||
|
uint8_t *dest = buffer;
|
||||||
|
|
||||||
|
while(numSectors>0) {
|
||||||
|
entry = _NTFS_cache_getPage(cache,sector);
|
||||||
|
if(entry==NULL) return false;
|
||||||
|
|
||||||
|
sec = sector - entry->sector;
|
||||||
|
secs_to_read = entry->count - sec;
|
||||||
|
if(secs_to_read>numSectors) secs_to_read = numSectors;
|
||||||
|
|
||||||
|
memcpy(dest,entry->cache + (sec*BYTES_PER_READ),(secs_to_read*BYTES_PER_READ));
|
||||||
|
|
||||||
|
dest += (secs_to_read*BYTES_PER_READ);
|
||||||
|
sector += secs_to_read;
|
||||||
|
numSectors -= secs_to_read;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Reads some data from a cache page, determined by the sector number
|
||||||
|
*/
|
||||||
|
/*
|
||||||
|
bool _NTFS_cache_readPartialSector (NTFS_CACHE* cache, void* buffer, sec_t sector, unsigned int offset, size_t size)
|
||||||
|
{
|
||||||
|
sec_t sec;
|
||||||
|
NTFS_CACHE_ENTRY *entry;
|
||||||
|
|
||||||
|
if (offset + size > BYTES_PER_READ) return false;
|
||||||
|
|
||||||
|
entry = _NTFS_cache_getPage(cache,sector);
|
||||||
|
if(entry==NULL) return false;
|
||||||
|
|
||||||
|
sec = sector - entry->sector;
|
||||||
|
memcpy(buffer,entry->cache + ((sec*BYTES_PER_READ) + offset),size);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool _NTFS_cache_readLittleEndianValue (NTFS_CACHE* cache, uint32_t *value, sec_t sector, unsigned int offset, int num_bytes) {
|
||||||
|
uint8_t buf[4];
|
||||||
|
if (!_NTFS_cache_readPartialSector(cache, buf, sector, offset, num_bytes)) return false;
|
||||||
|
|
||||||
|
switch(num_bytes) {
|
||||||
|
case 1: *value = buf[0]; break;
|
||||||
|
case 2: *value = u8array_to_u16(buf,0); break;
|
||||||
|
case 4: *value = u8array_to_u32(buf,0); break;
|
||||||
|
default: return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
/*
|
||||||
|
Writes some data to a cache page, making sure it is loaded into memory first.
|
||||||
|
*/
|
||||||
|
/*
|
||||||
|
bool _NTFS_cache_writePartialSector (NTFS_CACHE* cache, const void* buffer, sec_t sector, unsigned int offset, size_t size)
|
||||||
|
{
|
||||||
|
sec_t sec;
|
||||||
|
NTFS_CACHE_ENTRY *entry;
|
||||||
|
|
||||||
|
if (offset + size > BYTES_PER_READ) return false;
|
||||||
|
|
||||||
|
entry = _NTFS_cache_getPage(cache,sector);
|
||||||
|
if(entry==NULL) return false;
|
||||||
|
|
||||||
|
sec = sector - entry->sector;
|
||||||
|
memcpy(entry->cache + ((sec*BYTES_PER_READ) + offset),buffer,size);
|
||||||
|
|
||||||
|
entry->dirty = true;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool _NTFS_cache_writeLittleEndianValue (NTFS_CACHE* cache, const uint32_t value, sec_t sector, unsigned int offset, int size) {
|
||||||
|
uint8_t buf[4] = {0, 0, 0, 0};
|
||||||
|
|
||||||
|
switch(size) {
|
||||||
|
case 1: buf[0] = value; break;
|
||||||
|
case 2: u16_to_u8array(buf, 0, value); break;
|
||||||
|
case 4: u32_to_u8array(buf, 0, value); break;
|
||||||
|
default: return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return _NTFS_cache_writePartialSector(cache, buf, sector, offset, size);
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
/*
|
||||||
|
Writes some data to a cache page, zeroing out the page first
|
||||||
|
*/
|
||||||
|
/*
|
||||||
|
bool _NTFS_cache_eraseWritePartialSector (NTFS_CACHE* cache, const void* buffer, sec_t sector, unsigned int offset, size_t size)
|
||||||
|
{
|
||||||
|
sec_t sec;
|
||||||
|
NTFS_CACHE_ENTRY *entry;
|
||||||
|
|
||||||
|
if (offset + size > BYTES_PER_READ) return false;
|
||||||
|
|
||||||
|
entry = _NTFS_cache_getPage(cache,sector);
|
||||||
|
if(entry==NULL) return false;
|
||||||
|
|
||||||
|
sec = sector - entry->sector;
|
||||||
|
memset(entry->cache + (sec*BYTES_PER_READ),0,BYTES_PER_READ);
|
||||||
|
memcpy(entry->cache + ((sec*BYTES_PER_READ) + offset),buffer,size);
|
||||||
|
|
||||||
|
entry->dirty = true;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
bool _NTFS_cache_writeSectors (NTFS_CACHE* cache, sec_t sector, sec_t numSectors, const void* buffer)
|
||||||
|
{
|
||||||
|
sec_t sec;
|
||||||
|
sec_t secs_to_write;
|
||||||
|
NTFS_CACHE_ENTRY* entry;
|
||||||
|
const uint8_t *src = buffer;
|
||||||
|
|
||||||
|
while(numSectors>0)
|
||||||
|
{
|
||||||
|
entry = _NTFS_cache_getPage(cache,sector);
|
||||||
|
if(entry==NULL) return false;
|
||||||
|
|
||||||
|
sec = sector - entry->sector;
|
||||||
|
secs_to_write = entry->count - sec;
|
||||||
|
if(secs_to_write>numSectors) secs_to_write = numSectors;
|
||||||
|
|
||||||
|
memcpy(entry->cache + (sec*BYTES_PER_READ),src,(secs_to_write*BYTES_PER_READ));
|
||||||
|
|
||||||
|
src += (secs_to_write*BYTES_PER_READ);
|
||||||
|
sector += secs_to_write;
|
||||||
|
numSectors -= secs_to_write;
|
||||||
|
|
||||||
|
entry->dirty = true;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Flushes all dirty pages to disc, clearing the dirty flag.
|
||||||
|
*/
|
||||||
|
bool _NTFS_cache_flush (NTFS_CACHE* cache) {
|
||||||
|
unsigned int i;
|
||||||
|
if(cache==NULL) return true;
|
||||||
|
|
||||||
|
for (i = 0; i < cache->numberOfPages; i++) {
|
||||||
|
if (cache->cacheEntries[i].dirty) {
|
||||||
|
if (!cache->disc->writeSectors (cache->cacheEntries[i].sector, cache->cacheEntries[i].count, cache->cacheEntries[i].cache)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
cache->cacheEntries[i].dirty = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void _NTFS_cache_invalidate (NTFS_CACHE* cache) {
|
||||||
|
unsigned int i;
|
||||||
|
if(cache==NULL) return;
|
||||||
|
_NTFS_cache_flush(cache);
|
||||||
|
for (i = 0; i < cache->numberOfPages; i++) {
|
||||||
|
cache->cacheEntries[i].sector = CACHE_FREE;
|
||||||
|
cache->cacheEntries[i].last_access = 0;
|
||||||
|
cache->cacheEntries[i].count = 0;
|
||||||
|
cache->cacheEntries[i].dirty = false;
|
||||||
|
}
|
||||||
|
}
|
136
source/libntfs/cache.h
Normal file
136
source/libntfs/cache.h
Normal file
@ -0,0 +1,136 @@
|
|||||||
|
/*
|
||||||
|
NTFS_CACHE.h
|
||||||
|
The NTFS_CACHE is not visible to the user. It should be flushed
|
||||||
|
when any file is closed or changes are made to the filesystem.
|
||||||
|
|
||||||
|
This NTFS_CACHE implements a least-used-page replacement policy. This will
|
||||||
|
distribute sectors evenly over the pages, so if less than the maximum
|
||||||
|
pages are used at once, they should all eventually remain in the NTFS_CACHE.
|
||||||
|
This also has the benefit of throwing out old sectors, so as not to keep
|
||||||
|
too many stale pages around.
|
||||||
|
|
||||||
|
Copyright (c) 2006 Michael "Chishm" Chisholm
|
||||||
|
Copyright (c) 2009 shareese, rodries
|
||||||
|
|
||||||
|
Redistribution and use in source and binary forms, with or without modification,
|
||||||
|
are permitted provided that the following conditions are met:
|
||||||
|
|
||||||
|
1. Redistributions of source code must retain the above copyright notice,
|
||||||
|
this list of conditions and the following disclaimer.
|
||||||
|
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||||
|
this list of conditions and the following disclaimer in the documentation and/or
|
||||||
|
other materials provided with the distribution.
|
||||||
|
3. The name of the author may not be used to endorse or promote products derived
|
||||||
|
from this software without specific prior written permission.
|
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
||||||
|
WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||||
|
AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
|
||||||
|
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||||
|
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||||
|
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
||||||
|
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _CACHE_H
|
||||||
|
#define _CACHE_H
|
||||||
|
|
||||||
|
//#include "common.h"
|
||||||
|
//#include "disc.h"
|
||||||
|
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <gctypes.h>
|
||||||
|
#include <ogc/disc_io.h>
|
||||||
|
#include <gccore.h>
|
||||||
|
|
||||||
|
#define BYTES_PER_READ 512
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
sec_t sector;
|
||||||
|
unsigned int count;
|
||||||
|
u64 last_access;
|
||||||
|
bool dirty;
|
||||||
|
u8* cache;
|
||||||
|
} NTFS_CACHE_ENTRY;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
const DISC_INTERFACE* disc;
|
||||||
|
sec_t endOfPartition;
|
||||||
|
unsigned int numberOfPages;
|
||||||
|
unsigned int sectorsPerPage;
|
||||||
|
NTFS_CACHE_ENTRY* cacheEntries;
|
||||||
|
} NTFS_CACHE;
|
||||||
|
|
||||||
|
/*
|
||||||
|
Read data from a sector in the NTFS_CACHE
|
||||||
|
If the sector is not in the NTFS_CACHE, it will be swapped in
|
||||||
|
offset is the position to start reading from
|
||||||
|
size is the amount of data to read
|
||||||
|
Precondition: offset + size <= BYTES_PER_READ
|
||||||
|
*/
|
||||||
|
//bool _NTFS_cache_readPartialSector (NTFS_CACHE* NTFS_CACHE, void* buffer, sec_t sector, unsigned int offset, size_t size);
|
||||||
|
|
||||||
|
//bool _NTFS_cache_readLittleEndianValue (NTFS_CACHE* NTFS_CACHE, uint32_t *value, sec_t sector, unsigned int offset, int num_bytes);
|
||||||
|
|
||||||
|
/*
|
||||||
|
Write data to a sector in the NTFS_CACHE
|
||||||
|
If the sector is not in the NTFS_CACHE, it will be swapped in.
|
||||||
|
When the sector is swapped out, the data will be written to the disc
|
||||||
|
offset is the position to start writing to
|
||||||
|
size is the amount of data to write
|
||||||
|
Precondition: offset + size <= BYTES_PER_READ
|
||||||
|
*/
|
||||||
|
//bool _NTFS_cache_writePartialSector (NTFS_CACHE* NTFS_CACHE, const void* buffer, sec_t sector, unsigned int offset, size_t size);
|
||||||
|
|
||||||
|
//bool _NTFS_cache_writeLittleEndianValue (NTFS_CACHE* NTFS_CACHE, const uint32_t value, sec_t sector, unsigned int offset, int num_bytes);
|
||||||
|
|
||||||
|
/*
|
||||||
|
Write data to a sector in the NTFS_CACHE, zeroing the sector first
|
||||||
|
If the sector is not in the NTFS_CACHE, it will be swapped in.
|
||||||
|
When the sector is swapped out, the data will be written to the disc
|
||||||
|
offset is the position to start writing to
|
||||||
|
size is the amount of data to write
|
||||||
|
Precondition: offset + size <= BYTES_PER_READ
|
||||||
|
*/
|
||||||
|
//bool _NTFS_cache_eraseWritePartialSector (NTFS_CACHE* NTFS_CACHE, const void* buffer, sec_t sector, unsigned int offset, size_t size);
|
||||||
|
|
||||||
|
/*
|
||||||
|
Read several sectors from the NTFS_CACHE
|
||||||
|
*/
|
||||||
|
bool _NTFS_cache_readSectors (NTFS_CACHE* NTFS_CACHE, sec_t sector, sec_t numSectors, void* buffer);
|
||||||
|
|
||||||
|
/*
|
||||||
|
Read a full sector from the NTFS_CACHE
|
||||||
|
*/
|
||||||
|
//static inline bool _NTFS_cache_readSector (NTFS_CACHE* NTFS_CACHE, void* buffer, sec_t sector) {
|
||||||
|
// return _NTFS_cache_readPartialSector (NTFS_CACHE, buffer, sector, 0, BYTES_PER_READ);
|
||||||
|
//}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Write a full sector to the NTFS_CACHE
|
||||||
|
*/
|
||||||
|
//static inline bool _NTFS_cache_writeSector (NTFS_CACHE* NTFS_CACHE, const void* buffer, sec_t sector) {
|
||||||
|
// return _NTFS_cache_writePartialSector (NTFS_CACHE, buffer, sector, 0, BYTES_PER_READ);
|
||||||
|
//}
|
||||||
|
|
||||||
|
bool _NTFS_cache_writeSectors (NTFS_CACHE* NTFS_CACHE, sec_t sector, sec_t numSectors, const void* buffer);
|
||||||
|
|
||||||
|
/*
|
||||||
|
Write any dirty sectors back to disc and clear out the contents of the NTFS_CACHE
|
||||||
|
*/
|
||||||
|
bool _NTFS_cache_flush (NTFS_CACHE* NTFS_CACHE);
|
||||||
|
|
||||||
|
/*
|
||||||
|
Clear out the contents of the NTFS_CACHE without writing any dirty sectors first
|
||||||
|
*/
|
||||||
|
void _NTFS_cache_invalidate (NTFS_CACHE* NTFS_CACHE);
|
||||||
|
|
||||||
|
NTFS_CACHE* _NTFS_cache_constructor (unsigned int numberOfPages, unsigned int sectorsPerPage, const DISC_INTERFACE* discInterface, sec_t endOfPartition);
|
||||||
|
|
||||||
|
void _NTFS_cache_destructor (NTFS_CACHE* NTFS_CACHE);
|
||||||
|
|
||||||
|
#endif // _CACHE_H
|
||||||
|
|
316
source/libntfs/collate.c
Normal file
316
source/libntfs/collate.c
Normal file
@ -0,0 +1,316 @@
|
|||||||
|
/**
|
||||||
|
* collate.c - NTFS collation handling. Originated from the Linux-NTFS project.
|
||||||
|
*
|
||||||
|
* Copyright (c) 2004 Anton Altaparmakov
|
||||||
|
* Copyright (c) 2005 Yura Pakhuchiy
|
||||||
|
*
|
||||||
|
* This program/include file 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/include file 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
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifdef HAVE_CONFIG_H
|
||||||
|
#include "config.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef HAVE_STRING_H
|
||||||
|
#include <string.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "collate.h"
|
||||||
|
#include "debug.h"
|
||||||
|
#include "unistr.h"
|
||||||
|
#include "logging.h"
|
||||||
|
|
||||||
|
BOOL ntfs_is_collation_rule_supported(COLLATION_RULES cr)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* FIXME: At the moment we only support COLLATION_BINARY,
|
||||||
|
* COLLATION_NTOFS_ULONG and COLLATION_FILE_NAME so we return false
|
||||||
|
* for everything else.
|
||||||
|
* JPA added COLLATION_NTOFS_SECURITY_HASH
|
||||||
|
*/
|
||||||
|
if (cr != COLLATION_BINARY && cr != COLLATION_NTOFS_ULONG
|
||||||
|
&& cr != COLLATION_FILE_NAME
|
||||||
|
&& cr != COLLATION_NTOFS_SECURITY_HASH
|
||||||
|
&& cr != COLLATION_NTOFS_ULONGS)
|
||||||
|
return FALSE;
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ntfs_collate_binary - Which of two binary objects should be listed first
|
||||||
|
* @vol: unused
|
||||||
|
* @data1:
|
||||||
|
* @data1_len:
|
||||||
|
* @data2:
|
||||||
|
* @data2_len:
|
||||||
|
*
|
||||||
|
* Description...
|
||||||
|
*
|
||||||
|
* Returns:
|
||||||
|
*/
|
||||||
|
static int ntfs_collate_binary(ntfs_volume *vol __attribute__((unused)),
|
||||||
|
const void *data1, const int data1_len,
|
||||||
|
const void *data2, const int data2_len)
|
||||||
|
{
|
||||||
|
int rc;
|
||||||
|
|
||||||
|
ntfs_log_trace("Entering.\n");
|
||||||
|
rc = memcmp(data1, data2, min(data1_len, data2_len));
|
||||||
|
if (!rc && (data1_len != data2_len)) {
|
||||||
|
if (data1_len < data2_len)
|
||||||
|
rc = -1;
|
||||||
|
else
|
||||||
|
rc = 1;
|
||||||
|
}
|
||||||
|
ntfs_log_trace("Done, returning %i.\n", rc);
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ntfs_collate_ntofs_ulong - Which of two long ints should be listed first
|
||||||
|
* @vol: unused
|
||||||
|
* @data1:
|
||||||
|
* @data1_len:
|
||||||
|
* @data2:
|
||||||
|
* @data2_len:
|
||||||
|
*
|
||||||
|
* Description...
|
||||||
|
*
|
||||||
|
* Returns:
|
||||||
|
*/
|
||||||
|
static int ntfs_collate_ntofs_ulong(ntfs_volume *vol __attribute__((unused)),
|
||||||
|
const void *data1, const int data1_len,
|
||||||
|
const void *data2, const int data2_len)
|
||||||
|
{
|
||||||
|
int rc;
|
||||||
|
u32 d1, d2;
|
||||||
|
|
||||||
|
ntfs_log_trace("Entering.\n");
|
||||||
|
if (data1_len != data2_len || data1_len != 4) {
|
||||||
|
ntfs_log_error("data1_len or/and data2_len not equal to 4.\n");
|
||||||
|
return NTFS_COLLATION_ERROR;
|
||||||
|
}
|
||||||
|
d1 = le32_to_cpup(data1);
|
||||||
|
d2 = le32_to_cpup(data2);
|
||||||
|
if (d1 < d2)
|
||||||
|
rc = -1;
|
||||||
|
else {
|
||||||
|
if (d1 == d2)
|
||||||
|
rc = 0;
|
||||||
|
else
|
||||||
|
rc = 1;
|
||||||
|
}
|
||||||
|
ntfs_log_trace("Done, returning %i.\n", rc);
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ntfs_collate_ntofs_ulongs - Which of two le32 arrays should be listed first
|
||||||
|
*
|
||||||
|
* Returns: -1, 0 or 1 depending of how the arrays compare
|
||||||
|
*/
|
||||||
|
|
||||||
|
static int ntfs_collate_ntofs_ulongs(ntfs_volume *vol __attribute__((unused)),
|
||||||
|
const void *data1, const int data1_len,
|
||||||
|
const void *data2, const int data2_len)
|
||||||
|
{
|
||||||
|
int rc;
|
||||||
|
int len;
|
||||||
|
const le32 *p1, *p2;
|
||||||
|
u32 d1, d2;
|
||||||
|
|
||||||
|
ntfs_log_trace("Entering.\n");
|
||||||
|
if ((data1_len != data2_len) || (data1_len <= 0) || (data1_len & 3)) {
|
||||||
|
ntfs_log_error("data1_len or data2_len not valid\n");
|
||||||
|
return NTFS_COLLATION_ERROR;
|
||||||
|
}
|
||||||
|
p1 = (const le32*)data1;
|
||||||
|
p2 = (const le32*)data2;
|
||||||
|
len = data1_len;
|
||||||
|
do {
|
||||||
|
d1 = le32_to_cpup(p1);
|
||||||
|
p1++;
|
||||||
|
d2 = le32_to_cpup(p2);
|
||||||
|
p2++;
|
||||||
|
} while ((d1 == d2) && ((len -= 4) > 0));
|
||||||
|
if (d1 < d2)
|
||||||
|
rc = -1;
|
||||||
|
else {
|
||||||
|
if (d1 == d2)
|
||||||
|
rc = 0;
|
||||||
|
else
|
||||||
|
rc = 1;
|
||||||
|
}
|
||||||
|
ntfs_log_trace("Done, returning %i.\n", rc);
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ntfs_collate_ntofs_security_hash - Which of two security descriptors
|
||||||
|
* should be listed first
|
||||||
|
* @vol: unused
|
||||||
|
* @data1:
|
||||||
|
* @data1_len:
|
||||||
|
* @data2:
|
||||||
|
* @data2_len:
|
||||||
|
*
|
||||||
|
* JPA compare two security hash keys made of two unsigned le32
|
||||||
|
*
|
||||||
|
* Returns: -1, 0 or 1 depending of how the keys compare
|
||||||
|
*/
|
||||||
|
static int ntfs_collate_ntofs_security_hash(ntfs_volume *vol __attribute__((unused)),
|
||||||
|
const void *data1, const int data1_len,
|
||||||
|
const void *data2, const int data2_len)
|
||||||
|
{
|
||||||
|
int rc;
|
||||||
|
u32 d1, d2;
|
||||||
|
const u32 *p1, *p2;
|
||||||
|
|
||||||
|
ntfs_log_trace("Entering.\n");
|
||||||
|
if (data1_len != data2_len || data1_len != 8) {
|
||||||
|
ntfs_log_error("data1_len or/and data2_len not equal to 8.\n");
|
||||||
|
return NTFS_COLLATION_ERROR;
|
||||||
|
}
|
||||||
|
p1 = (const u32*)data1;
|
||||||
|
p2 = (const u32*)data2;
|
||||||
|
d1 = le32_to_cpup(p1);
|
||||||
|
d2 = le32_to_cpup(p2);
|
||||||
|
if (d1 < d2)
|
||||||
|
rc = -1;
|
||||||
|
else {
|
||||||
|
if (d1 > d2)
|
||||||
|
rc = 1;
|
||||||
|
else {
|
||||||
|
p1++;
|
||||||
|
p2++;
|
||||||
|
d1 = le32_to_cpup(p1);
|
||||||
|
d2 = le32_to_cpup(p2);
|
||||||
|
if (d1 < d2)
|
||||||
|
rc = -1;
|
||||||
|
else {
|
||||||
|
if (d1 > d2)
|
||||||
|
rc = 1;
|
||||||
|
else
|
||||||
|
rc = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ntfs_log_trace("Done, returning %i.\n", rc);
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ntfs_collate_file_name - Which of two filenames should be listed first
|
||||||
|
* @vol:
|
||||||
|
* @data1:
|
||||||
|
* @data1_len: unused
|
||||||
|
* @data2:
|
||||||
|
* @data2_len: unused
|
||||||
|
*
|
||||||
|
* Description...
|
||||||
|
*
|
||||||
|
* Returns:
|
||||||
|
*/
|
||||||
|
static int ntfs_collate_file_name(ntfs_volume *vol,
|
||||||
|
const void *data1, const int data1_len __attribute__((unused)),
|
||||||
|
const void *data2, const int data2_len __attribute__((unused)))
|
||||||
|
{
|
||||||
|
int rc;
|
||||||
|
|
||||||
|
ntfs_log_trace("Entering.\n");
|
||||||
|
rc = ntfs_file_values_compare(data1, data2, NTFS_COLLATION_ERROR,
|
||||||
|
IGNORE_CASE, vol->upcase, vol->upcase_len);
|
||||||
|
if (!rc)
|
||||||
|
rc = ntfs_file_values_compare(data1, data2,
|
||||||
|
NTFS_COLLATION_ERROR, CASE_SENSITIVE,
|
||||||
|
vol->upcase, vol->upcase_len);
|
||||||
|
ntfs_log_trace("Done, returning %i.\n", rc);
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
typedef int (*ntfs_collate_func_t)(ntfs_volume *, const void *, const int,
|
||||||
|
const void *, const int);
|
||||||
|
|
||||||
|
static ntfs_collate_func_t ntfs_do_collate0x0[3] = {
|
||||||
|
ntfs_collate_binary,
|
||||||
|
ntfs_collate_file_name,
|
||||||
|
NULL/*ntfs_collate_unicode_string*/,
|
||||||
|
};
|
||||||
|
|
||||||
|
static ntfs_collate_func_t ntfs_do_collate0x1[4] = {
|
||||||
|
ntfs_collate_ntofs_ulong,
|
||||||
|
NULL/*ntfs_collate_ntofs_sid*/,
|
||||||
|
ntfs_collate_ntofs_security_hash,
|
||||||
|
ntfs_collate_ntofs_ulongs
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ntfs_collate - collate two data items using a specified collation rule
|
||||||
|
* @vol: ntfs volume to which the data items belong
|
||||||
|
* @cr: collation rule to use when comparing the items
|
||||||
|
* @data1: first data item to collate
|
||||||
|
* @data1_len: length in bytes of @data1
|
||||||
|
* @data2: second data item to collate
|
||||||
|
* @data2_len: length in bytes of @data2
|
||||||
|
*
|
||||||
|
* Collate the two data items @data1 and @data2 using the collation rule @cr
|
||||||
|
* and return -1, 0, or 1 if @data1 is found, respectively, to collate before,
|
||||||
|
* to match, or to collate after @data2.
|
||||||
|
*
|
||||||
|
* For speed we use the collation rule @cr as an index into two tables of
|
||||||
|
* function pointers to call the appropriate collation function.
|
||||||
|
*
|
||||||
|
* Return NTFS_COLLATION_ERROR if error occurred.
|
||||||
|
*/
|
||||||
|
int ntfs_collate(ntfs_volume *vol, COLLATION_RULES cr,
|
||||||
|
const void *data1, const int data1_len,
|
||||||
|
const void *data2, const int data2_len)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
ntfs_log_trace("Entering.\n");
|
||||||
|
if (!vol || !data1 || !data2 || data1_len < 0 || data2_len < 0) {
|
||||||
|
ntfs_log_error("Invalid arguments passed.\n");
|
||||||
|
return NTFS_COLLATION_ERROR;
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
* FIXME: At the moment we only support COLLATION_BINARY,
|
||||||
|
* COLLATION_NTOFS_ULONG and COLLATION_FILE_NAME so we return error
|
||||||
|
* for everything else.
|
||||||
|
* JPA added COLLATION_NTOFS_SECURITY_HASH
|
||||||
|
* JPA added COLLATION_NTOFS_ULONGS
|
||||||
|
*/
|
||||||
|
if (cr != COLLATION_BINARY && cr != COLLATION_NTOFS_ULONG
|
||||||
|
&& cr != COLLATION_FILE_NAME
|
||||||
|
&& cr != COLLATION_NTOFS_SECURITY_HASH
|
||||||
|
&& cr != COLLATION_NTOFS_ULONGS)
|
||||||
|
goto err;
|
||||||
|
i = le32_to_cpu(cr);
|
||||||
|
if (i < 0)
|
||||||
|
goto err;
|
||||||
|
if (i <= 0x02)
|
||||||
|
return ntfs_do_collate0x0[i](vol, data1, data1_len,
|
||||||
|
data2, data2_len);
|
||||||
|
if (i < 0x10)
|
||||||
|
goto err;
|
||||||
|
i -= 0x10;
|
||||||
|
if (i <= 3)
|
||||||
|
return ntfs_do_collate0x1[i](vol, data1, data1_len,
|
||||||
|
data2, data2_len);
|
||||||
|
err:
|
||||||
|
ntfs_log_debug("Unknown collation rule.\n");
|
||||||
|
return NTFS_COLLATION_ERROR;
|
||||||
|
}
|
37
source/libntfs/collate.h
Normal file
37
source/libntfs/collate.h
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
/*
|
||||||
|
* collate.h - Defines for NTFS collation handling. Originated from the Linux-NTFS
|
||||||
|
* project.
|
||||||
|
*
|
||||||
|
* Copyright (c) 2004 Anton Altaparmakov
|
||||||
|
* Copyright (c) 2005 Yura Pakhuchiy
|
||||||
|
*
|
||||||
|
* This program/include file 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/include file 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
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _NTFS_COLLATE_H
|
||||||
|
#define _NTFS_COLLATE_H
|
||||||
|
|
||||||
|
#include "types.h"
|
||||||
|
#include "volume.h"
|
||||||
|
|
||||||
|
#define NTFS_COLLATION_ERROR -2
|
||||||
|
|
||||||
|
extern BOOL ntfs_is_collation_rule_supported(COLLATION_RULES cr);
|
||||||
|
extern int ntfs_collate(ntfs_volume *vol, COLLATION_RULES cr,
|
||||||
|
const void *data1, const int data1_len,
|
||||||
|
const void *data2, const int data2_len);
|
||||||
|
|
||||||
|
#endif /* _NTFS_COLLATE_H */
|
250
source/libntfs/compat.c
Normal file
250
source/libntfs/compat.c
Normal file
@ -0,0 +1,250 @@
|
|||||||
|
/**
|
||||||
|
* compat.c - Tweaks for Windows compatibility
|
||||||
|
*
|
||||||
|
* Copyright (c) 2002 Richard Russon
|
||||||
|
* Copyright (c) 2002-2004 Anton Altaparmakov
|
||||||
|
*
|
||||||
|
* This program/include file 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/include file 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
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifdef HAVE_CONFIG_H
|
||||||
|
#include "config.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "compat.h"
|
||||||
|
|
||||||
|
#ifndef HAVE_FFS
|
||||||
|
/**
|
||||||
|
* ffs - Find the first set bit in an int
|
||||||
|
* @x:
|
||||||
|
*
|
||||||
|
* Description...
|
||||||
|
*
|
||||||
|
* Returns:
|
||||||
|
*/
|
||||||
|
int ffs(int x)
|
||||||
|
{
|
||||||
|
int r = 1;
|
||||||
|
|
||||||
|
if (!x)
|
||||||
|
return 0;
|
||||||
|
if (!(x & 0xffff)) {
|
||||||
|
x >>= 16;
|
||||||
|
r += 16;
|
||||||
|
}
|
||||||
|
if (!(x & 0xff)) {
|
||||||
|
x >>= 8;
|
||||||
|
r += 8;
|
||||||
|
}
|
||||||
|
if (!(x & 0xf)) {
|
||||||
|
x >>= 4;
|
||||||
|
r += 4;
|
||||||
|
}
|
||||||
|
if (!(x & 3)) {
|
||||||
|
x >>= 2;
|
||||||
|
r += 2;
|
||||||
|
}
|
||||||
|
if (!(x & 1)) {
|
||||||
|
x >>= 1;
|
||||||
|
r += 1;
|
||||||
|
}
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
#endif /* HAVE_FFS */
|
||||||
|
|
||||||
|
#ifndef HAVE_DAEMON
|
||||||
|
/* ************************************************************
|
||||||
|
* From: src.opensolaris.org
|
||||||
|
* src/lib/libresolv2/common/bsd/daemon.c
|
||||||
|
*/
|
||||||
|
/*
|
||||||
|
* Copyright (c) 1997-2000 by Sun Microsystems, Inc.
|
||||||
|
* All rights reserved.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#if defined(LIBC_SCCS) && !defined(lint)
|
||||||
|
static const char sccsid[] = "@(#)daemon.c 8.1 (Berkeley) 6/4/93";
|
||||||
|
static const char rcsid[] = "$Id: compat.c,v 1.1.1.1.2.7 2009/03/27 09:09:59 jpandre Exp $";
|
||||||
|
#endif /* LIBC_SCCS and not lint */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright (c) 1990, 1993
|
||||||
|
* The Regents of the University of California. All rights reserved.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions
|
||||||
|
* are met:
|
||||||
|
* 1. Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer.
|
||||||
|
* 2. Redistributions in binary form must reproduce the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer in the
|
||||||
|
* documentation and/or other materials provided with the distribution.
|
||||||
|
* 3. All advertising materials mentioning features or use of this software
|
||||||
|
* must display the following acknowledgement:
|
||||||
|
* This product includes software developed by the University of
|
||||||
|
* California, Berkeley and its contributors.
|
||||||
|
* 4. Neither the name of the University nor the names of its contributors
|
||||||
|
* may be used to endorse or promote products derived from this software
|
||||||
|
* without specific prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
|
||||||
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||||
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
|
||||||
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||||
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||||
|
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||||
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||||
|
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||||
|
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||||
|
* SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifdef HAVE_FCNTL_H
|
||||||
|
#include <fcntl.h>
|
||||||
|
#endif
|
||||||
|
#ifdef HAVE_UNISTD_H
|
||||||
|
#include <unistd.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
int daemon(int nochdir, int noclose) {
|
||||||
|
int fd;
|
||||||
|
|
||||||
|
switch (fork()) {
|
||||||
|
case -1:
|
||||||
|
return (-1);
|
||||||
|
case 0:
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
_exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (setsid() == -1)
|
||||||
|
return (-1);
|
||||||
|
|
||||||
|
if (!nochdir)
|
||||||
|
(void)chdir("/");
|
||||||
|
|
||||||
|
if (!noclose && (fd = open("/dev/null", O_RDWR, 0)) != -1) {
|
||||||
|
(void)dup2(fd, 0);
|
||||||
|
(void)dup2(fd, 1);
|
||||||
|
(void)dup2(fd, 2);
|
||||||
|
if (fd > 2)
|
||||||
|
(void)close (fd);
|
||||||
|
}
|
||||||
|
return (0);
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
* End: src/lib/libresolv2/common/bsd/daemon.c
|
||||||
|
*************************************************************/
|
||||||
|
#endif /* HAVE_DAEMON */
|
||||||
|
|
||||||
|
#ifndef HAVE_STRSEP
|
||||||
|
/* ************************************************************
|
||||||
|
* From: src.opensolaris.org
|
||||||
|
* src/lib/libresolv2/common/bsd/strsep.c
|
||||||
|
*/
|
||||||
|
/*
|
||||||
|
* Copyright (c) 1997, by Sun Microsystems, Inc.
|
||||||
|
* All rights reserved.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#if defined(LIBC_SCCS) && !defined(lint)
|
||||||
|
static const char sccsid[] = "strsep.c 8.1 (Berkeley) 6/4/93";
|
||||||
|
static const char rcsid[] = "$Id: compat.c,v 1.1.1.1.2.7 2009/03/27 09:09:59 jpandre Exp $";
|
||||||
|
#endif /* LIBC_SCCS and not lint */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright (c) 1990, 1993
|
||||||
|
* The Regents of the University of California. All rights reserved.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions
|
||||||
|
* are met:
|
||||||
|
* 1. Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer.
|
||||||
|
* 2. Redistributions in binary form must reproduce the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer in the
|
||||||
|
* documentation and/or other materials provided with the distribution.
|
||||||
|
* 3. All advertising materials mentioning features or use of this software
|
||||||
|
* must display the following acknowledgement:
|
||||||
|
* This product includes software developed by the University of
|
||||||
|
* California, Berkeley and its contributors.
|
||||||
|
* 4. Neither the name of the University nor the names of its contributors
|
||||||
|
* may be used to endorse or promote products derived from this software
|
||||||
|
* without specific prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
|
||||||
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||||
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
|
||||||
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||||
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||||
|
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||||
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||||
|
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||||
|
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||||
|
* SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifdef HAVE_STRING_H
|
||||||
|
#include <string.h>
|
||||||
|
#endif
|
||||||
|
#ifdef HAVE_STDIO_H
|
||||||
|
#include <stdio.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Get next token from string *stringp, where tokens are possibly-empty
|
||||||
|
* strings separated by characters from delim.
|
||||||
|
*
|
||||||
|
* Writes NULs into the string at *stringp to end tokens.
|
||||||
|
* delim need not remain constant from call to call.
|
||||||
|
* On return, *stringp points past the last NUL written (if there might
|
||||||
|
* be further tokens), or is NULL (if there are definitely no more tokens).
|
||||||
|
*
|
||||||
|
* If *stringp is NULL, strsep returns NULL.
|
||||||
|
*/
|
||||||
|
char *strsep(char **stringp, const char *delim) {
|
||||||
|
char *s;
|
||||||
|
const char *spanp;
|
||||||
|
int c, sc;
|
||||||
|
char *tok;
|
||||||
|
|
||||||
|
if ((s = *stringp) == NULL)
|
||||||
|
return (NULL);
|
||||||
|
for (tok = s;;) {
|
||||||
|
c = *s++;
|
||||||
|
spanp = delim;
|
||||||
|
do {
|
||||||
|
if ((sc = *spanp++) == c) {
|
||||||
|
if (c == 0)
|
||||||
|
s = NULL;
|
||||||
|
else
|
||||||
|
s[-1] = 0;
|
||||||
|
*stringp = s;
|
||||||
|
return (tok);
|
||||||
|
}
|
||||||
|
} while (sc != 0);
|
||||||
|
}
|
||||||
|
/* NOTREACHED */
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* End: src/lib/libresolv2/common/bsd/strsep.c
|
||||||
|
*************************************************************/
|
||||||
|
#endif /* HAVE_STRSEP */
|
||||||
|
|
87
source/libntfs/compat.h
Normal file
87
source/libntfs/compat.h
Normal file
@ -0,0 +1,87 @@
|
|||||||
|
/*
|
||||||
|
* compat.h - Tweaks for Windows compatibility.
|
||||||
|
*
|
||||||
|
* Copyright (c) 2002 Richard Russon
|
||||||
|
* Copyright (c) 2002-2004 Anton Altaparmakov
|
||||||
|
* Copyright (c) 2008-2009 Szabolcs Szakacsits
|
||||||
|
*
|
||||||
|
* This program/include file 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/include file 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
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _NTFS_COMPAT_H
|
||||||
|
#define _NTFS_COMPAT_H
|
||||||
|
|
||||||
|
#ifdef HAVE_CONFIG_H
|
||||||
|
#include "config.h"
|
||||||
|
#endif
|
||||||
|
#ifdef HAVE_SYS_PARAM_H
|
||||||
|
#include <sys/param.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef PATH_MAX
|
||||||
|
#define PATH_MAX 4096
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef HAVE_FFS
|
||||||
|
extern int ffs(int i);
|
||||||
|
#endif /* HAVE_FFS */
|
||||||
|
|
||||||
|
#ifndef HAVE_DAEMON
|
||||||
|
extern int daemon(int nochdir, int noclose);
|
||||||
|
#endif /* HAVE_DAEMON */
|
||||||
|
|
||||||
|
#ifndef HAVE_STRSEP
|
||||||
|
extern char *strsep(char **stringp, const char *delim);
|
||||||
|
#endif /* HAVE_STRSEP */
|
||||||
|
|
||||||
|
#ifdef WINDOWS
|
||||||
|
|
||||||
|
#define HAVE_STDIO_H /* mimic config.h */
|
||||||
|
#define HAVE_STDARG_H
|
||||||
|
|
||||||
|
#define atoll _atoi64
|
||||||
|
#define fdatasync commit
|
||||||
|
#define __inline__ inline
|
||||||
|
#define __attribute__(X) /*nothing*/
|
||||||
|
|
||||||
|
#else /* !defined WINDOWS */
|
||||||
|
|
||||||
|
#ifndef O_BINARY
|
||||||
|
#define O_BINARY 0 /* unix is binary by default */
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef GEKKO
|
||||||
|
|
||||||
|
#include "mem_allocate.h"
|
||||||
|
#include <limits.h>
|
||||||
|
|
||||||
|
#define XATTR_CREATE 1
|
||||||
|
#define XATTR_REPLACE 2
|
||||||
|
|
||||||
|
#define MINORBITS 20
|
||||||
|
#define MINORMASK ((1U << MINORBITS) - 1)
|
||||||
|
|
||||||
|
#define major(dev) ((unsigned int) ((dev) >> MINORBITS))
|
||||||
|
#define minor(dev) ((unsigned int) ((dev) & MINORMASK))
|
||||||
|
#define mkdev(ma,mi) (((ma) << MINORBITS) | (mi))
|
||||||
|
#define random rand
|
||||||
|
|
||||||
|
#endif /* defined GEKKO */
|
||||||
|
|
||||||
|
#endif /* defined WINDOWS */
|
||||||
|
|
||||||
|
#endif /* defined _NTFS_COMPAT_H */
|
||||||
|
|
1430
source/libntfs/compress.c
Normal file
1430
source/libntfs/compress.c
Normal file
File diff suppressed because it is too large
Load Diff
39
source/libntfs/compress.h
Normal file
39
source/libntfs/compress.h
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
/*
|
||||||
|
* compress.h - Exports for compressed attribute handling.
|
||||||
|
* Originated from the Linux-NTFS project.
|
||||||
|
*
|
||||||
|
* Copyright (c) 2004 Anton Altaparmakov
|
||||||
|
*
|
||||||
|
* This program/include file 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/include file 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
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _NTFS_COMPRESS_H
|
||||||
|
#define _NTFS_COMPRESS_H
|
||||||
|
|
||||||
|
#include "types.h"
|
||||||
|
#include "attrib.h"
|
||||||
|
|
||||||
|
extern s64 ntfs_compressed_attr_pread(ntfs_attr *na, s64 pos, s64 count,
|
||||||
|
void *b);
|
||||||
|
|
||||||
|
extern s64 ntfs_compressed_pwrite(ntfs_attr *na, runlist_element *brl, s64 wpos,
|
||||||
|
s64 offs, s64 to_write, s64 rounded,
|
||||||
|
const void *b, int compressed_part);
|
||||||
|
|
||||||
|
extern int ntfs_compressed_close(ntfs_attr *na, runlist_element *brl, s64 offs);
|
||||||
|
|
||||||
|
#endif /* defined _NTFS_COMPRESS_H */
|
||||||
|
|
387
source/libntfs/config.h
Normal file
387
source/libntfs/config.h
Normal file
@ -0,0 +1,387 @@
|
|||||||
|
/* config.h. Generated from config.h.in by configure. */
|
||||||
|
/* config.h.in. Generated from configure.ac by autoheader. */
|
||||||
|
|
||||||
|
/* Define if building universal (internal helper macro) */
|
||||||
|
/* #undef AC_APPLE_UNIVERSAL_BUILD */
|
||||||
|
|
||||||
|
/* Define to 1 if debug should be enabled */
|
||||||
|
#ifdef DEBUG
|
||||||
|
# define ENABLE_DEBUG
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Define to 1 if using internal fuse */
|
||||||
|
/* #undef FUSE_INTERNAL */
|
||||||
|
|
||||||
|
/* Define to 1 if you have the `atexit' function. */
|
||||||
|
#define HAVE_ATEXIT 1
|
||||||
|
|
||||||
|
/* Define to 1 if you have the `basename' function. */
|
||||||
|
/* #undef HAVE_BASENAME */
|
||||||
|
|
||||||
|
/* Define to 1 if you have the <byteswap.h> header file. */
|
||||||
|
/* #undef HAVE_BYTESWAP_H */
|
||||||
|
|
||||||
|
/* Define to 1 if you have the <ctype.h> header file. */
|
||||||
|
#define HAVE_CTYPE_H 1
|
||||||
|
|
||||||
|
/* Define to 1 if you have the `daemon' function. */
|
||||||
|
/* #undef HAVE_DAEMON */
|
||||||
|
|
||||||
|
/* Define to 1 if you have the <dlfcn.h> header file. */
|
||||||
|
/* #undef HAVE_DLFCN_H */
|
||||||
|
|
||||||
|
/* Define to 1 if you don't have `vprintf' but do have `_doprnt.' */
|
||||||
|
/* #undef HAVE_DOPRNT */
|
||||||
|
|
||||||
|
/* Define to 1 if you have the `dup2' function. */
|
||||||
|
/* #undef HAVE_DUP2 */
|
||||||
|
|
||||||
|
/* Define to 1 if you have the <endian.h> header file. */
|
||||||
|
/* #undef HAVE_ENDIAN_H */
|
||||||
|
|
||||||
|
/* Define to 1 if you have the <errno.h> header file. */
|
||||||
|
#define HAVE_ERRNO_H 1
|
||||||
|
|
||||||
|
/* Define to 1 if you have the <fcntl.h> header file. */
|
||||||
|
#define HAVE_FCNTL_H 1
|
||||||
|
|
||||||
|
/* Define to 1 if you have the `fdatasync' function. */
|
||||||
|
/* #undef HAVE_FDATASYNC */
|
||||||
|
|
||||||
|
/* Define to 1 if you have the <features.h> header file. */
|
||||||
|
/* #undef HAVE_FEATURES_H */
|
||||||
|
|
||||||
|
/* Define to 1 if you have the `ffs' function. */
|
||||||
|
#define HAVE_FFS 1
|
||||||
|
|
||||||
|
/* Define to 1 if you have the `fork' function. */
|
||||||
|
#define HAVE_FORK 1
|
||||||
|
|
||||||
|
/* Define to 1 if you have the `getmntent' function. */
|
||||||
|
/* #undef HAVE_GETMNTENT */
|
||||||
|
|
||||||
|
/* Define to 1 if you have the <getopt.h> header file. */
|
||||||
|
#define HAVE_GETOPT_H 1
|
||||||
|
|
||||||
|
/* Define to 1 if you have the `getopt_long' function. */
|
||||||
|
#define HAVE_GETOPT_LONG 1
|
||||||
|
|
||||||
|
/* Define to 1 if you have the `hasmntopt' function. */
|
||||||
|
/* #undef HAVE_HASMNTOPT */
|
||||||
|
|
||||||
|
/* Define to 1 if you have the <inttypes.h> header file. */
|
||||||
|
#define HAVE_INTTYPES_H 1
|
||||||
|
|
||||||
|
/* Define to 1 if you have the <libgen.h> header file. */
|
||||||
|
#define HAVE_LIBGEN_H 1
|
||||||
|
|
||||||
|
/* Define to 1 if you have the <libintl.h> header file. */
|
||||||
|
/* #undef HAVE_LIBINTL_H */
|
||||||
|
|
||||||
|
/* Define to 1 if you have the <limits.h> header file. */
|
||||||
|
#define HAVE_LIMITS_H 1
|
||||||
|
|
||||||
|
/* Define to 1 if you have the <linux/fd.h> header file. */
|
||||||
|
/* #undef HAVE_LINUX_FD_H */
|
||||||
|
|
||||||
|
/* Define to 1 if you have the <linux/hdreg.h> header file. */
|
||||||
|
/* #undef HAVE_LINUX_HDREG_H */
|
||||||
|
|
||||||
|
/* Define to 1 if you have the <linux/major.h> header file. */
|
||||||
|
/* #undef HAVE_LINUX_MAJOR_H */
|
||||||
|
|
||||||
|
/* Define to 1 if you have the <locale.h> header file. */
|
||||||
|
#define HAVE_LOCALE_H 1
|
||||||
|
|
||||||
|
/* Define to 1 if you have the <machine/endian.h> header file. */
|
||||||
|
#define HAVE_MACHINE_ENDIAN_H 1
|
||||||
|
|
||||||
|
/* Define to 1 if you have the <math.h> header file. */
|
||||||
|
#define HAVE_MATH_H 1
|
||||||
|
|
||||||
|
/* Define to 1 if mbrtowc and mbstate_t are properly declared. */
|
||||||
|
#define HAVE_MBRTOWC 1
|
||||||
|
|
||||||
|
/* Define to 1 if you have the `mbsinit' function. */
|
||||||
|
#define HAVE_MBSINIT 1
|
||||||
|
|
||||||
|
/* Define to 1 if you have the `memmove' function. */
|
||||||
|
#define HAVE_MEMMOVE 1
|
||||||
|
|
||||||
|
/* Define to 1 if you have the <memory.h> header file. */
|
||||||
|
/* #undef HAVE_MEMORY_H */
|
||||||
|
|
||||||
|
/* Define to 1 if you have the `memset' function. */
|
||||||
|
#define HAVE_MEMSET 1
|
||||||
|
|
||||||
|
/* Define to 1 if you have the <mntent.h> header file. */
|
||||||
|
/* #undef HAVE_MNTENT_H */
|
||||||
|
|
||||||
|
/* Define to 1 if you have the `realpath' function. */
|
||||||
|
/* #undef HAVE_REALPATH */
|
||||||
|
|
||||||
|
/* Define to 1 if you have the `regcomp' function. */
|
||||||
|
/* #undef HAVE_REGCOMP */
|
||||||
|
|
||||||
|
/* Define to 1 if you have the `setlocale' function. */
|
||||||
|
#define HAVE_SETLOCALE 1
|
||||||
|
|
||||||
|
/* Define to 1 if you have the `setxattr' function. */
|
||||||
|
/* #undef HAVE_SETXATTR */
|
||||||
|
|
||||||
|
/* Define to 1 if `stat' has the bug that it succeeds when given the
|
||||||
|
zero-length file name argument. */
|
||||||
|
#define HAVE_STAT_EMPTY_STRING_BUG 1
|
||||||
|
|
||||||
|
/* Define to 1 if you have the <stdarg.h> header file. */
|
||||||
|
#define HAVE_STDARG_H 1
|
||||||
|
|
||||||
|
/* Define to 1 if stdbool.h conforms to C99. */
|
||||||
|
#define HAVE_STDBOOL_H 1
|
||||||
|
|
||||||
|
/* Define to 1 if you have the <stddef.h> header file. */
|
||||||
|
#define HAVE_STDDEF_H 1
|
||||||
|
|
||||||
|
/* Define to 1 if you have the <stdint.h> header file. */
|
||||||
|
#define HAVE_STDINT_H 1
|
||||||
|
|
||||||
|
/* Define to 1 if you have the <stdio.h> header file. */
|
||||||
|
#define HAVE_STDIO_H 1
|
||||||
|
|
||||||
|
/* Define to 1 if you have the <stdlib.h> header file. */
|
||||||
|
#define HAVE_STDLIB_H 1
|
||||||
|
|
||||||
|
/* Define to 1 if you have the `strcasecmp' function. */
|
||||||
|
#define HAVE_STRCASECMP 1
|
||||||
|
|
||||||
|
/* Define to 1 if you have the `strchr' function. */
|
||||||
|
#define HAVE_STRCHR 1
|
||||||
|
|
||||||
|
/* Define to 1 if you have the `strdup' function. */
|
||||||
|
#define HAVE_STRDUP 1
|
||||||
|
|
||||||
|
/* Define to 1 if you have the `strerror' function. */
|
||||||
|
#define HAVE_STRERROR 1
|
||||||
|
|
||||||
|
/* Define to 1 if you have the `strftime' function. */
|
||||||
|
#define HAVE_STRFTIME 1
|
||||||
|
|
||||||
|
/* Define to 1 if you have the <strings.h> header file. */
|
||||||
|
/* #undef HAVE_STRINGS_H */
|
||||||
|
|
||||||
|
/* Define to 1 if you have the <string.h> header file. */
|
||||||
|
#define HAVE_STRING_H 1
|
||||||
|
|
||||||
|
/* Define to 1 if you have the `strnlen' function. */
|
||||||
|
#define HAVE_STRNLEN 1
|
||||||
|
|
||||||
|
/* Define to 1 if you have the `strsep' function. */
|
||||||
|
#define HAVE_STRSEP 1
|
||||||
|
|
||||||
|
/* Define to 1 if you have the `strtol' function. */
|
||||||
|
#define HAVE_STRTOL 1
|
||||||
|
|
||||||
|
/* Define to 1 if you have the `strtoul' function. */
|
||||||
|
#define HAVE_STRTOUL 1
|
||||||
|
|
||||||
|
/* Define to 1 if `st_atim' is member of `struct stat'. */
|
||||||
|
/* #undef HAVE_STRUCT_STAT_ST_ATIM */
|
||||||
|
|
||||||
|
/* Define to 1 if `st_atimespec' is member of `struct stat'. */
|
||||||
|
/* #undef HAVE_STRUCT_STAT_ST_ATIMESPEC */
|
||||||
|
|
||||||
|
/* Define to 1 if `st_blocks' is member of `struct stat'. */
|
||||||
|
#define HAVE_STRUCT_STAT_ST_BLOCKS 1
|
||||||
|
|
||||||
|
/* Define to 1 if `st_rdev' is member of `struct stat'. */
|
||||||
|
#define HAVE_STRUCT_STAT_ST_RDEV 1
|
||||||
|
|
||||||
|
/* Define to 1 if your `struct stat' has `st_blocks'. Deprecated, use
|
||||||
|
`HAVE_STRUCT_STAT_ST_BLOCKS' instead. */
|
||||||
|
#define HAVE_ST_BLOCKS 1
|
||||||
|
#define HAVE_STRUCT_STAT_ST_BLOCKS 1
|
||||||
|
|
||||||
|
/* Define to 1 if you have the `sysconf' function. */
|
||||||
|
/* #undef HAVE_SYSCONF */
|
||||||
|
|
||||||
|
/* Define to 1 if you have the <syslog.h> header file. */
|
||||||
|
/* #undef HAVE_SYSLOG_H */
|
||||||
|
|
||||||
|
/* Define to 1 if you have the <sys/byteorder.h> header file. */
|
||||||
|
/* #undef HAVE_SYS_BYTEORDER_H */
|
||||||
|
|
||||||
|
/* Define to 1 if you have the <sys/endian.h> header file. */
|
||||||
|
/* #undef HAVE_SYS_ENDIAN_H */
|
||||||
|
|
||||||
|
/* Define to 1 if you have the <sys/ioctl.h> header file. */
|
||||||
|
/* #undef HAVE_SYS_IOCTL_H */
|
||||||
|
|
||||||
|
/* Define to 1 if you have the <sys/mkdev.h> header file. */
|
||||||
|
/* #undef HAVE_SYS_MKDEV_H */
|
||||||
|
|
||||||
|
/* Define to 1 if you have the <sys/mount.h> header file. */
|
||||||
|
/* #undef HAVE_SYS_MOUNT_H */
|
||||||
|
|
||||||
|
/* Define to 1 if you have the <sys/param.h> header file. */
|
||||||
|
#define HAVE_SYS_PARAM_H 1
|
||||||
|
|
||||||
|
/* Define to 1 if you have the <sys/statvfs.h> header file. */
|
||||||
|
#define HAVE_SYS_STATVFS_H 1
|
||||||
|
|
||||||
|
/* Define to 1 if you have the <sys/stat.h> header file. */
|
||||||
|
#define HAVE_SYS_STAT_H 1
|
||||||
|
|
||||||
|
/* Define to 1 if you have the <sys/sysmacros.h> header file. */
|
||||||
|
/* #undef HAVE_SYS_SYSMACROS_H */
|
||||||
|
|
||||||
|
/* Define to 1 if you have the <sys/types.h> header file. */
|
||||||
|
#define HAVE_SYS_TYPES_H 1
|
||||||
|
|
||||||
|
/* Define to 1 if you have the <sys/vfs.h> header file. */
|
||||||
|
/* #undef HAVE_SYS_VFS_H */
|
||||||
|
|
||||||
|
/* Define to 1 if you have the <time.h> header file. */
|
||||||
|
#define HAVE_TIME_H 1
|
||||||
|
|
||||||
|
/* Define to 1 if you have the <unistd.h> header file. */
|
||||||
|
#define HAVE_UNISTD_H 1
|
||||||
|
|
||||||
|
/* Define to 1 if you have the `utime' function. */
|
||||||
|
/* #undef HAVE_UTIME */
|
||||||
|
|
||||||
|
/* Define to 1 if you have the <utime.h> header file. */
|
||||||
|
#define HAVE_UTIME_H 1
|
||||||
|
|
||||||
|
/* Define to 1 if `utime(file, NULL)' sets file's timestamp to the present. */
|
||||||
|
/* #undef HAVE_UTIME_NULL */
|
||||||
|
|
||||||
|
/* Define to 1 if you have the `vprintf' function. */
|
||||||
|
#define HAVE_VPRINTF 1
|
||||||
|
|
||||||
|
/* Define to 1 if you have the <wchar.h> header file. */
|
||||||
|
#define HAVE_WCHAR_H 1
|
||||||
|
|
||||||
|
/* Define to 1 if you have the <windows.h> header file. */
|
||||||
|
/* #undef HAVE_WINDOWS_H */
|
||||||
|
|
||||||
|
/* Define to 1 if the system has the type `_Bool'. */
|
||||||
|
#define HAVE__BOOL 1
|
||||||
|
|
||||||
|
/* Don't update /etc/mtab */
|
||||||
|
/* #undef IGNORE_MTAB */
|
||||||
|
|
||||||
|
/* Define to 1 if `lstat' dereferences a symlink specified with a trailing
|
||||||
|
slash. */
|
||||||
|
/* #undef LSTAT_FOLLOWS_SLASHED_SYMLINK */
|
||||||
|
|
||||||
|
/* Define to the sub-directory in which libtool stores uninstalled libraries.
|
||||||
|
*/
|
||||||
|
#define LT_OBJDIR ".libs/"
|
||||||
|
|
||||||
|
/* Define to 1 if your C compiler doesn't accept -c and -o together. */
|
||||||
|
/* #undef NO_MINUS_C_MINUS_O */
|
||||||
|
|
||||||
|
/* Don't use default IO ops */
|
||||||
|
/* #undef NO_NTFS_DEVICE_DEFAULT_IO_OPS */
|
||||||
|
|
||||||
|
/* Name of package */
|
||||||
|
#define PACKAGE "ntfs-3g"
|
||||||
|
|
||||||
|
/* Define to the address where bug reports for this package should be sent. */
|
||||||
|
#define PACKAGE_BUGREPORT "ntfs-3g-devel@lists.sf.net"
|
||||||
|
|
||||||
|
/* Define to the full name of this package. */
|
||||||
|
#define PACKAGE_NAME "ntfs-3g"
|
||||||
|
|
||||||
|
/* Define to the full name and version of this package. */
|
||||||
|
#define PACKAGE_STRING "ntfs-3g 2009.4.4AR.16"
|
||||||
|
|
||||||
|
/* Define to the one symbol short name of this package. */
|
||||||
|
#define PACKAGE_TARNAME "ntfs-3g"
|
||||||
|
|
||||||
|
/* Define to the version of this package. */
|
||||||
|
#define PACKAGE_VERSION "2009.4.4AR.16"
|
||||||
|
|
||||||
|
/* POSIX ACL support */
|
||||||
|
/* #undef POSIXACLS */
|
||||||
|
|
||||||
|
/* Define to 1 if you have the ANSI C header files. */
|
||||||
|
#define STDC_HEADERS 1
|
||||||
|
|
||||||
|
/* Enable extensions on AIX 3, Interix. */
|
||||||
|
#ifndef _ALL_SOURCE
|
||||||
|
# define _ALL_SOURCE 1
|
||||||
|
#endif
|
||||||
|
/* Enable GNU extensions on systems that have them. */
|
||||||
|
#ifndef _GNU_SOURCE
|
||||||
|
# define _GNU_SOURCE 1
|
||||||
|
#endif
|
||||||
|
/* Enable threading extensions on Solaris. */
|
||||||
|
#ifndef _POSIX_PTHREAD_SEMANTICS
|
||||||
|
# define _POSIX_PTHREAD_SEMANTICS 1
|
||||||
|
#endif
|
||||||
|
/* Enable extensions on HP NonStop. */
|
||||||
|
#ifndef _TANDEM_SOURCE
|
||||||
|
# define _TANDEM_SOURCE 1
|
||||||
|
#endif
|
||||||
|
/* Enable general extensions on Solaris. */
|
||||||
|
#ifndef __EXTENSIONS__
|
||||||
|
# define __EXTENSIONS__ 1
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
/* Version number of package */
|
||||||
|
#define VERSION "2009.4.4AR.16"
|
||||||
|
|
||||||
|
/* Define to 1 if this is a Windows OS */
|
||||||
|
/* #undef WINDOWS */
|
||||||
|
|
||||||
|
/* Define WORDS_BIGENDIAN to 1 if your processor stores words with the most
|
||||||
|
significant byte first (like Motorola and SPARC, unlike Intel). */
|
||||||
|
#if defined AC_APPLE_UNIVERSAL_BUILD
|
||||||
|
# if defined __BIG_ENDIAN__
|
||||||
|
# define WORDS_BIGENDIAN 1
|
||||||
|
# endif
|
||||||
|
#else
|
||||||
|
# ifndef WORDS_BIGENDIAN
|
||||||
|
# define WORDS_BIGENDIAN 1
|
||||||
|
# endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Define to 1 if your processor stores words with the least significant byte
|
||||||
|
first (like Intel and VAX, unlike Motorola and SPARC). */
|
||||||
|
/* #undef WORDS_LITTLEENDIAN */
|
||||||
|
|
||||||
|
/* Number of bits in a file offset, on hosts where this is settable. */
|
||||||
|
/* #undef _FILE_OFFSET_BITS */
|
||||||
|
|
||||||
|
/* Define for large files, on AIX-style hosts. */
|
||||||
|
/* #undef _LARGE_FILES */
|
||||||
|
|
||||||
|
/* Define to 1 if on MINIX. */
|
||||||
|
/* #undef _MINIX */
|
||||||
|
|
||||||
|
/* Define to 2 if the system does not provide POSIX.1 features except with
|
||||||
|
this defined. */
|
||||||
|
/* #undef _POSIX_1_SOURCE */
|
||||||
|
|
||||||
|
/* Define to 1 if you need to in order for `stat' and other things to work. */
|
||||||
|
/* #undef _POSIX_SOURCE */
|
||||||
|
|
||||||
|
/* Required define if using POSIX threads */
|
||||||
|
/* #undef _REENTRANT */
|
||||||
|
|
||||||
|
/* Define to empty if `const' does not conform to ANSI C. */
|
||||||
|
/* #undef const */
|
||||||
|
|
||||||
|
/* Define to `__inline__' or `__inline' if that's what the C compiler
|
||||||
|
calls it, or to nothing if 'inline' is not supported under any name. */
|
||||||
|
#ifndef __cplusplus
|
||||||
|
/* #undef inline */
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Define to `long int' if <sys/types.h> does not define. */
|
||||||
|
/* #undef off_t */
|
||||||
|
|
||||||
|
/* Define to `unsigned int' if <sys/types.h> does not define. */
|
||||||
|
/* #undef size_t */
|
79
source/libntfs/debug.c
Normal file
79
source/libntfs/debug.c
Normal file
@ -0,0 +1,79 @@
|
|||||||
|
/**
|
||||||
|
* debug.c - Debugging output functions. Originated from the Linux-NTFS project.
|
||||||
|
*
|
||||||
|
* Copyright (c) 2002-2004 Anton Altaparmakov
|
||||||
|
* Copyright (c) 2004-2006 Szabolcs Szakacsits
|
||||||
|
*
|
||||||
|
* This program/include file 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/include file 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
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifdef HAVE_CONFIG_H
|
||||||
|
#include "config.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef HAVE_ERRNO_H
|
||||||
|
#include <errno.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "types.h"
|
||||||
|
#include "runlist.h"
|
||||||
|
#include "debug.h"
|
||||||
|
#include "logging.h"
|
||||||
|
|
||||||
|
#ifdef DEBUG
|
||||||
|
/**
|
||||||
|
* 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("%-16lld %s %-16lld%s\n",
|
||||||
|
(long long)rl[i].vcn, lcn_str[idx],
|
||||||
|
(long long)rl[i].length,
|
||||||
|
rl[i].length ? "" : " (runlist end)");
|
||||||
|
} else
|
||||||
|
ntfs_log_debug("%-16lld %-16lld %-16lld%s\n",
|
||||||
|
(long long)rl[i].vcn, (long long)rl[i].lcn,
|
||||||
|
(long long)rl[i].length,
|
||||||
|
rl[i].length ? "" : " (runlist end)");
|
||||||
|
} while (rl[i++].length);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
47
source/libntfs/debug.h
Normal file
47
source/libntfs/debug.h
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
/*
|
||||||
|
* debug.h - Debugging output functions. Originated from the Linux-NTFS project.
|
||||||
|
*
|
||||||
|
* Copyright (c) 2002-2004 Anton Altaparmakov
|
||||||
|
*
|
||||||
|
* This program/include file 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/include file 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
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _NTFS_DEBUG_H
|
||||||
|
#define _NTFS_DEBUG_H
|
||||||
|
|
||||||
|
#ifdef HAVE_CONFIG_H
|
||||||
|
#include "config.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "logging.h"
|
||||||
|
|
||||||
|
struct _runlist_element;
|
||||||
|
|
||||||
|
#ifdef DEBUG
|
||||||
|
extern void ntfs_debug_runlist_dump(const struct _runlist_element *rl);
|
||||||
|
#else
|
||||||
|
static __inline__ void ntfs_debug_runlist_dump(const struct _runlist_element *rl __attribute__((unused))) {}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define NTFS_BUG(msg) \
|
||||||
|
{ \
|
||||||
|
int ___i; \
|
||||||
|
ntfs_log_critical("Bug in %s(): %s\n", __FUNCTION__, msg); \
|
||||||
|
ntfs_log_debug("Forcing segmentation fault!"); \
|
||||||
|
___i = ((int*)NULL)[1]; \
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif /* defined _NTFS_DEBUG_H */
|
730
source/libntfs/device.c
Normal file
730
source/libntfs/device.c
Normal file
@ -0,0 +1,730 @@
|
|||||||
|
/**
|
||||||
|
* device.c - Low level device io functions. Originated from the Linux-NTFS project.
|
||||||
|
*
|
||||||
|
* Copyright (c) 2004-2006 Anton Altaparmakov
|
||||||
|
* Copyright (c) 2004-2006 Szabolcs Szakacsits
|
||||||
|
*
|
||||||
|
* This program/include file 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/include file 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
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifdef HAVE_CONFIG_H
|
||||||
|
#include "config.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef HAVE_UNISTD_H
|
||||||
|
#include <unistd.h>
|
||||||
|
#endif
|
||||||
|
#ifdef HAVE_STDLIB_H
|
||||||
|
#include <stdlib.h>
|
||||||
|
#endif
|
||||||
|
#ifdef HAVE_STRING_H
|
||||||
|
#include <string.h>
|
||||||
|
#endif
|
||||||
|
#ifdef HAVE_ERRNO_H
|
||||||
|
#include <errno.h>
|
||||||
|
#endif
|
||||||
|
#ifdef HAVE_STDIO_H
|
||||||
|
#include <stdio.h>
|
||||||
|
#endif
|
||||||
|
#ifdef HAVE_SYS_TYPES_H
|
||||||
|
#include <sys/types.h>
|
||||||
|
#endif
|
||||||
|
#ifdef HAVE_SYS_STAT_H
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#endif
|
||||||
|
#ifdef HAVE_FCNTL_H
|
||||||
|
#include <fcntl.h>
|
||||||
|
#endif
|
||||||
|
#ifdef HAVE_SYS_IOCTL_H
|
||||||
|
#include <sys/ioctl.h>
|
||||||
|
#endif
|
||||||
|
#ifdef HAVE_SYS_PARAM_H
|
||||||
|
#include <sys/param.h>
|
||||||
|
#endif
|
||||||
|
#ifdef HAVE_SYS_MOUNT_H
|
||||||
|
#include <sys/mount.h>
|
||||||
|
#endif
|
||||||
|
#ifdef HAVE_LINUX_FD_H
|
||||||
|
#include <linux/fd.h>
|
||||||
|
#endif
|
||||||
|
#ifdef HAVE_LINUX_HDREG_H
|
||||||
|
#include <linux/hdreg.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "types.h"
|
||||||
|
#include "mst.h"
|
||||||
|
#include "debug.h"
|
||||||
|
#include "device.h"
|
||||||
|
#include "logging.h"
|
||||||
|
#include "misc.h"
|
||||||
|
|
||||||
|
#if defined(linux) && defined(_IO) && !defined(BLKGETSIZE)
|
||||||
|
#define BLKGETSIZE _IO(0x12,96) /* Get device size in 512-byte blocks. */
|
||||||
|
#endif
|
||||||
|
#if defined(linux) && defined(_IOR) && !defined(BLKGETSIZE64)
|
||||||
|
#define BLKGETSIZE64 _IOR(0x12,114,size_t) /* Get device size in bytes. */
|
||||||
|
#endif
|
||||||
|
#if defined(linux) && !defined(HDIO_GETGEO)
|
||||||
|
#define HDIO_GETGEO 0x0301 /* Get device geometry. */
|
||||||
|
#endif
|
||||||
|
#if defined(linux) && defined(_IO) && !defined(BLKSSZGET)
|
||||||
|
# define BLKSSZGET _IO(0x12,104) /* Get device sector size in bytes. */
|
||||||
|
#endif
|
||||||
|
#if defined(linux) && defined(_IO) && !defined(BLKBSZSET)
|
||||||
|
# define BLKBSZSET _IOW(0x12,113,size_t) /* Set device block size in bytes. */
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ntfs_device_alloc - allocate an ntfs device structure and pre-initialize it
|
||||||
|
* @name: name of the device (must be present)
|
||||||
|
* @state: initial device state (usually zero)
|
||||||
|
* @dops: ntfs device operations to use with the device (must be present)
|
||||||
|
* @priv_data: pointer to private data (optional)
|
||||||
|
*
|
||||||
|
* Allocate an ntfs device structure and pre-initialize it with the user-
|
||||||
|
* specified device operations @dops, device state @state, device name @name,
|
||||||
|
* and optional private data @priv_data.
|
||||||
|
*
|
||||||
|
* Note, @name is copied and can hence be freed after this functions returns.
|
||||||
|
*
|
||||||
|
* On success return a pointer to the allocated ntfs device structure and on
|
||||||
|
* error return NULL with errno set to the error code returned by ntfs_malloc().
|
||||||
|
*/
|
||||||
|
struct ntfs_device *ntfs_device_alloc(const char *name, const long state,
|
||||||
|
struct ntfs_device_operations *dops, void *priv_data)
|
||||||
|
{
|
||||||
|
struct ntfs_device *dev;
|
||||||
|
|
||||||
|
if (!name) {
|
||||||
|
errno = EINVAL;
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
dev = ntfs_malloc(sizeof(struct ntfs_device));
|
||||||
|
if (dev) {
|
||||||
|
if (!(dev->d_name = strdup(name))) {
|
||||||
|
int eo = errno;
|
||||||
|
free(dev);
|
||||||
|
errno = eo;
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
dev->d_ops = dops;
|
||||||
|
dev->d_state = state;
|
||||||
|
dev->d_private = priv_data;
|
||||||
|
}
|
||||||
|
return dev;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ntfs_device_free - free an ntfs device structure
|
||||||
|
* @dev: ntfs device structure to free
|
||||||
|
*
|
||||||
|
* Free the ntfs device structure @dev.
|
||||||
|
*
|
||||||
|
* Return 0 on success or -1 on error with errno set to the error code. The
|
||||||
|
* following error codes are defined:
|
||||||
|
* EINVAL Invalid pointer @dev.
|
||||||
|
* EBUSY Device is still open. Close it before freeing it!
|
||||||
|
*/
|
||||||
|
int ntfs_device_free(struct ntfs_device *dev)
|
||||||
|
{
|
||||||
|
if (!dev) {
|
||||||
|
errno = EINVAL;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (NDevOpen(dev)) {
|
||||||
|
errno = EBUSY;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
free(dev->d_name);
|
||||||
|
free(dev);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ntfs_pread - positioned read from disk
|
||||||
|
* @dev: device to read from
|
||||||
|
* @pos: position in device to read from
|
||||||
|
* @count: number of bytes to read
|
||||||
|
* @b: output data buffer
|
||||||
|
*
|
||||||
|
* This function will read @count bytes from device @dev at position @pos into
|
||||||
|
* the data buffer @b.
|
||||||
|
*
|
||||||
|
* On success, return the number of successfully read bytes. If this number is
|
||||||
|
* lower than @count this means that we have either reached end of file or
|
||||||
|
* encountered an error during the read so that the read is partial. 0 means
|
||||||
|
* end of file or nothing to read (@count is 0).
|
||||||
|
*
|
||||||
|
* On error and nothing has been read, return -1 with errno set appropriately
|
||||||
|
* to the return code of either seek, read, or set to EINVAL in case of
|
||||||
|
* invalid arguments.
|
||||||
|
*/
|
||||||
|
s64 ntfs_pread(struct ntfs_device *dev, const s64 pos, s64 count, void *b)
|
||||||
|
{
|
||||||
|
s64 br, total;
|
||||||
|
struct ntfs_device_operations *dops;
|
||||||
|
|
||||||
|
ntfs_log_trace("pos %lld, count %lld\n",(long long)pos,(long long)count);
|
||||||
|
|
||||||
|
if (!b || count < 0 || pos < 0) {
|
||||||
|
errno = EINVAL;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (!count)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
dops = dev->d_ops;
|
||||||
|
|
||||||
|
for (total = 0; count; count -= br, total += br) {
|
||||||
|
br = dops->pread(dev, (char*)b + total, count, pos + total);
|
||||||
|
/* If everything ok, continue. */
|
||||||
|
if (br > 0)
|
||||||
|
continue;
|
||||||
|
/* If EOF or error return number of bytes read. */
|
||||||
|
if (!br || total)
|
||||||
|
return total;
|
||||||
|
/* Nothing read and error, return error status. */
|
||||||
|
return br;
|
||||||
|
}
|
||||||
|
/* Finally, return the number of bytes read. */
|
||||||
|
return total;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ntfs_pwrite - positioned write to disk
|
||||||
|
* @dev: device to write to
|
||||||
|
* @pos: position in file descriptor to write to
|
||||||
|
* @count: number of bytes to write
|
||||||
|
* @b: data buffer to write to disk
|
||||||
|
*
|
||||||
|
* This function will write @count bytes from data buffer @b to the device @dev
|
||||||
|
* at position @pos.
|
||||||
|
*
|
||||||
|
* On success, return the number of successfully written bytes. If this number
|
||||||
|
* is lower than @count this means that the write has been interrupted in
|
||||||
|
* flight or that an error was encountered during the write so that the write
|
||||||
|
* is partial. 0 means nothing was written (also return 0 when @count is 0).
|
||||||
|
*
|
||||||
|
* On error and nothing has been written, return -1 with errno set
|
||||||
|
* appropriately to the return code of either seek, write, or set
|
||||||
|
* to EINVAL in case of invalid arguments.
|
||||||
|
*/
|
||||||
|
s64 ntfs_pwrite(struct ntfs_device *dev, const s64 pos, s64 count,
|
||||||
|
const void *b)
|
||||||
|
{
|
||||||
|
s64 written, total, ret = -1;
|
||||||
|
struct ntfs_device_operations *dops;
|
||||||
|
|
||||||
|
ntfs_log_trace("pos %lld, count %lld\n",(long long)pos,(long long)count);
|
||||||
|
|
||||||
|
if (!b || count < 0 || pos < 0) {
|
||||||
|
errno = EINVAL;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
if (!count)
|
||||||
|
return 0;
|
||||||
|
if (NDevReadOnly(dev)) {
|
||||||
|
errno = EROFS;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
dops = dev->d_ops;
|
||||||
|
|
||||||
|
NDevSetDirty(dev);
|
||||||
|
for (total = 0; count; count -= written, total += written) {
|
||||||
|
written = dops->pwrite(dev, (const char*)b + total, count,
|
||||||
|
pos + total);
|
||||||
|
/* If everything ok, continue. */
|
||||||
|
if (written > 0)
|
||||||
|
continue;
|
||||||
|
/*
|
||||||
|
* If nothing written or error return number of bytes written.
|
||||||
|
*/
|
||||||
|
if (!written || total)
|
||||||
|
break;
|
||||||
|
/* Nothing written and error, return error status. */
|
||||||
|
total = written;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
ret = total;
|
||||||
|
out:
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ntfs_mst_pread - multi sector transfer (mst) positioned read
|
||||||
|
* @dev: device to read from
|
||||||
|
* @pos: position in file descriptor to read from
|
||||||
|
* @count: number of blocks to read
|
||||||
|
* @bksize: size of each block that needs mst deprotecting
|
||||||
|
* @b: output data buffer
|
||||||
|
*
|
||||||
|
* Multi sector transfer (mst) positioned read. This function will read @count
|
||||||
|
* blocks of size @bksize bytes each from device @dev at position @pos into the
|
||||||
|
* the data buffer @b.
|
||||||
|
*
|
||||||
|
* On success, return the number of successfully read blocks. If this number is
|
||||||
|
* lower than @count this means that we have reached end of file, that the read
|
||||||
|
* was interrupted, or that an error was encountered during the read so that
|
||||||
|
* the read is partial. 0 means end of file or nothing was read (also return 0
|
||||||
|
* when @count or @bksize are 0).
|
||||||
|
*
|
||||||
|
* On error and nothing was read, return -1 with errno set appropriately to the
|
||||||
|
* return code of either seek, read, or set to EINVAL in case of invalid
|
||||||
|
* arguments.
|
||||||
|
*
|
||||||
|
* NOTE: If an incomplete multi sector transfer has been detected the magic
|
||||||
|
* will have been changed to magic_BAAD but no error will be returned. Thus it
|
||||||
|
* is possible that we return count blocks as being read but that any number
|
||||||
|
* (between zero and count!) of these blocks is actually subject to a multi
|
||||||
|
* sector transfer error. This should be detected by the caller by checking for
|
||||||
|
* the magic being "BAAD".
|
||||||
|
*/
|
||||||
|
s64 ntfs_mst_pread(struct ntfs_device *dev, const s64 pos, s64 count,
|
||||||
|
const u32 bksize, void *b)
|
||||||
|
{
|
||||||
|
s64 br, i;
|
||||||
|
|
||||||
|
if (bksize & (bksize - 1) || bksize % NTFS_BLOCK_SIZE) {
|
||||||
|
errno = EINVAL;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
/* Do the read. */
|
||||||
|
br = ntfs_pread(dev, pos, count * bksize, b);
|
||||||
|
if (br < 0)
|
||||||
|
return br;
|
||||||
|
/*
|
||||||
|
* Apply fixups to successfully read data, disregarding any errors
|
||||||
|
* returned from the MST fixup function. This is because we want to
|
||||||
|
* fixup everything possible and we rely on the fact that the "BAAD"
|
||||||
|
* magic will be detected later on.
|
||||||
|
*/
|
||||||
|
count = br / bksize;
|
||||||
|
for (i = 0; i < count; ++i)
|
||||||
|
ntfs_mst_post_read_fixup((NTFS_RECORD*)
|
||||||
|
((u8*)b + i * bksize), bksize);
|
||||||
|
/* Finally, return the number of complete blocks read. */
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ntfs_mst_pwrite - multi sector transfer (mst) positioned write
|
||||||
|
* @dev: device to write to
|
||||||
|
* @pos: position in file descriptor to write to
|
||||||
|
* @count: number of blocks to write
|
||||||
|
* @bksize: size of each block that needs mst protecting
|
||||||
|
* @b: data buffer to write to disk
|
||||||
|
*
|
||||||
|
* Multi sector transfer (mst) positioned write. This function will write
|
||||||
|
* @count blocks of size @bksize bytes each from data buffer @b to the device
|
||||||
|
* @dev at position @pos.
|
||||||
|
*
|
||||||
|
* On success, return the number of successfully written blocks. If this number
|
||||||
|
* is lower than @count this means that the write has been interrupted or that
|
||||||
|
* an error was encountered during the write so that the write is partial. 0
|
||||||
|
* means nothing was written (also return 0 when @count or @bksize are 0).
|
||||||
|
*
|
||||||
|
* On error and nothing has been written, return -1 with errno set
|
||||||
|
* appropriately to the return code of either seek, write, or set
|
||||||
|
* to EINVAL in case of invalid arguments.
|
||||||
|
*
|
||||||
|
* NOTE: We mst protect the data, write it, then mst deprotect it using a quick
|
||||||
|
* deprotect algorithm (no checking). This saves us from making a copy before
|
||||||
|
* the write and at the same time causes the usn to be incremented in the
|
||||||
|
* buffer. This conceptually fits in better with the idea that cached data is
|
||||||
|
* always deprotected and protection is performed when the data is actually
|
||||||
|
* going to hit the disk and the cache is immediately deprotected again
|
||||||
|
* simulating an mst read on the written data. This way cache coherency is
|
||||||
|
* achieved.
|
||||||
|
*/
|
||||||
|
s64 ntfs_mst_pwrite(struct ntfs_device *dev, const s64 pos, s64 count,
|
||||||
|
const u32 bksize, void *b)
|
||||||
|
{
|
||||||
|
s64 written, i;
|
||||||
|
|
||||||
|
if (count < 0 || bksize % NTFS_BLOCK_SIZE) {
|
||||||
|
errno = EINVAL;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (!count)
|
||||||
|
return 0;
|
||||||
|
/* Prepare data for writing. */
|
||||||
|
for (i = 0; i < count; ++i) {
|
||||||
|
int err;
|
||||||
|
|
||||||
|
err = ntfs_mst_pre_write_fixup((NTFS_RECORD*)
|
||||||
|
((u8*)b + i * bksize), bksize);
|
||||||
|
if (err < 0) {
|
||||||
|
/* Abort write at this position. */
|
||||||
|
if (!i)
|
||||||
|
return err;
|
||||||
|
count = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/* Write the prepared data. */
|
||||||
|
written = ntfs_pwrite(dev, pos, count * bksize, b);
|
||||||
|
/* Quickly deprotect the data again. */
|
||||||
|
for (i = 0; i < count; ++i)
|
||||||
|
ntfs_mst_post_write_fixup((NTFS_RECORD*)((u8*)b + i * bksize));
|
||||||
|
if (written <= 0)
|
||||||
|
return written;
|
||||||
|
/* Finally, return the number of complete blocks written. */
|
||||||
|
return written / bksize;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ntfs_cluster_read - read ntfs clusters
|
||||||
|
* @vol: volume to read from
|
||||||
|
* @lcn: starting logical cluster number
|
||||||
|
* @count: number of clusters to read
|
||||||
|
* @b: output data buffer
|
||||||
|
*
|
||||||
|
* Read @count ntfs clusters starting at logical cluster number @lcn from
|
||||||
|
* volume @vol into buffer @b. Return number of clusters read or -1 on error,
|
||||||
|
* with errno set to the error code.
|
||||||
|
*/
|
||||||
|
s64 ntfs_cluster_read(const ntfs_volume *vol, const s64 lcn, const s64 count,
|
||||||
|
void *b)
|
||||||
|
{
|
||||||
|
s64 br;
|
||||||
|
|
||||||
|
if (!vol || lcn < 0 || count < 0) {
|
||||||
|
errno = EINVAL;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (vol->nr_clusters < lcn + count) {
|
||||||
|
errno = ESPIPE;
|
||||||
|
ntfs_log_perror("Trying to read outside of volume "
|
||||||
|
"(%lld < %lld)", (long long)vol->nr_clusters,
|
||||||
|
(long long)lcn + count);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
br = ntfs_pread(vol->dev, lcn << vol->cluster_size_bits,
|
||||||
|
count << vol->cluster_size_bits, b);
|
||||||
|
if (br < 0) {
|
||||||
|
ntfs_log_perror("Error reading cluster(s)");
|
||||||
|
return br;
|
||||||
|
}
|
||||||
|
return br >> vol->cluster_size_bits;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ntfs_cluster_write - write ntfs clusters
|
||||||
|
* @vol: volume to write to
|
||||||
|
* @lcn: starting logical cluster number
|
||||||
|
* @count: number of clusters to write
|
||||||
|
* @b: data buffer to write to disk
|
||||||
|
*
|
||||||
|
* Write @count ntfs clusters starting at logical cluster number @lcn from
|
||||||
|
* buffer @b to volume @vol. Return the number of clusters written or -1 on
|
||||||
|
* error, with errno set to the error code.
|
||||||
|
*/
|
||||||
|
s64 ntfs_cluster_write(const ntfs_volume *vol, const s64 lcn,
|
||||||
|
const s64 count, const void *b)
|
||||||
|
{
|
||||||
|
s64 bw;
|
||||||
|
|
||||||
|
if (!vol || lcn < 0 || count < 0) {
|
||||||
|
errno = EINVAL;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (vol->nr_clusters < lcn + count) {
|
||||||
|
errno = ESPIPE;
|
||||||
|
ntfs_log_perror("Trying to write outside of volume "
|
||||||
|
"(%lld < %lld)", (long long)vol->nr_clusters,
|
||||||
|
(long long)lcn + count);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (!NVolReadOnly(vol))
|
||||||
|
bw = ntfs_pwrite(vol->dev, lcn << vol->cluster_size_bits,
|
||||||
|
count << vol->cluster_size_bits, b);
|
||||||
|
else
|
||||||
|
bw = count << vol->cluster_size_bits;
|
||||||
|
if (bw < 0) {
|
||||||
|
ntfs_log_perror("Error writing cluster(s)");
|
||||||
|
return bw;
|
||||||
|
}
|
||||||
|
return bw >> vol->cluster_size_bits;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ntfs_device_offset_valid - test if a device offset is valid
|
||||||
|
* @dev: open device
|
||||||
|
* @ofs: offset to test for validity
|
||||||
|
*
|
||||||
|
* Test if the offset @ofs is an existing location on the device described
|
||||||
|
* by the open device structure @dev.
|
||||||
|
*
|
||||||
|
* Return 0 if it is valid and -1 if it is not valid.
|
||||||
|
*/
|
||||||
|
static int ntfs_device_offset_valid(struct ntfs_device *dev, s64 ofs)
|
||||||
|
{
|
||||||
|
char ch;
|
||||||
|
|
||||||
|
if (dev->d_ops->seek(dev, ofs, SEEK_SET) >= 0 &&
|
||||||
|
dev->d_ops->read(dev, &ch, 1) == 1)
|
||||||
|
return 0;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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_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_heads_get - get number of heads of device
|
||||||
|
* @dev: open device
|
||||||
|
*
|
||||||
|
* On success, return the number of heads 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_heads_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 heads = %u (0x%x)\n",
|
||||||
|
(unsigned)geo.heads,
|
||||||
|
(unsigned)geo.heads);
|
||||||
|
return geo.heads;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#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_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_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;
|
||||||
|
}
|
128
source/libntfs/device.h
Normal file
128
source/libntfs/device.h
Normal file
@ -0,0 +1,128 @@
|
|||||||
|
/*
|
||||||
|
* device.h - Exports for low level device io. Originated from the Linux-NTFS project.
|
||||||
|
*
|
||||||
|
* Copyright (c) 2000-2006 Anton Altaparmakov
|
||||||
|
*
|
||||||
|
* This program/include file 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/include file 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
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _NTFS_DEVICE_H
|
||||||
|
#define _NTFS_DEVICE_H
|
||||||
|
|
||||||
|
#ifdef HAVE_CONFIG_H
|
||||||
|
#include "config.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "device_io.h"
|
||||||
|
#include "types.h"
|
||||||
|
#include "support.h"
|
||||||
|
#include "volume.h"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* enum ntfs_device_state_bits -
|
||||||
|
*
|
||||||
|
* Defined bits for the state field in the ntfs_device structure.
|
||||||
|
*/
|
||||||
|
typedef enum {
|
||||||
|
ND_Open, /* 1: Device is open. */
|
||||||
|
ND_ReadOnly, /* 1: Device is read-only. */
|
||||||
|
ND_Dirty, /* 1: Device is dirty, needs sync. */
|
||||||
|
ND_Block, /* 1: Device is a block device. */
|
||||||
|
} ntfs_device_state_bits;
|
||||||
|
|
||||||
|
#define test_ndev_flag(nd, flag) test_bit(ND_##flag, (nd)->d_state)
|
||||||
|
#define set_ndev_flag(nd, flag) set_bit(ND_##flag, (nd)->d_state)
|
||||||
|
#define clear_ndev_flag(nd, flag) clear_bit(ND_##flag, (nd)->d_state)
|
||||||
|
|
||||||
|
#define NDevOpen(nd) test_ndev_flag(nd, Open)
|
||||||
|
#define NDevSetOpen(nd) set_ndev_flag(nd, Open)
|
||||||
|
#define NDevClearOpen(nd) clear_ndev_flag(nd, Open)
|
||||||
|
|
||||||
|
#define NDevReadOnly(nd) test_ndev_flag(nd, ReadOnly)
|
||||||
|
#define NDevSetReadOnly(nd) set_ndev_flag(nd, ReadOnly)
|
||||||
|
#define NDevClearReadOnly(nd) clear_ndev_flag(nd, ReadOnly)
|
||||||
|
|
||||||
|
#define NDevDirty(nd) test_ndev_flag(nd, Dirty)
|
||||||
|
#define NDevSetDirty(nd) set_ndev_flag(nd, Dirty)
|
||||||
|
#define NDevClearDirty(nd) clear_ndev_flag(nd, Dirty)
|
||||||
|
|
||||||
|
#define NDevBlock(nd) test_ndev_flag(nd, Block)
|
||||||
|
#define NDevSetBlock(nd) set_ndev_flag(nd, Block)
|
||||||
|
#define NDevClearBlock(nd) clear_ndev_flag(nd, Block)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* struct ntfs_device -
|
||||||
|
*
|
||||||
|
* The ntfs device structure defining all operations needed to access the low
|
||||||
|
* level device underlying the ntfs volume.
|
||||||
|
*/
|
||||||
|
struct ntfs_device {
|
||||||
|
struct ntfs_device_operations *d_ops; /* Device operations. */
|
||||||
|
unsigned long d_state; /* State of the device. */
|
||||||
|
char *d_name; /* Name of device. */
|
||||||
|
void *d_private; /* Private data used by the
|
||||||
|
device operations. */
|
||||||
|
};
|
||||||
|
|
||||||
|
struct stat;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* struct ntfs_device_operations -
|
||||||
|
*
|
||||||
|
* The ntfs device operations defining all operations that can be performed on
|
||||||
|
* the low level device described by an ntfs device structure.
|
||||||
|
*/
|
||||||
|
struct ntfs_device_operations {
|
||||||
|
int (*open)(struct ntfs_device *dev, int flags);
|
||||||
|
int (*close)(struct ntfs_device *dev);
|
||||||
|
s64 (*seek)(struct ntfs_device *dev, s64 offset, int whence);
|
||||||
|
s64 (*read)(struct ntfs_device *dev, void *buf, s64 count);
|
||||||
|
s64 (*write)(struct ntfs_device *dev, const void *buf, s64 count);
|
||||||
|
s64 (*pread)(struct ntfs_device *dev, void *buf, s64 count, s64 offset);
|
||||||
|
s64 (*pwrite)(struct ntfs_device *dev, const void *buf, s64 count,
|
||||||
|
s64 offset);
|
||||||
|
int (*sync)(struct ntfs_device *dev);
|
||||||
|
int (*stat)(struct ntfs_device *dev, struct stat *buf);
|
||||||
|
int (*ioctl)(struct ntfs_device *dev, int request, void *argp);
|
||||||
|
};
|
||||||
|
|
||||||
|
extern struct ntfs_device *ntfs_device_alloc(const char *name, const long state,
|
||||||
|
struct ntfs_device_operations *dops, void *priv_data);
|
||||||
|
extern int ntfs_device_free(struct ntfs_device *dev);
|
||||||
|
|
||||||
|
extern s64 ntfs_pread(struct ntfs_device *dev, const s64 pos, s64 count,
|
||||||
|
void *b);
|
||||||
|
extern s64 ntfs_pwrite(struct ntfs_device *dev, const s64 pos, s64 count,
|
||||||
|
const void *b);
|
||||||
|
|
||||||
|
extern s64 ntfs_mst_pread(struct ntfs_device *dev, const s64 pos, s64 count,
|
||||||
|
const u32 bksize, void *b);
|
||||||
|
extern s64 ntfs_mst_pwrite(struct ntfs_device *dev, const s64 pos, s64 count,
|
||||||
|
const u32 bksize, void *b);
|
||||||
|
|
||||||
|
extern s64 ntfs_cluster_read(const ntfs_volume *vol, const s64 lcn,
|
||||||
|
const s64 count, void *b);
|
||||||
|
extern s64 ntfs_cluster_write(const ntfs_volume *vol, const s64 lcn,
|
||||||
|
const s64 count, const void *b);
|
||||||
|
|
||||||
|
extern s64 ntfs_device_size_get(struct ntfs_device *dev, int block_size);
|
||||||
|
extern s64 ntfs_device_partition_start_sector_get(struct ntfs_device *dev);
|
||||||
|
extern int ntfs_device_heads_get(struct ntfs_device *dev);
|
||||||
|
extern int ntfs_device_sectors_per_track_get(struct ntfs_device *dev);
|
||||||
|
extern int ntfs_device_sector_size_get(struct ntfs_device *dev);
|
||||||
|
extern int ntfs_device_block_size_set(struct ntfs_device *dev, int block_size);
|
||||||
|
|
||||||
|
#endif /* defined _NTFS_DEVICE_H */
|
26
source/libntfs/device_io.c
Normal file
26
source/libntfs/device_io.c
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
/*
|
||||||
|
* device_io.c - Default device io operations. Originated from the Linux-NTFS project.
|
||||||
|
*
|
||||||
|
* Copyright (c) 2003 Anton Altaparmakov
|
||||||
|
*
|
||||||
|
* This program/include file 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/include file 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"
|
||||||
|
|
||||||
|
#ifndef NO_NTFS_DEVICE_DEFAULT_IO_OPS
|
||||||
|
|
||||||
|
#endif /* NO_NTFS_DEVICE_DEFAULT_IO_OPS */
|
68
source/libntfs/device_io.h
Normal file
68
source/libntfs/device_io.h
Normal file
@ -0,0 +1,68 @@
|
|||||||
|
/*
|
||||||
|
* device_io.h - Exports for default device io. Originated from the Linux-NTFS project.
|
||||||
|
*
|
||||||
|
* Copyright (c) 2000-2006 Anton Altaparmakov
|
||||||
|
*
|
||||||
|
* This program/include file 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/include file 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
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _NTFS_DEVICE_IO_H
|
||||||
|
#define _NTFS_DEVICE_IO_H
|
||||||
|
|
||||||
|
#ifdef HAVE_CONFIG_H
|
||||||
|
#include "config.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef NO_NTFS_DEVICE_DEFAULT_IO_OPS
|
||||||
|
|
||||||
|
#ifndef HDIO_GETGEO
|
||||||
|
# define HDIO_GETGEO 0x301
|
||||||
|
/**
|
||||||
|
* struct hd_geometry -
|
||||||
|
*/
|
||||||
|
struct hd_geometry {
|
||||||
|
unsigned char heads;
|
||||||
|
unsigned char sectors;
|
||||||
|
unsigned short cylinders;
|
||||||
|
unsigned long start;
|
||||||
|
};
|
||||||
|
#endif
|
||||||
|
#ifndef BLKGETSIZE
|
||||||
|
# define BLKGETSIZE 0x1260
|
||||||
|
#endif
|
||||||
|
#ifndef BLKSSZGET
|
||||||
|
# define BLKSSZGET 0x1268
|
||||||
|
#endif
|
||||||
|
#ifndef BLKGETSIZE64
|
||||||
|
# define BLKGETSIZE64 0x80041272
|
||||||
|
#endif
|
||||||
|
#ifndef BLKBSZSET
|
||||||
|
# define BLKBSZSET 0x40041271
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* On Nintendo GameCube/Wii; use Gekko low level device operations. */
|
||||||
|
#define ntfs_device_default_io_ops ntfs_device_gekko_io_ops
|
||||||
|
|
||||||
|
|
||||||
|
/* Forward declaration. */
|
||||||
|
struct ntfs_device_operations;
|
||||||
|
|
||||||
|
extern struct ntfs_device_operations ntfs_device_default_io_ops;
|
||||||
|
|
||||||
|
#endif /* NO_NTFS_DEVICE_DEFAULT_IO_OPS */
|
||||||
|
|
||||||
|
#endif /* defined _NTFS_DEVICE_IO_H */
|
||||||
|
|
2301
source/libntfs/dir.c
Normal file
2301
source/libntfs/dir.c
Normal file
File diff suppressed because it is too large
Load Diff
115
source/libntfs/dir.h
Normal file
115
source/libntfs/dir.h
Normal file
@ -0,0 +1,115 @@
|
|||||||
|
/*
|
||||||
|
* dir.h - Exports for directory handling. Originated from the Linux-NTFS project.
|
||||||
|
*
|
||||||
|
* Copyright (c) 2002 Anton Altaparmakov
|
||||||
|
* Copyright (c) 2005-2006 Yura Pakhuchiy
|
||||||
|
* Copyright (c) 2004-2005 Richard Russon
|
||||||
|
* Copyright (c) 2005-2008 Szabolcs Szakacsits
|
||||||
|
*
|
||||||
|
* This program/include file 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/include file 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
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _NTFS_DIR_H
|
||||||
|
#define _NTFS_DIR_H
|
||||||
|
|
||||||
|
#include "types.h"
|
||||||
|
|
||||||
|
#define PATH_SEP '/'
|
||||||
|
|
||||||
|
/*
|
||||||
|
* We do not have these under DJGPP, so define our version that do not conflict
|
||||||
|
* with other S_IFs defined under DJGPP.
|
||||||
|
*/
|
||||||
|
#ifdef DJGPP
|
||||||
|
#ifndef S_IFLNK
|
||||||
|
#define S_IFLNK 0120000
|
||||||
|
#endif
|
||||||
|
#ifndef S_ISLNK
|
||||||
|
#define S_ISLNK(m) (((m) & S_IFMT) == S_IFLNK)
|
||||||
|
#endif
|
||||||
|
#ifndef S_IFSOCK
|
||||||
|
#define S_IFSOCK 0140000
|
||||||
|
#endif
|
||||||
|
#ifndef S_ISSOCK
|
||||||
|
#define S_ISSOCK(m) (((m) & S_IFMT) == S_IFSOCK)
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The little endian Unicode strings $I30, $SII, $SDH, $O, $Q, $R
|
||||||
|
* as a global constant.
|
||||||
|
*/
|
||||||
|
extern ntfschar NTFS_INDEX_I30[5];
|
||||||
|
extern ntfschar NTFS_INDEX_SII[5];
|
||||||
|
extern ntfschar NTFS_INDEX_SDH[5];
|
||||||
|
extern ntfschar NTFS_INDEX_O[3];
|
||||||
|
extern ntfschar NTFS_INDEX_Q[3];
|
||||||
|
extern ntfschar NTFS_INDEX_R[3];
|
||||||
|
|
||||||
|
extern u64 ntfs_inode_lookup_by_name(ntfs_inode *dir_ni,
|
||||||
|
const ntfschar *uname, const int uname_len);
|
||||||
|
|
||||||
|
extern ntfs_inode *ntfs_pathname_to_inode(ntfs_volume *vol, ntfs_inode *parent,
|
||||||
|
const char *pathname);
|
||||||
|
extern ntfs_inode *ntfs_create(ntfs_inode *dir_ni, le32 securid,
|
||||||
|
ntfschar *name, u8 name_len, mode_t type);
|
||||||
|
extern ntfs_inode *ntfs_create_device(ntfs_inode *dir_ni, le32 securid,
|
||||||
|
ntfschar *name, u8 name_len, mode_t type, dev_t dev);
|
||||||
|
extern ntfs_inode *ntfs_create_symlink(ntfs_inode *dir_ni, le32 securid,
|
||||||
|
ntfschar *name, u8 name_len, ntfschar *target, int target_len);
|
||||||
|
extern int ntfs_check_empty_dir(ntfs_inode *ni);
|
||||||
|
extern int ntfs_delete(ntfs_volume *vol, const char *path,
|
||||||
|
ntfs_inode *ni, ntfs_inode *dir_ni, ntfschar *name,
|
||||||
|
u8 name_len);
|
||||||
|
|
||||||
|
extern int ntfs_link(ntfs_inode *ni, ntfs_inode *dir_ni, ntfschar *name,
|
||||||
|
u8 name_len);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* File types (adapted from include <linux/fs.h>)
|
||||||
|
*/
|
||||||
|
#define NTFS_DT_UNKNOWN 0
|
||||||
|
#define NTFS_DT_FIFO 1
|
||||||
|
#define NTFS_DT_CHR 2
|
||||||
|
#define NTFS_DT_DIR 4
|
||||||
|
#define NTFS_DT_BLK 6
|
||||||
|
#define NTFS_DT_REG 8
|
||||||
|
#define NTFS_DT_LNK 10
|
||||||
|
#define NTFS_DT_SOCK 12
|
||||||
|
#define NTFS_DT_WHT 14
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This is the "ntfs_filldir" function type, used by ntfs_readdir() to let
|
||||||
|
* the caller specify what kind of dirent layout it wants to have.
|
||||||
|
* This allows the caller to read directories into their application or
|
||||||
|
* to have different dirent layouts depending on the binary type.
|
||||||
|
*/
|
||||||
|
typedef int (*ntfs_filldir_t)(void *dirent, const ntfschar *name,
|
||||||
|
const int name_len, const int name_type, const s64 pos,
|
||||||
|
const MFT_REF mref, const unsigned dt_type);
|
||||||
|
|
||||||
|
extern int ntfs_readdir(ntfs_inode *dir_ni, s64 *pos,
|
||||||
|
void *dirent, ntfs_filldir_t filldir);
|
||||||
|
|
||||||
|
int ntfs_get_ntfs_dos_name(const char *path,
|
||||||
|
char *value, size_t size, ntfs_inode *ni);
|
||||||
|
int ntfs_set_ntfs_dos_name(const char *path,
|
||||||
|
const char *value, size_t size, int flags,
|
||||||
|
ntfs_inode *ni);
|
||||||
|
int ntfs_remove_ntfs_dos_name(const char *path, ntfs_inode *ni);
|
||||||
|
|
||||||
|
#endif /* defined _NTFS_DIR_H */
|
||||||
|
|
339
source/libntfs/efs.c
Normal file
339
source/libntfs/efs.c
Normal file
@ -0,0 +1,339 @@
|
|||||||
|
/**
|
||||||
|
* efs.c - Limited processing of encrypted files
|
||||||
|
*
|
||||||
|
* This module is part of ntfs-3g library
|
||||||
|
*
|
||||||
|
* Copyright (c) 2009 Martin Bene
|
||||||
|
* Copyright (c) 2009 Jean-Pierre Andre
|
||||||
|
*
|
||||||
|
* This program/include file 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/include file 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
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifdef HAVE_CONFIG_H
|
||||||
|
#include "config.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef HAVE_STDLIB_H
|
||||||
|
#include <stdlib.h>
|
||||||
|
#endif
|
||||||
|
#ifdef HAVE_ERRNO_H
|
||||||
|
#include <errno.h>
|
||||||
|
#endif
|
||||||
|
#ifdef HAVE_STRING_H
|
||||||
|
#include <string.h>
|
||||||
|
#endif
|
||||||
|
#ifdef HAVE_SYS_STAT_H
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef HAVE_SETXATTR
|
||||||
|
#include <sys/xattr.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef HAVE_SYS_SYSMACROS_H
|
||||||
|
#include <sys/sysmacros.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "types.h"
|
||||||
|
#include "debug.h"
|
||||||
|
#include "attrib.h"
|
||||||
|
#include "inode.h"
|
||||||
|
#include "dir.h"
|
||||||
|
#include "efs.h"
|
||||||
|
#include "index.h"
|
||||||
|
#include "logging.h"
|
||||||
|
#include "misc.h"
|
||||||
|
#include "efs.h"
|
||||||
|
|
||||||
|
static ntfschar logged_utility_stream_name[] = {
|
||||||
|
const_cpu_to_le16('$'),
|
||||||
|
const_cpu_to_le16('E'),
|
||||||
|
const_cpu_to_le16('F'),
|
||||||
|
const_cpu_to_le16('S'),
|
||||||
|
const_cpu_to_le16(0)
|
||||||
|
} ;
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Get the ntfs EFS info into an extended attribute
|
||||||
|
*/
|
||||||
|
|
||||||
|
int ntfs_get_efs_info(const char *path,
|
||||||
|
char *value, size_t size, ntfs_inode *ni)
|
||||||
|
{
|
||||||
|
EFS_ATTR_HEADER *efs_info;
|
||||||
|
s64 attr_size = 0;
|
||||||
|
|
||||||
|
if (ni) {
|
||||||
|
if (ni->flags & FILE_ATTR_ENCRYPTED) {
|
||||||
|
efs_info = (EFS_ATTR_HEADER*)ntfs_attr_readall(ni,
|
||||||
|
AT_LOGGED_UTILITY_STREAM,(ntfschar*)NULL, 0,
|
||||||
|
&attr_size);
|
||||||
|
if (efs_info
|
||||||
|
&& (le32_to_cpu(efs_info->length) == attr_size)) {
|
||||||
|
if (attr_size <= (s64)size) {
|
||||||
|
if (value)
|
||||||
|
memcpy(value,efs_info,attr_size);
|
||||||
|
else {
|
||||||
|
errno = EFAULT;
|
||||||
|
attr_size = 0;
|
||||||
|
}
|
||||||
|
} else
|
||||||
|
if (size) {
|
||||||
|
errno = ERANGE;
|
||||||
|
attr_size = 0;
|
||||||
|
}
|
||||||
|
free (efs_info);
|
||||||
|
} else {
|
||||||
|
if (efs_info) {
|
||||||
|
free(efs_info);
|
||||||
|
ntfs_log_info("Bad efs_info for file %s\n",path);
|
||||||
|
} else {
|
||||||
|
ntfs_log_info("Could not get efsinfo"
|
||||||
|
" for file %s\n", path);
|
||||||
|
}
|
||||||
|
errno = EIO;
|
||||||
|
attr_size = 0;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
errno = ENODATA;
|
||||||
|
ntfs_log_info("File %s is not encrypted",path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return (attr_size ? (int)attr_size : -errno);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Set the efs data from an extended attribute
|
||||||
|
* Warning : the new data is not checked
|
||||||
|
* Returns 0, or -1 if there is a problem
|
||||||
|
*/
|
||||||
|
|
||||||
|
int ntfs_set_efs_info(const char *path __attribute__((unused)),
|
||||||
|
const char *value, size_t size, int flags,
|
||||||
|
ntfs_inode *ni)
|
||||||
|
{
|
||||||
|
int res;
|
||||||
|
int written;
|
||||||
|
ntfs_attr *na;
|
||||||
|
const EFS_ATTR_HEADER *info_header;
|
||||||
|
ntfs_attr_search_ctx *ctx;
|
||||||
|
|
||||||
|
res = 0;
|
||||||
|
if (ni && value && size) {
|
||||||
|
if (ni->flags & (FILE_ATTR_ENCRYPTED | FILE_ATTR_COMPRESSED)) {
|
||||||
|
if (ni->flags & FILE_ATTR_ENCRYPTED) {
|
||||||
|
ntfs_log_info("File %s already encrypted",path);
|
||||||
|
errno = EEXIST;
|
||||||
|
} else {
|
||||||
|
/*
|
||||||
|
* Possible problem : if encrypted file was
|
||||||
|
* restored in a compressed directory, it was
|
||||||
|
* restored as compressed.
|
||||||
|
* TODO : decompress first.
|
||||||
|
*/
|
||||||
|
ntfs_log_error("File %s cannot be encrypted and compressed\n",
|
||||||
|
path);
|
||||||
|
errno = EIO;
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
info_header = (const EFS_ATTR_HEADER*)value;
|
||||||
|
/* make sure we get a likely efsinfo */
|
||||||
|
if (le32_to_cpu(info_header->length) != size) {
|
||||||
|
errno = EINVAL;
|
||||||
|
return (-1);
|
||||||
|
}
|
||||||
|
if (!ntfs_attr_exist(ni,AT_LOGGED_UTILITY_STREAM,
|
||||||
|
(ntfschar*)NULL,0)) {
|
||||||
|
if (!(flags & XATTR_REPLACE)) {
|
||||||
|
/*
|
||||||
|
* no logged_utility_stream attribute : add one,
|
||||||
|
* apparently, this does not feed the new value in
|
||||||
|
*/
|
||||||
|
res = ntfs_attr_add(ni,AT_LOGGED_UTILITY_STREAM,
|
||||||
|
logged_utility_stream_name,4,
|
||||||
|
(u8*)NULL,(s64)size);
|
||||||
|
} else {
|
||||||
|
errno = ENODATA;
|
||||||
|
res = -1;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
errno = EEXIST;
|
||||||
|
res = -1;
|
||||||
|
}
|
||||||
|
if (!res) {
|
||||||
|
/*
|
||||||
|
* open and update the existing efs data
|
||||||
|
*/
|
||||||
|
na = ntfs_attr_open(ni, AT_LOGGED_UTILITY_STREAM,
|
||||||
|
logged_utility_stream_name, 4);
|
||||||
|
if (na) {
|
||||||
|
/* resize attribute */
|
||||||
|
res = ntfs_attr_truncate(na, (s64)size);
|
||||||
|
/* overwrite value if any */
|
||||||
|
if (!res && value) {
|
||||||
|
written = (int)ntfs_attr_pwrite(na,
|
||||||
|
(s64)0, (s64)size, value);
|
||||||
|
if (written != (s64)size) {
|
||||||
|
ntfs_log_error("Failed to "
|
||||||
|
"update efs data\n");
|
||||||
|
errno = EIO;
|
||||||
|
res = -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ntfs_attr_close(na);
|
||||||
|
} else
|
||||||
|
res = -1;
|
||||||
|
}
|
||||||
|
if (!res) {
|
||||||
|
/* Don't handle AT_DATA Attribute(s) if inode is a directory */
|
||||||
|
if (!(ni->mrec->flags & MFT_RECORD_IS_DIRECTORY)) {
|
||||||
|
/* iterate over AT_DATA attributes */
|
||||||
|
/* set encrypted flag, truncate attribute to match padding bytes */
|
||||||
|
|
||||||
|
ctx = ntfs_attr_get_search_ctx(ni, NULL);
|
||||||
|
if (!ctx) {
|
||||||
|
ntfs_log_error("Failed to get ctx for efs\n");
|
||||||
|
return (-1);
|
||||||
|
}
|
||||||
|
while (!ntfs_attr_lookup(AT_DATA, NULL, 0,
|
||||||
|
CASE_SENSITIVE, 0, NULL, 0, ctx)) {
|
||||||
|
if (ntfs_efs_fixup_attribute(ctx, NULL)) {
|
||||||
|
ntfs_log_error("Error in efs fixup of AT_DATA Attribute");
|
||||||
|
ntfs_attr_put_search_ctx(ctx);
|
||||||
|
return(-1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ntfs_attr_put_search_ctx(ctx);
|
||||||
|
}
|
||||||
|
ni->flags |= FILE_ATTR_ENCRYPTED;
|
||||||
|
NInoSetDirty(ni);
|
||||||
|
NInoFileNameSetDirty(ni);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
errno = EINVAL;
|
||||||
|
res = -1;
|
||||||
|
}
|
||||||
|
return (res ? -1 : 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Fixup raw encrypted AT_DATA Attribute
|
||||||
|
* read padding length from last two bytes
|
||||||
|
* truncate attribute, make non-resident,
|
||||||
|
* set data size to match padding length
|
||||||
|
* set ATTR_IS_ENCRYPTED flag on attribute
|
||||||
|
*
|
||||||
|
* Return 0 if successful
|
||||||
|
* -1 if failed (errno tells why)
|
||||||
|
*/
|
||||||
|
|
||||||
|
int ntfs_efs_fixup_attribute(ntfs_attr_search_ctx *ctx, ntfs_attr *na)
|
||||||
|
{
|
||||||
|
u64 newsize;
|
||||||
|
le16 appended_bytes;
|
||||||
|
u16 padding_length;
|
||||||
|
ATTR_RECORD *a;
|
||||||
|
ntfs_inode *ni;
|
||||||
|
BOOL close_na = FALSE;
|
||||||
|
BOOL close_ctx = FALSE;
|
||||||
|
|
||||||
|
if (!ctx && !na) {
|
||||||
|
ntfs_log_error("neither ctx nor na specified for efs_fixup_attribute\n");
|
||||||
|
goto err_out;
|
||||||
|
}
|
||||||
|
if (!ctx) {
|
||||||
|
ctx = ntfs_attr_get_search_ctx(na->ni, NULL);
|
||||||
|
if (!ctx) {
|
||||||
|
ntfs_log_error("Failed to get ctx for efs\n");
|
||||||
|
goto err_out;
|
||||||
|
}
|
||||||
|
close_ctx=TRUE;
|
||||||
|
if (ntfs_attr_lookup(AT_DATA, na->name, na->name_len,
|
||||||
|
CASE_SENSITIVE, 0, NULL, 0, ctx)) {
|
||||||
|
ntfs_log_error("attr lookup for AT_DATA attribute failed in efs fixup\n");
|
||||||
|
goto err_out;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
a = ctx->attr;
|
||||||
|
if (!na) {
|
||||||
|
na = ntfs_attr_open(ctx->ntfs_ino, AT_DATA,
|
||||||
|
(ntfschar*)((u8*)a + le16_to_cpu(a->name_offset)),
|
||||||
|
a->name_length);
|
||||||
|
if (!na) {
|
||||||
|
ntfs_log_error("can't open DATA Attribute\n");
|
||||||
|
return (-1);
|
||||||
|
}
|
||||||
|
close_na = TRUE;
|
||||||
|
}
|
||||||
|
/* make sure size is valid for a raw encrypted stream */
|
||||||
|
if ((na->data_size & 511) != 2) {
|
||||||
|
ntfs_log_error("Bad raw encrypted stream");
|
||||||
|
goto err_out;
|
||||||
|
}
|
||||||
|
/* read padding length from last two bytes of attribute */
|
||||||
|
if (ntfs_attr_pread(na, na->data_size-2, 2, &appended_bytes) != 2) {
|
||||||
|
ntfs_log_error("Error reading padding length\n");
|
||||||
|
goto err_out;
|
||||||
|
}
|
||||||
|
padding_length = le16_to_cpu(appended_bytes);
|
||||||
|
if (padding_length > 511 || padding_length > na->data_size-2) {
|
||||||
|
errno = EINVAL;
|
||||||
|
ntfs_log_error("invalid padding length %d for data_size %lld\n",
|
||||||
|
padding_length, (long long)na->data_size);
|
||||||
|
goto err_out;
|
||||||
|
}
|
||||||
|
newsize = na->data_size - padding_length - 2;
|
||||||
|
/* truncate attribute to possibly free clusters allocated
|
||||||
|
for the last two bytes */
|
||||||
|
if (ntfs_attr_truncate(na, na->data_size-2)) {
|
||||||
|
ntfs_log_error("Error truncating attribute\n");
|
||||||
|
goto err_out;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Encrypted AT_DATA Attributes MUST be non-resident */
|
||||||
|
if (!NAttrNonResident(na)
|
||||||
|
&& ntfs_attr_make_non_resident(na, ctx)) {
|
||||||
|
ntfs_log_error("Error making DATA attribute non-resident\n");
|
||||||
|
goto err_out;
|
||||||
|
}
|
||||||
|
ni = na->ni;
|
||||||
|
if (!na->name_len) {
|
||||||
|
ni->data_size = newsize;
|
||||||
|
ni->allocated_size = na->allocated_size;
|
||||||
|
}
|
||||||
|
NInoSetDirty(ni);
|
||||||
|
NInoFileNameSetDirty(ni);
|
||||||
|
if (close_na)
|
||||||
|
ntfs_attr_close(na);
|
||||||
|
|
||||||
|
ctx->attr->data_size = cpu_to_le64(newsize);
|
||||||
|
if (le64_to_cpu(ctx->attr->initialized_size) > newsize)
|
||||||
|
ctx->attr->initialized_size = ctx->attr->data_size;
|
||||||
|
ctx->attr->flags |= ATTR_IS_ENCRYPTED;
|
||||||
|
if (close_ctx)
|
||||||
|
ntfs_attr_put_search_ctx(ctx);
|
||||||
|
|
||||||
|
return (0);
|
||||||
|
err_out:
|
||||||
|
if (close_na && na)
|
||||||
|
ntfs_attr_close(na);
|
||||||
|
if (close_ctx && ctx)
|
||||||
|
ntfs_attr_put_search_ctx(ctx);
|
||||||
|
return (-1);
|
||||||
|
}
|
31
source/libntfs/efs.h
Normal file
31
source/libntfs/efs.h
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
/*
|
||||||
|
*
|
||||||
|
* Copyright (c) 2009 Martin Bene
|
||||||
|
*
|
||||||
|
* This program/include file 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/include file 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
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef EFS_H
|
||||||
|
#define EFS_H
|
||||||
|
|
||||||
|
int ntfs_get_efs_info(const char *path,
|
||||||
|
char *value, size_t size, ntfs_inode *ni);
|
||||||
|
int ntfs_set_efs_info(const char *path,
|
||||||
|
const char *value, size_t size, int flags,
|
||||||
|
ntfs_inode *ni);
|
||||||
|
int ntfs_efs_fixup_attribute(ntfs_attr_search_ctx *ctx, ntfs_attr *na);
|
||||||
|
|
||||||
|
#endif /* EFS_H */
|
203
source/libntfs/endians.h
Normal file
203
source/libntfs/endians.h
Normal file
@ -0,0 +1,203 @@
|
|||||||
|
/*
|
||||||
|
* endians.h - Definitions related to handling of byte ordering.
|
||||||
|
* Originated from the Linux-NTFS project.
|
||||||
|
*
|
||||||
|
* Copyright (c) 2000-2005 Anton Altaparmakov
|
||||||
|
*
|
||||||
|
* This program/include file 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/include file 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
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _NTFS_ENDIANS_H
|
||||||
|
#define _NTFS_ENDIANS_H
|
||||||
|
|
||||||
|
#ifdef HAVE_CONFIG_H
|
||||||
|
#include "config.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Notes:
|
||||||
|
* We define the conversion functions including typecasts since the
|
||||||
|
* defaults don't necessarily perform appropriate typecasts.
|
||||||
|
* Also, using our own functions means that we can change them if it
|
||||||
|
* turns out that we do need to use the unaligned access macros on
|
||||||
|
* architectures requiring aligned memory accesses...
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifdef HAVE_ENDIAN_H
|
||||||
|
#include <endian.h>
|
||||||
|
#endif
|
||||||
|
#ifdef HAVE_SYS_ENDIAN_H
|
||||||
|
#include <sys/endian.h>
|
||||||
|
#endif
|
||||||
|
#ifdef HAVE_MACHINE_ENDIAN_H
|
||||||
|
#include <machine/endian.h>
|
||||||
|
#endif
|
||||||
|
#ifdef HAVE_SYS_BYTEORDER_H
|
||||||
|
#include <sys/byteorder.h>
|
||||||
|
#endif
|
||||||
|
#ifdef HAVE_SYS_PARAM_H
|
||||||
|
#include <sys/param.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef __BYTE_ORDER
|
||||||
|
# if defined(_BYTE_ORDER)
|
||||||
|
# define __BYTE_ORDER _BYTE_ORDER
|
||||||
|
# define __LITTLE_ENDIAN _LITTLE_ENDIAN
|
||||||
|
# define __BIG_ENDIAN _BIG_ENDIAN
|
||||||
|
# elif defined(BYTE_ORDER)
|
||||||
|
# define __BYTE_ORDER BYTE_ORDER
|
||||||
|
# define __LITTLE_ENDIAN LITTLE_ENDIAN
|
||||||
|
# define __BIG_ENDIAN BIG_ENDIAN
|
||||||
|
# elif defined(__BYTE_ORDER__)
|
||||||
|
# define __BYTE_ORDER __BYTE_ORDER__
|
||||||
|
# define __LITTLE_ENDIAN __LITTLE_ENDIAN__
|
||||||
|
# define __BIG_ENDIAN __BIG_ENDIAN__
|
||||||
|
# elif (defined(_LITTLE_ENDIAN) && !defined(_BIG_ENDIAN)) || \
|
||||||
|
defined(WORDS_LITTLEENDIAN)
|
||||||
|
# define __BYTE_ORDER 1
|
||||||
|
# define __LITTLE_ENDIAN 1
|
||||||
|
# define __BIG_ENDIAN 0
|
||||||
|
# elif (!defined(_LITTLE_ENDIAN) && defined(_BIG_ENDIAN)) || \
|
||||||
|
defined(WORDS_BIGENDIAN)
|
||||||
|
# define __BYTE_ORDER 0
|
||||||
|
# define __LITTLE_ENDIAN 1
|
||||||
|
# define __BIG_ENDIAN 0
|
||||||
|
# else
|
||||||
|
# error "__BYTE_ORDER is not defined."
|
||||||
|
# endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define __ntfs_bswap_constant_16(x) \
|
||||||
|
(u16)((((u16)(x) & 0xff00) >> 8) | \
|
||||||
|
(((u16)(x) & 0x00ff) << 8))
|
||||||
|
|
||||||
|
#define __ntfs_bswap_constant_32(x) \
|
||||||
|
(u32)((((u32)(x) & 0xff000000u) >> 24) | \
|
||||||
|
(((u32)(x) & 0x00ff0000u) >> 8) | \
|
||||||
|
(((u32)(x) & 0x0000ff00u) << 8) | \
|
||||||
|
(((u32)(x) & 0x000000ffu) << 24))
|
||||||
|
|
||||||
|
#define __ntfs_bswap_constant_64(x) \
|
||||||
|
(u64)((((u64)(x) & 0xff00000000000000ull) >> 56) | \
|
||||||
|
(((u64)(x) & 0x00ff000000000000ull) >> 40) | \
|
||||||
|
(((u64)(x) & 0x0000ff0000000000ull) >> 24) | \
|
||||||
|
(((u64)(x) & 0x000000ff00000000ull) >> 8) | \
|
||||||
|
(((u64)(x) & 0x00000000ff000000ull) << 8) | \
|
||||||
|
(((u64)(x) & 0x0000000000ff0000ull) << 24) | \
|
||||||
|
(((u64)(x) & 0x000000000000ff00ull) << 40) | \
|
||||||
|
(((u64)(x) & 0x00000000000000ffull) << 56))
|
||||||
|
|
||||||
|
#ifdef HAVE_BYTESWAP_H
|
||||||
|
# include <byteswap.h>
|
||||||
|
#else
|
||||||
|
# define bswap_16(x) __ntfs_bswap_constant_16(x)
|
||||||
|
# define bswap_32(x) __ntfs_bswap_constant_32(x)
|
||||||
|
# define bswap_64(x) __ntfs_bswap_constant_64(x)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(__LITTLE_ENDIAN) && (__BYTE_ORDER == __LITTLE_ENDIAN)
|
||||||
|
|
||||||
|
#define __le16_to_cpu(x) (x)
|
||||||
|
#define __le32_to_cpu(x) (x)
|
||||||
|
#define __le64_to_cpu(x) (x)
|
||||||
|
|
||||||
|
#define __cpu_to_le16(x) (x)
|
||||||
|
#define __cpu_to_le32(x) (x)
|
||||||
|
#define __cpu_to_le64(x) (x)
|
||||||
|
|
||||||
|
#define __constant_le16_to_cpu(x) (x)
|
||||||
|
#define __constant_le32_to_cpu(x) (x)
|
||||||
|
#define __constant_le64_to_cpu(x) (x)
|
||||||
|
|
||||||
|
#define __constant_cpu_to_le16(x) (x)
|
||||||
|
#define __constant_cpu_to_le32(x) (x)
|
||||||
|
#define __constant_cpu_to_le64(x) (x)
|
||||||
|
|
||||||
|
#elif defined(__BIG_ENDIAN) && (__BYTE_ORDER == __BIG_ENDIAN)
|
||||||
|
|
||||||
|
#define __le16_to_cpu(x) bswap_16(x)
|
||||||
|
#define __le32_to_cpu(x) bswap_32(x)
|
||||||
|
#define __le64_to_cpu(x) bswap_64(x)
|
||||||
|
|
||||||
|
#define __cpu_to_le16(x) bswap_16(x)
|
||||||
|
#define __cpu_to_le32(x) bswap_32(x)
|
||||||
|
#define __cpu_to_le64(x) bswap_64(x)
|
||||||
|
|
||||||
|
#define __constant_le16_to_cpu(x) __ntfs_bswap_constant_16((u16)(x))
|
||||||
|
#define __constant_le32_to_cpu(x) __ntfs_bswap_constant_32((u32)(x))
|
||||||
|
#define __constant_le64_to_cpu(x) __ntfs_bswap_constant_64((u64)(x))
|
||||||
|
|
||||||
|
#define __constant_cpu_to_le16(x) __ntfs_bswap_constant_16((u16)(x))
|
||||||
|
#define __constant_cpu_to_le32(x) __ntfs_bswap_constant_32((u32)(x))
|
||||||
|
#define __constant_cpu_to_le64(x) __ntfs_bswap_constant_64((u64)(x))
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
#error "You must define __BYTE_ORDER to be __LITTLE_ENDIAN or __BIG_ENDIAN."
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Unsigned from LE to CPU conversion. */
|
||||||
|
|
||||||
|
#define le16_to_cpu(x) (u16)__le16_to_cpu((u16)(x))
|
||||||
|
#define le32_to_cpu(x) (u32)__le32_to_cpu((u32)(x))
|
||||||
|
#define le64_to_cpu(x) (u64)__le64_to_cpu((u64)(x))
|
||||||
|
|
||||||
|
#define le16_to_cpup(x) (u16)__le16_to_cpu(*(const u16*)(x))
|
||||||
|
#define le32_to_cpup(x) (u32)__le32_to_cpu(*(const u32*)(x))
|
||||||
|
#define le64_to_cpup(x) (u64)__le64_to_cpu(*(const u64*)(x))
|
||||||
|
|
||||||
|
/* Signed from LE to CPU conversion. */
|
||||||
|
|
||||||
|
#define sle16_to_cpu(x) (s16)__le16_to_cpu((s16)(x))
|
||||||
|
#define sle32_to_cpu(x) (s32)__le32_to_cpu((s32)(x))
|
||||||
|
#define sle64_to_cpu(x) (s64)__le64_to_cpu((s64)(x))
|
||||||
|
|
||||||
|
#define sle16_to_cpup(x) (s16)__le16_to_cpu(*(s16*)(x))
|
||||||
|
#define sle32_to_cpup(x) (s32)__le32_to_cpu(*(s32*)(x))
|
||||||
|
#define sle64_to_cpup(x) (s64)__le64_to_cpu(*(s64*)(x))
|
||||||
|
|
||||||
|
/* Unsigned from CPU to LE conversion. */
|
||||||
|
|
||||||
|
#define cpu_to_le16(x) (u16)__cpu_to_le16((u16)(x))
|
||||||
|
#define cpu_to_le32(x) (u32)__cpu_to_le32((u32)(x))
|
||||||
|
#define cpu_to_le64(x) (u64)__cpu_to_le64((u64)(x))
|
||||||
|
|
||||||
|
#define cpu_to_le16p(x) (u16)__cpu_to_le16(*(u16*)(x))
|
||||||
|
#define cpu_to_le32p(x) (u32)__cpu_to_le32(*(u32*)(x))
|
||||||
|
#define cpu_to_le64p(x) (u64)__cpu_to_le64(*(u64*)(x))
|
||||||
|
|
||||||
|
/* Signed from CPU to LE conversion. */
|
||||||
|
|
||||||
|
#define cpu_to_sle16(x) (s16)__cpu_to_le16((s16)(x))
|
||||||
|
#define cpu_to_sle32(x) (s32)__cpu_to_le32((s32)(x))
|
||||||
|
#define cpu_to_sle64(x) (s64)__cpu_to_le64((s64)(x))
|
||||||
|
|
||||||
|
#define cpu_to_sle16p(x) (s16)__cpu_to_le16(*(s16*)(x))
|
||||||
|
#define cpu_to_sle32p(x) (s32)__cpu_to_le32(*(s32*)(x))
|
||||||
|
#define cpu_to_sle64p(x) (s64)__cpu_to_le64(*(s64*)(x))
|
||||||
|
|
||||||
|
/* Constant endianness conversion defines. */
|
||||||
|
|
||||||
|
#define const_le16_to_cpu(x) __constant_le16_to_cpu(x)
|
||||||
|
#define const_le32_to_cpu(x) __constant_le32_to_cpu(x)
|
||||||
|
#define const_le64_to_cpu(x) __constant_le64_to_cpu(x)
|
||||||
|
|
||||||
|
#define const_cpu_to_le16(x) __constant_cpu_to_le16(x)
|
||||||
|
#define const_cpu_to_le32(x) __constant_cpu_to_le32(x)
|
||||||
|
#define const_cpu_to_le64(x) __constant_cpu_to_le64(x)
|
||||||
|
|
||||||
|
#endif /* defined _NTFS_ENDIANS_H */
|
643
source/libntfs/gekko_io.c
Normal file
643
source/libntfs/gekko_io.c
Normal file
@ -0,0 +1,643 @@
|
|||||||
|
/**
|
||||||
|
* gekko_io.c - Gekko style disk io functions.
|
||||||
|
*
|
||||||
|
* Copyright (c) 2009 Rhys "Shareese" Koedijk
|
||||||
|
*
|
||||||
|
* This program/include file 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/include file 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; if not, write to the Free Software Foundation,
|
||||||
|
* Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifdef HAVE_CONFIG_H
|
||||||
|
#include "config.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef HAVE_STDLIB_H
|
||||||
|
#include <stdlib.h>
|
||||||
|
#endif
|
||||||
|
#ifdef HAVE_STDIO_H
|
||||||
|
#include <stdio.h>
|
||||||
|
#endif
|
||||||
|
#ifdef HAVE_MATH_H
|
||||||
|
#include <math.h>
|
||||||
|
#endif
|
||||||
|
#ifdef HAVE_STRING_H
|
||||||
|
#include <string.h>
|
||||||
|
#endif
|
||||||
|
#ifdef HAVE_FCNTL_H
|
||||||
|
#include <fcntl.h>
|
||||||
|
#endif
|
||||||
|
#ifdef HAVE_UNISTD_H
|
||||||
|
#include <unistd.h>
|
||||||
|
#endif
|
||||||
|
#ifdef HAVE_ERRNO_H
|
||||||
|
#include <errno.h>
|
||||||
|
#endif
|
||||||
|
#ifdef HAVE_SYS_STAT_H
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#endif
|
||||||
|
#ifdef HAVE_LIMITS_H
|
||||||
|
#include <limits.h>
|
||||||
|
#endif
|
||||||
|
#ifdef HAVE_LOCALE_H
|
||||||
|
#include <locale.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "ntfs.h"
|
||||||
|
#include "types.h"
|
||||||
|
#include "logging.h"
|
||||||
|
#include "device_io.h"
|
||||||
|
#include "gekko_io.h"
|
||||||
|
#include "cache.h"
|
||||||
|
#include "device.h"
|
||||||
|
#include "bootsect.h"
|
||||||
|
|
||||||
|
#define DEV_FD(dev) ((gekko_fd *)dev->d_private)
|
||||||
|
|
||||||
|
/* Prototypes */
|
||||||
|
static s64 ntfs_device_gekko_io_readbytes(struct ntfs_device *dev, s64 offset, s64 count, void *buf);
|
||||||
|
static bool ntfs_device_gekko_io_readsectors(struct ntfs_device *dev, sec_t sector, sec_t numSectors, void* buffer);
|
||||||
|
static s64 ntfs_device_gekko_io_writebytes(struct ntfs_device *dev, s64 offset, s64 count, const void *buf);
|
||||||
|
static bool ntfs_device_gekko_io_writesectors(struct ntfs_device *dev, sec_t sector, sec_t numSectors, const void* buffer);
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
static int ntfs_device_gekko_io_open(struct ntfs_device *dev, int flags)
|
||||||
|
{
|
||||||
|
ntfs_log_trace("dev %p, flags %i\n", dev, flags);
|
||||||
|
|
||||||
|
// Get the device driver descriptor
|
||||||
|
gekko_fd *fd = DEV_FD(dev);
|
||||||
|
if (!fd) {
|
||||||
|
errno = EBADF;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the device interface
|
||||||
|
const DISC_INTERFACE* interface = fd->interface;
|
||||||
|
if (!interface) {
|
||||||
|
errno = ENODEV;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start the device interface and ensure that it is inserted
|
||||||
|
if (!interface->startup()) {
|
||||||
|
ntfs_log_perror("device failed to start\n");
|
||||||
|
errno = EIO;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (!interface->isInserted()) {
|
||||||
|
ntfs_log_perror("device media is not inserted\n");
|
||||||
|
errno = EIO;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check that the device isn't already open (used by another volume?)
|
||||||
|
if (NDevOpen(dev)) {
|
||||||
|
ntfs_log_perror("device is busy (already open)\n");
|
||||||
|
errno = EBUSY;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check that there is a valid NTFS boot sector at the start of the device
|
||||||
|
NTFS_BOOT_SECTOR boot;
|
||||||
|
if (interface->readSectors(fd->startSector, 1, &boot)) {
|
||||||
|
if (!ntfs_boot_sector_is_ntfs(&boot)) {
|
||||||
|
errno = EINVALPART;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
ntfs_log_perror("read failure @ sector %d\n", fd->startSector);
|
||||||
|
errno = EIO;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse the boot sector
|
||||||
|
fd->hiddenSectors = le32_to_cpu(boot.bpb.hidden_sectors);
|
||||||
|
fd->sectorSize = le16_to_cpu(boot.bpb.bytes_per_sector);
|
||||||
|
fd->sectorCount = sle64_to_cpu(boot.number_of_sectors);
|
||||||
|
fd->pos = 0;
|
||||||
|
fd->len = (fd->sectorCount * fd->sectorSize);
|
||||||
|
fd->ino = le64_to_cpu(boot.volume_serial_number);
|
||||||
|
|
||||||
|
// If the device sector size is not 512 bytes then we cannot continue,
|
||||||
|
// gekko disc I/O works on the assumption that sectors are always 512 bytes long.
|
||||||
|
// TODO: Implement support for non-512 byte sector sizes through some fancy maths!?
|
||||||
|
if (fd->sectorSize != BYTES_PER_SECTOR) {
|
||||||
|
ntfs_log_error("Boot sector claims there is %i bytes per sector; expected %i\n", fd->sectorSize, BYTES_PER_SECTOR);
|
||||||
|
errno = EIO;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mark the device as read-only (if required)
|
||||||
|
if (flags & O_RDONLY) {
|
||||||
|
NDevSetReadOnly(dev);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create the cache
|
||||||
|
fd->cache = _NTFS_cache_constructor(fd->cachePageCount, fd->cachePageSize, interface, fd->startSector + fd->sectorCount);
|
||||||
|
|
||||||
|
// Mark the device as open
|
||||||
|
NDevSetBlock(dev);
|
||||||
|
NDevSetOpen(dev);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
static int ntfs_device_gekko_io_close(struct ntfs_device *dev)
|
||||||
|
{
|
||||||
|
ntfs_log_trace("dev %p\n", dev);
|
||||||
|
|
||||||
|
// Get the device driver descriptor
|
||||||
|
gekko_fd *fd = DEV_FD(dev);
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
static s64 ntfs_device_gekko_io_seek(struct ntfs_device *dev, s64 offset, int whence)
|
||||||
|
{
|
||||||
|
ntfs_log_trace("dev %p, offset %Li, whence %i\n", dev, offset, whence);
|
||||||
|
|
||||||
|
// Get the device driver descriptor
|
||||||
|
gekko_fd *fd = DEV_FD(dev);
|
||||||
|
if (!fd) {
|
||||||
|
errno = EBADF;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set the current position on the device (in bytes)
|
||||||
|
switch(whence) {
|
||||||
|
case SEEK_SET: fd->pos = MIN(MAX(offset, 0), fd->len); break;
|
||||||
|
case SEEK_CUR: fd->pos = MIN(MAX(fd->pos + offset, 0), fd->len); break;
|
||||||
|
case SEEK_END: fd->pos = MIN(MAX(fd->len + offset, 0), fd->len); break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
static s64 ntfs_device_gekko_io_read(struct ntfs_device *dev, void *buf, s64 count)
|
||||||
|
{
|
||||||
|
return ntfs_device_gekko_io_readbytes(dev, DEV_FD(dev)->pos, count, buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
static s64 ntfs_device_gekko_io_write(struct ntfs_device *dev, const void *buf, s64 count)
|
||||||
|
{
|
||||||
|
return ntfs_device_gekko_io_writebytes(dev, DEV_FD(dev)->pos, count, buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
static s64 ntfs_device_gekko_io_pread(struct ntfs_device *dev, void *buf, s64 count, s64 offset)
|
||||||
|
{
|
||||||
|
return ntfs_device_gekko_io_readbytes(dev, offset, count, buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
static s64 ntfs_device_gekko_io_pwrite(struct ntfs_device *dev, const void *buf, s64 count, s64 offset)
|
||||||
|
{
|
||||||
|
return ntfs_device_gekko_io_writebytes(dev, offset, count, buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
static s64 ntfs_device_gekko_io_readbytes(struct ntfs_device *dev, s64 offset, s64 count, void *buf)
|
||||||
|
{
|
||||||
|
ntfs_log_trace("dev %p, offset %Li, count %Li\n", dev, offset, count);
|
||||||
|
|
||||||
|
// Get the device driver descriptor
|
||||||
|
gekko_fd *fd = DEV_FD(dev);
|
||||||
|
if (!fd) {
|
||||||
|
errno = EBADF;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the device interface
|
||||||
|
const DISC_INTERFACE* interface = fd->interface;
|
||||||
|
if (!interface) {
|
||||||
|
errno = ENODEV;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
sec_t sec_start = fd->startSector;
|
||||||
|
sec_t sec_count = 1;
|
||||||
|
u16 buffer_offset = 0;
|
||||||
|
u8 *buffer;
|
||||||
|
|
||||||
|
// Determine the range of sectors required for this read
|
||||||
|
if (offset > 0) {
|
||||||
|
sec_start += floor(offset / fd->sectorSize);
|
||||||
|
buffer_offset = offset % fd->sectorSize;
|
||||||
|
}
|
||||||
|
if (count > fd->sectorSize) {
|
||||||
|
sec_count = ceil(count / fd->sectorSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
// If this read happens to be on the sector boundaries then do the read straight into the destination buffer
|
||||||
|
if((offset % fd->sectorSize == 0) && (count % fd->sectorSize == 0)) {
|
||||||
|
|
||||||
|
// Read from the device
|
||||||
|
ntfs_log_trace("direct read from sector %d (%d sector(s) long)\n", sec_start, sec_count);
|
||||||
|
if (!ntfs_device_gekko_io_readsectors(dev, sec_start, sec_count, buf)) {
|
||||||
|
ntfs_log_perror("direct read failure @ sector %d (%d sector(s) long)\n", sec_start, sec_count);
|
||||||
|
errno = EIO;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Else read into a buffer and copy over only what was requested
|
||||||
|
} else {
|
||||||
|
|
||||||
|
// Allocate a buffer to hold the read data
|
||||||
|
buffer = (u8*)ntfs_alloc(sec_count * fd->sectorSize);
|
||||||
|
if (!buffer) {
|
||||||
|
errno = ENOMEM;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read from the device
|
||||||
|
ntfs_log_trace("buffered read from sector %d (%d sector(s) long)\n", sec_start, sec_count);
|
||||||
|
if (!ntfs_device_gekko_io_readsectors(dev, sec_start, sec_count, buffer)) {
|
||||||
|
ntfs_log_perror("buffered read failure @ sector %d (%d sector(s) long)\n", sec_start, sec_count);
|
||||||
|
ntfs_free(buffer);
|
||||||
|
errno = EIO;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Copy what was requested to the destination buffer
|
||||||
|
memcpy(buf, buffer + buffer_offset, count);
|
||||||
|
ntfs_free(buffer);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
static s64 ntfs_device_gekko_io_writebytes(struct ntfs_device *dev, s64 offset, s64 count, const void *buf)
|
||||||
|
{
|
||||||
|
ntfs_log_trace("dev %p, offset %Li, count %Li\n", dev, offset, count);
|
||||||
|
|
||||||
|
// Get the device driver descriptor
|
||||||
|
gekko_fd *fd = DEV_FD(dev);
|
||||||
|
if (!fd) {
|
||||||
|
errno = EBADF;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the device interface
|
||||||
|
const DISC_INTERFACE* interface = fd->interface;
|
||||||
|
if (!interface) {
|
||||||
|
errno = ENODEV;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check that the device can be written to
|
||||||
|
if (NDevReadOnly(dev)) {
|
||||||
|
errno = EROFS;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
sec_t sec_start = fd->startSector;
|
||||||
|
sec_t sec_count = 1;
|
||||||
|
u16 buffer_offset = 0;
|
||||||
|
u8 *buffer;
|
||||||
|
|
||||||
|
// Determine the range of sectors required for this write
|
||||||
|
if (offset > 0) {
|
||||||
|
sec_start += floor(offset / fd->sectorSize);
|
||||||
|
buffer_offset = offset % fd->sectorSize;
|
||||||
|
}
|
||||||
|
if (count > fd->sectorSize) {
|
||||||
|
sec_count = ceil(count / fd->sectorSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
// If this write happens to be on the sector boundaries then do the write straight to disc
|
||||||
|
if((offset % fd->sectorSize == 0) && (count % fd->sectorSize == 0)) {
|
||||||
|
|
||||||
|
// Write to the device
|
||||||
|
ntfs_log_trace("direct write to sector %d (%d sector(s) long)\n", sec_start, sec_count);
|
||||||
|
if (!ntfs_device_gekko_io_writesectors(dev, sec_start, sec_count, buf)) {
|
||||||
|
ntfs_log_perror("direct write failure @ sector %d (%d sector(s) long)\n", sec_start, sec_count);
|
||||||
|
errno = EIO;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Else write from a buffer aligned to the sector boundaries
|
||||||
|
} else {
|
||||||
|
|
||||||
|
// Allocate a buffer to hold the write data
|
||||||
|
buffer = (u8*)ntfs_alloc(sec_count * fd->sectorSize);
|
||||||
|
if (!buffer) {
|
||||||
|
errno = ENOMEM;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read the first and last sectors of the buffer from disc (if required)
|
||||||
|
// NOTE: This is done because the data does not line up with the sector boundaries,
|
||||||
|
// we just read in the buffer edges where the data overlaps with the rest of the disc
|
||||||
|
if((offset % fd->sectorSize == 0)) {
|
||||||
|
if (!ntfs_device_gekko_io_readsectors(dev, sec_start, 1, buffer)) {
|
||||||
|
ntfs_log_perror("read failure @ sector %d\n", sec_start);
|
||||||
|
ntfs_free(buffer);
|
||||||
|
errno = EIO;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if((count % fd->sectorSize == 0)) {
|
||||||
|
if (!ntfs_device_gekko_io_readsectors(dev, sec_start + sec_count, 1, buffer + ((sec_count - 1) * fd->sectorSize))) {
|
||||||
|
ntfs_log_perror("read failure @ sector %d\n", sec_start + sec_count);
|
||||||
|
ntfs_free(buffer);
|
||||||
|
errno = EIO;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Copy the data into the write buffer
|
||||||
|
memcpy(buffer + buffer_offset, buf, count);
|
||||||
|
|
||||||
|
// Write to the device
|
||||||
|
ntfs_log_trace("buffered write to sector %d (%d sector(s) long)\n", sec_start, sec_count);
|
||||||
|
if (!ntfs_device_gekko_io_writesectors(dev, sec_start, sec_count, buffer)) {
|
||||||
|
ntfs_log_perror("buffered write failure @ sector %d\n", sec_start);
|
||||||
|
ntfs_free(buffer);
|
||||||
|
errno = EIO;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Free the buffer
|
||||||
|
ntfs_free(buffer);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mark the device as dirty (if we actually wrote anything)
|
||||||
|
if (count)
|
||||||
|
NDevSetDirty(dev);
|
||||||
|
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool ntfs_device_gekko_io_readsectors(struct ntfs_device *dev, sec_t sector, sec_t numSectors, void* buffer)
|
||||||
|
{
|
||||||
|
// Get the device driver descriptor
|
||||||
|
gekko_fd *fd = DEV_FD(dev);
|
||||||
|
if (!fd) {
|
||||||
|
errno = EBADF;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read the sectors from disc (or cache, if enabled)
|
||||||
|
if (fd->cache)
|
||||||
|
return _NTFS_cache_readSectors(fd->cache, sector, numSectors, buffer);
|
||||||
|
else
|
||||||
|
return fd->interface->readSectors(sector, numSectors, buffer);
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool ntfs_device_gekko_io_writesectors(struct ntfs_device *dev, sec_t sector, sec_t numSectors, const void* buffer)
|
||||||
|
{
|
||||||
|
// Get the device driver descriptor
|
||||||
|
gekko_fd *fd = DEV_FD(dev);
|
||||||
|
if (!fd) {
|
||||||
|
errno = EBADF;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write the sectors to disc (or cache, if enabled)
|
||||||
|
if (fd->cache)
|
||||||
|
return _NTFS_cache_writeSectors(fd->cache, sector, numSectors, buffer);
|
||||||
|
else
|
||||||
|
return fd->interface->writeSectors(sector, numSectors, buffer);
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
static int ntfs_device_gekko_io_sync(struct ntfs_device *dev)
|
||||||
|
{
|
||||||
|
gekko_fd *fd = DEV_FD(dev);
|
||||||
|
ntfs_log_trace("dev %p\n", dev);
|
||||||
|
|
||||||
|
// Check that the device can be written to
|
||||||
|
if (NDevReadOnly(dev)) {
|
||||||
|
errno = EROFS;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mark the device as clean
|
||||||
|
NDevClearDirty(dev);
|
||||||
|
|
||||||
|
// Flush any sectors in the disc cache (if required)
|
||||||
|
if (fd->cache) {
|
||||||
|
if (!_NTFS_cache_flush(fd->cache)) {
|
||||||
|
errno = EIO;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
static int ntfs_device_gekko_io_stat(struct ntfs_device *dev, struct stat *buf)
|
||||||
|
{
|
||||||
|
ntfs_log_trace("dev %p, buf %p\n", dev, buf);
|
||||||
|
|
||||||
|
// Get the device driver descriptor
|
||||||
|
gekko_fd *fd = DEV_FD(dev);
|
||||||
|
if (!fd) {
|
||||||
|
errno = EBADF;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Short circuit cases were we don't actually have to do anything
|
||||||
|
if (!buf)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
// Build the device mode
|
||||||
|
mode_t mode = (S_IFBLK) |
|
||||||
|
(S_IRUSR | S_IRGRP | S_IROTH) |
|
||||||
|
((!NDevReadOnly(dev)) ? (S_IWUSR | S_IWGRP | S_IWOTH) : 0);
|
||||||
|
|
||||||
|
// Zero out the stat buffer
|
||||||
|
memset(buf, 0, sizeof(struct stat));
|
||||||
|
|
||||||
|
// Build the device stats
|
||||||
|
buf->st_dev = fd->interface->ioType;
|
||||||
|
buf->st_ino = fd->ino;
|
||||||
|
buf->st_mode = mode;
|
||||||
|
buf->st_rdev = fd->interface->ioType;
|
||||||
|
buf->st_blksize = fd->sectorSize;
|
||||||
|
buf->st_blocks = fd->sectorCount;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
static int ntfs_device_gekko_io_ioctl(struct ntfs_device *dev, int request, void *argp)
|
||||||
|
{
|
||||||
|
ntfs_log_trace("dev %p, request %i, argp %p\n", dev, request, argp);
|
||||||
|
|
||||||
|
// Get the device driver descriptor
|
||||||
|
gekko_fd *fd = DEV_FD(dev);
|
||||||
|
if (!fd) {
|
||||||
|
errno = EBADF;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Figure out which i/o control was requested
|
||||||
|
switch (request) {
|
||||||
|
|
||||||
|
// Get block device size (sectors)
|
||||||
|
#if defined(BLKGETSIZE)
|
||||||
|
case BLKGETSIZE: {
|
||||||
|
*(u32*)argp = fd->sectorCount;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Get block device size (bytes)
|
||||||
|
#if defined(BLKGETSIZE64)
|
||||||
|
case BLKGETSIZE64: {
|
||||||
|
*(u64*)argp = (fd->sectorCount * fd->sectorSize);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Get hard drive geometry
|
||||||
|
#if defined(HDIO_GETGEO)
|
||||||
|
case HDIO_GETGEO: {
|
||||||
|
struct hd_geometry *geo = (struct hd_geometry*)argp;
|
||||||
|
geo->sectors = 0;
|
||||||
|
geo->heads = 0;
|
||||||
|
geo->cylinders = 0;
|
||||||
|
geo->start = fd->hiddenSectors;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Get block device sector size (bytes)
|
||||||
|
#if defined(BLKSSZGET)
|
||||||
|
case BLKSSZGET: {
|
||||||
|
*(int*)argp = fd->sectorSize;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Set block device block size (bytes)
|
||||||
|
#if defined(BLKBSZSET)
|
||||||
|
case BLKBSZSET: {
|
||||||
|
int sectorSize = *(int*)argp;
|
||||||
|
if (sectorSize != BYTES_PER_SECTOR) {
|
||||||
|
ntfs_log_perror("Attempt to set sector size to an unsupported value (%i), ignored\n", sectorSize);
|
||||||
|
errno = EOPNOTSUPP;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
fd->sectorSize = sectorSize;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Unimplemented ioctrl
|
||||||
|
default: {
|
||||||
|
ntfs_log_perror("Unimplemented ioctrl %i\n", request);
|
||||||
|
errno = EOPNOTSUPP;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Device operations for working with gekko style devices and files.
|
||||||
|
*/
|
||||||
|
struct ntfs_device_operations ntfs_device_gekko_io_ops = {
|
||||||
|
.open = ntfs_device_gekko_io_open,
|
||||||
|
.close = ntfs_device_gekko_io_close,
|
||||||
|
.seek = ntfs_device_gekko_io_seek,
|
||||||
|
.read = ntfs_device_gekko_io_read,
|
||||||
|
.write = ntfs_device_gekko_io_write,
|
||||||
|
.pread = ntfs_device_gekko_io_pread,
|
||||||
|
.pwrite = ntfs_device_gekko_io_pwrite,
|
||||||
|
.sync = ntfs_device_gekko_io_sync,
|
||||||
|
.stat = ntfs_device_gekko_io_stat,
|
||||||
|
.ioctl = ntfs_device_gekko_io_ioctl,
|
||||||
|
};
|
58
source/libntfs/gekko_io.h
Normal file
58
source/libntfs/gekko_io.h
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
/*
|
||||||
|
* gekko_io.h - Platform specifics for device io.
|
||||||
|
*
|
||||||
|
* Copyright (c) 2009 Rhys "Shareese" Koedijk
|
||||||
|
*
|
||||||
|
* This program/include file 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/include file 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; if not, write to the Free Software Foundation,
|
||||||
|
* Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _GEKKO_IO_H
|
||||||
|
#define _GEKKO_IO_H
|
||||||
|
|
||||||
|
#ifdef HAVE_CONFIG_H
|
||||||
|
#include "config.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "types.h"
|
||||||
|
#include "cache.h"
|
||||||
|
#include <gccore.h>
|
||||||
|
#include <ogc/disc_io.h>
|
||||||
|
|
||||||
|
#define BYTES_PER_SECTOR 512 /* Forced by gekko disc i/o */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* gekko_fd - Gekko device driver descriptor
|
||||||
|
*/
|
||||||
|
typedef struct _gekko_fd {
|
||||||
|
const DISC_INTERFACE* interface; /* Device disc interface */
|
||||||
|
sec_t startSector; /* LBA of partition start */
|
||||||
|
sec_t hiddenSectors; /* LBA offset to true partition start (as described by boot sector) */
|
||||||
|
u16 sectorSize; /* Device sector size (in bytes) */
|
||||||
|
u64 sectorCount; /* Total number of sectors in partition */
|
||||||
|
u64 pos; /* Current position within the partition (in bytes) */
|
||||||
|
u64 len; /* Total length of partition (in bytes) */
|
||||||
|
ino_t ino; /* Device identifier */
|
||||||
|
NTFS_CACHE *cache; /* Cache */
|
||||||
|
u32 cachePageCount; /* The number of pages in the cache */
|
||||||
|
u32 cachePageSize; /* The number of sectors per cache page */
|
||||||
|
} gekko_fd;
|
||||||
|
|
||||||
|
/* Forward declarations */
|
||||||
|
struct ntfs_device_operations;
|
||||||
|
|
||||||
|
/* Gekko device driver i/o operations */
|
||||||
|
extern struct ntfs_device_operations ntfs_device_gekko_io_ops;
|
||||||
|
|
||||||
|
#endif /* _GEKKO_IO_H */
|
2058
source/libntfs/index.c
Normal file
2058
source/libntfs/index.c
Normal file
File diff suppressed because it is too large
Load Diff
164
source/libntfs/index.h
Normal file
164
source/libntfs/index.h
Normal file
@ -0,0 +1,164 @@
|
|||||||
|
/*
|
||||||
|
* index.h - Defines for NTFS index handling. Originated from the Linux-NTFS project.
|
||||||
|
*
|
||||||
|
* Copyright (c) 2004 Anton Altaparmakov
|
||||||
|
* Copyright (c) 2004-2005 Richard Russon
|
||||||
|
* Copyright (c) 2005 Yura Pakhuchiy
|
||||||
|
* Copyright (c) 2006-2008 Szabolcs Szakacsits
|
||||||
|
*
|
||||||
|
* This program/include file 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/include file 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
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _NTFS_INDEX_H
|
||||||
|
#define _NTFS_INDEX_H
|
||||||
|
|
||||||
|
/* Convenience macros to test the versions of gcc.
|
||||||
|
* Use them like this:
|
||||||
|
* #if __GNUC_PREREQ (2,8)
|
||||||
|
* ... code requiring gcc 2.8 or later ...
|
||||||
|
* #endif
|
||||||
|
* Note - they won't work for gcc1 or glibc1, since the _MINOR macros
|
||||||
|
* were not defined then.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef __GNUC_PREREQ
|
||||||
|
# if defined __GNUC__ && defined __GNUC_MINOR__
|
||||||
|
# define __GNUC_PREREQ(maj, min) \
|
||||||
|
((__GNUC__ << 16) + __GNUC_MINOR__ >= ((maj) << 16) + (min))
|
||||||
|
# else
|
||||||
|
# define __GNUC_PREREQ(maj, min) 0
|
||||||
|
# endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* allows us to warn about unused results of certain function calls */
|
||||||
|
#ifndef __attribute_warn_unused_result__
|
||||||
|
# if __GNUC_PREREQ (3,4)
|
||||||
|
# define __attribute_warn_unused_result__ \
|
||||||
|
__attribute__ ((__warn_unused_result__))
|
||||||
|
# else
|
||||||
|
# define __attribute_warn_unused_result__ /* empty */
|
||||||
|
# endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "attrib.h"
|
||||||
|
#include "types.h"
|
||||||
|
#include "layout.h"
|
||||||
|
#include "inode.h"
|
||||||
|
#include "mft.h"
|
||||||
|
|
||||||
|
#define VCN_INDEX_ROOT_PARENT ((VCN)-2)
|
||||||
|
|
||||||
|
#define MAX_PARENT_VCN 32
|
||||||
|
|
||||||
|
/**
|
||||||
|
* struct ntfs_index_context -
|
||||||
|
* @ni: inode containing the @entry described by this context
|
||||||
|
* @name: name of the index described by this context
|
||||||
|
* @name_len: length of the index name
|
||||||
|
* @entry: index entry (points into @ir or @ia)
|
||||||
|
* @data: index entry data (points into @entry)
|
||||||
|
* @data_len: length in bytes of @data
|
||||||
|
* @is_in_root: TRUE if @entry is in @ir or FALSE if it is in @ia
|
||||||
|
* @ir: index root if @is_in_root or NULL otherwise
|
||||||
|
* @actx: attribute search context if in root or NULL otherwise
|
||||||
|
* @ia: index block if @is_in_root is FALSE or NULL otherwise
|
||||||
|
* @ia_na: opened INDEX_ALLOCATION attribute
|
||||||
|
* @parent_pos: parent entries' positions in the index block
|
||||||
|
* @parent_vcn: entry's parent node or VCN_INDEX_ROOT_PARENT
|
||||||
|
* @new_vcn: new VCN if we need to create a new index block
|
||||||
|
* @median: move to the parent if splitting index blocks
|
||||||
|
* @ib_dirty: TRUE if index block was changed
|
||||||
|
* @block_size: index block size
|
||||||
|
* @vcn_size_bits: VCN size bits for this index block
|
||||||
|
*
|
||||||
|
* @ni is the inode this context belongs to.
|
||||||
|
*
|
||||||
|
* @entry is the index entry described by this context. @data and @data_len
|
||||||
|
* are the index entry data and its length in bytes, respectively. @data
|
||||||
|
* simply points into @entry. This is probably what the user is interested in.
|
||||||
|
*
|
||||||
|
* If @is_in_root is TRUE, @entry is in the index root attribute @ir described
|
||||||
|
* by the attribute search context @actx and inode @ni. @ia and
|
||||||
|
* @ib_dirty are undefined in this case.
|
||||||
|
*
|
||||||
|
* If @is_in_root is FALSE, @entry is in the index allocation attribute and @ia
|
||||||
|
* point to the index allocation block and VCN where it's placed,
|
||||||
|
* respectively. @ir and @actx are NULL in this case. @ia_na is opened
|
||||||
|
* INDEX_ALLOCATION attribute. @ib_dirty is TRUE if index block was changed and
|
||||||
|
* FALSE otherwise.
|
||||||
|
*
|
||||||
|
* To obtain a context call ntfs_index_ctx_get().
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
typedef struct {
|
||||||
|
ntfs_inode *ni;
|
||||||
|
ntfschar *name;
|
||||||
|
u32 name_len;
|
||||||
|
INDEX_ENTRY *entry;
|
||||||
|
void *data;
|
||||||
|
u16 data_len;
|
||||||
|
COLLATION_RULES cr;
|
||||||
|
BOOL is_in_root;
|
||||||
|
INDEX_ROOT *ir;
|
||||||
|
ntfs_attr_search_ctx *actx;
|
||||||
|
INDEX_BLOCK *ib;
|
||||||
|
ntfs_attr *ia_na;
|
||||||
|
int parent_pos[MAX_PARENT_VCN]; /* parent entries' positions */
|
||||||
|
VCN parent_vcn[MAX_PARENT_VCN]; /* entry's parent nodes */
|
||||||
|
int pindex; /* maximum it's the number of the parent nodes */
|
||||||
|
BOOL ib_dirty;
|
||||||
|
u32 block_size;
|
||||||
|
u8 vcn_size_bits;
|
||||||
|
} ntfs_index_context;
|
||||||
|
|
||||||
|
extern ntfs_index_context *ntfs_index_ctx_get(ntfs_inode *ni,
|
||||||
|
ntfschar *name, u32 name_len);
|
||||||
|
extern void ntfs_index_ctx_put(ntfs_index_context *ictx);
|
||||||
|
extern void ntfs_index_ctx_reinit(ntfs_index_context *ictx);
|
||||||
|
|
||||||
|
extern int ntfs_index_lookup(const void *key, const int key_len,
|
||||||
|
ntfs_index_context *ictx) __attribute_warn_unused_result__;
|
||||||
|
|
||||||
|
extern INDEX_ENTRY *ntfs_index_next(INDEX_ENTRY *ie,
|
||||||
|
ntfs_index_context *ictx);
|
||||||
|
|
||||||
|
extern int ntfs_index_add_filename(ntfs_inode *ni, FILE_NAME_ATTR *fn,
|
||||||
|
MFT_REF mref);
|
||||||
|
extern int ntfs_index_remove(ntfs_inode *dir_ni, ntfs_inode *ni,
|
||||||
|
const void *key, const int keylen);
|
||||||
|
|
||||||
|
extern INDEX_ROOT *ntfs_index_root_get(ntfs_inode *ni, ATTR_RECORD *attr);
|
||||||
|
|
||||||
|
extern VCN ntfs_ie_get_vcn(INDEX_ENTRY *ie);
|
||||||
|
|
||||||
|
extern void ntfs_index_entry_mark_dirty(ntfs_index_context *ictx);
|
||||||
|
|
||||||
|
extern char *ntfs_ie_filename_get(INDEX_ENTRY *ie);
|
||||||
|
extern void ntfs_ie_filename_dump(INDEX_ENTRY *ie);
|
||||||
|
extern void ntfs_ih_filename_dump(INDEX_HEADER *ih);
|
||||||
|
|
||||||
|
/* the following was added by JPA for use in security.c */
|
||||||
|
extern int ntfs_ie_add(ntfs_index_context *icx, INDEX_ENTRY *ie);
|
||||||
|
extern int ntfs_index_rm(ntfs_index_context *icx);
|
||||||
|
|
||||||
|
#endif /* _NTFS_INDEX_H */
|
||||||
|
|
1346
source/libntfs/inode.c
Normal file
1346
source/libntfs/inode.c
Normal file
File diff suppressed because it is too large
Load Diff
203
source/libntfs/inode.h
Normal file
203
source/libntfs/inode.h
Normal file
@ -0,0 +1,203 @@
|
|||||||
|
/*
|
||||||
|
* inode.h - Defines for NTFS inode handling. Originated from the Linux-NTFS project.
|
||||||
|
*
|
||||||
|
* Copyright (c) 2001-2004 Anton Altaparmakov
|
||||||
|
* Copyright (c) 2004-2007 Yura Pakhuchiy
|
||||||
|
* Copyright (c) 2004-2005 Richard Russon
|
||||||
|
* Copyright (c) 2006-2008 Szabolcs Szakacsits
|
||||||
|
*
|
||||||
|
* This program/include file 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/include file 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
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _NTFS_INODE_H
|
||||||
|
#define _NTFS_INODE_H
|
||||||
|
|
||||||
|
/* Forward declaration */
|
||||||
|
typedef struct _ntfs_inode ntfs_inode;
|
||||||
|
|
||||||
|
#include "types.h"
|
||||||
|
#include "layout.h"
|
||||||
|
#include "support.h"
|
||||||
|
#include "volume.h"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* enum ntfs_inode_state_bits -
|
||||||
|
*
|
||||||
|
* Defined bits for the state field in the ntfs_inode structure.
|
||||||
|
* (f) = files only, (d) = directories only
|
||||||
|
*/
|
||||||
|
typedef enum {
|
||||||
|
NI_Dirty, /* 1: Mft record needs to be written to disk. */
|
||||||
|
|
||||||
|
/* The NI_AttrList* tests only make sense for base inodes. */
|
||||||
|
NI_AttrList, /* 1: Mft record contains an attribute list. */
|
||||||
|
NI_AttrListDirty, /* 1: Attribute list needs to be written to the
|
||||||
|
mft record and then to disk. */
|
||||||
|
NI_FileNameDirty, /* 1: FILE_NAME attributes need to be updated
|
||||||
|
in the index. */
|
||||||
|
NI_v3_Extensions, /* 1: JPA v3.x extensions present. */
|
||||||
|
NI_TimesDirty, /* 1: Times need to be updated */
|
||||||
|
} ntfs_inode_state_bits;
|
||||||
|
|
||||||
|
#define test_nino_flag(ni, flag) test_bit(NI_##flag, (ni)->state)
|
||||||
|
#define set_nino_flag(ni, flag) set_bit(NI_##flag, (ni)->state)
|
||||||
|
#define clear_nino_flag(ni, flag) clear_bit(NI_##flag, (ni)->state)
|
||||||
|
|
||||||
|
#define test_and_set_nino_flag(ni, flag) \
|
||||||
|
test_and_set_bit(NI_##flag, (ni)->state)
|
||||||
|
#define test_and_clear_nino_flag(ni, flag) \
|
||||||
|
test_and_clear_bit(NI_##flag, (ni)->state)
|
||||||
|
|
||||||
|
#define NInoDirty(ni) test_nino_flag(ni, Dirty)
|
||||||
|
#define NInoSetDirty(ni) set_nino_flag(ni, Dirty)
|
||||||
|
#define NInoClearDirty(ni) clear_nino_flag(ni, Dirty)
|
||||||
|
#define NInoTestAndSetDirty(ni) test_and_set_nino_flag(ni, Dirty)
|
||||||
|
#define NInoTestAndClearDirty(ni) test_and_clear_nino_flag(ni, Dirty)
|
||||||
|
|
||||||
|
#define NInoAttrList(ni) test_nino_flag(ni, AttrList)
|
||||||
|
#define NInoSetAttrList(ni) set_nino_flag(ni, AttrList)
|
||||||
|
#define NInoClearAttrList(ni) clear_nino_flag(ni, AttrList)
|
||||||
|
|
||||||
|
|
||||||
|
#define test_nino_al_flag(ni, flag) test_nino_flag(ni, AttrList##flag)
|
||||||
|
#define set_nino_al_flag(ni, flag) set_nino_flag(ni, AttrList##flag)
|
||||||
|
#define clear_nino_al_flag(ni, flag) clear_nino_flag(ni, AttrList##flag)
|
||||||
|
|
||||||
|
#define test_and_set_nino_al_flag(ni, flag) \
|
||||||
|
test_and_set_nino_flag(ni, AttrList##flag)
|
||||||
|
#define test_and_clear_nino_al_flag(ni, flag) \
|
||||||
|
test_and_clear_nino_flag(ni, AttrList##flag)
|
||||||
|
|
||||||
|
#define NInoAttrListDirty(ni) test_nino_al_flag(ni, Dirty)
|
||||||
|
#define NInoAttrListSetDirty(ni) set_nino_al_flag(ni, Dirty)
|
||||||
|
#define NInoAttrListClearDirty(ni) clear_nino_al_flag(ni, Dirty)
|
||||||
|
#define NInoAttrListTestAndSetDirty(ni) test_and_set_nino_al_flag(ni, Dirty)
|
||||||
|
#define NInoAttrListTestAndClearDirty(ni) test_and_clear_nino_al_flag(ni, Dirty)
|
||||||
|
|
||||||
|
#define NInoFileNameDirty(ni) test_nino_flag(ni, FileNameDirty)
|
||||||
|
#define NInoFileNameSetDirty(ni) set_nino_flag(ni, FileNameDirty)
|
||||||
|
#define NInoFileNameClearDirty(ni) clear_nino_flag(ni, FileNameDirty)
|
||||||
|
#define NInoFileNameTestAndSetDirty(ni) \
|
||||||
|
test_and_set_nino_flag(ni, FileNameDirty)
|
||||||
|
#define NInoFileNameTestAndClearDirty(ni) \
|
||||||
|
test_and_clear_nino_flag(ni, FileNameDirty)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* struct _ntfs_inode - The NTFS in-memory inode structure.
|
||||||
|
*
|
||||||
|
* It is just used as an extension to the fields already provided in the VFS
|
||||||
|
* inode.
|
||||||
|
*/
|
||||||
|
struct _ntfs_inode {
|
||||||
|
u64 mft_no; /* Inode / mft record number. */
|
||||||
|
MFT_RECORD *mrec; /* The actual mft record of the inode. */
|
||||||
|
ntfs_volume *vol; /* Pointer to the ntfs volume of this inode. */
|
||||||
|
unsigned long state; /* NTFS specific flags describing this inode.
|
||||||
|
See ntfs_inode_state_bits above. */
|
||||||
|
FILE_ATTR_FLAGS flags; /* Flags describing the file.
|
||||||
|
(Copy from STANDARD_INFORMATION) */
|
||||||
|
/*
|
||||||
|
* Attribute list support (for use by the attribute lookup functions).
|
||||||
|
* Setup during ntfs_open_inode() for all inodes with attribute lists.
|
||||||
|
* Only valid if NI_AttrList is set in state.
|
||||||
|
*/
|
||||||
|
u32 attr_list_size; /* Length of attribute list value in bytes. */
|
||||||
|
u8 *attr_list; /* Attribute list value itself. */
|
||||||
|
/* Below fields are always valid. */
|
||||||
|
s32 nr_extents; /* For a base mft record, the number of
|
||||||
|
attached extent inodes (0 if none), for
|
||||||
|
extent records this is -1. */
|
||||||
|
union { /* This union is only used if nr_extents != 0. */
|
||||||
|
ntfs_inode **extent_nis;/* For nr_extents > 0, array of the
|
||||||
|
ntfs inodes of the extent mft
|
||||||
|
records belonging to this base
|
||||||
|
inode which have been loaded. */
|
||||||
|
ntfs_inode *base_ni; /* For nr_extents == -1, the ntfs
|
||||||
|
inode of the base mft record. */
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Below fields are valid only for base inode. */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* These two fields are used to sync filename index and guaranteed to be
|
||||||
|
* correct, however value in index itself maybe wrong (windows itself
|
||||||
|
* do not update them properly).
|
||||||
|
*/
|
||||||
|
s64 data_size; /* Data size of unnamed DATA attribute. */
|
||||||
|
s64 allocated_size; /* Allocated size stored in the filename
|
||||||
|
index. (NOTE: Equal to allocated size of
|
||||||
|
the unnamed data attribute for normal or
|
||||||
|
encrypted files and to compressed size
|
||||||
|
of the unnamed data attribute for sparse or
|
||||||
|
compressed files.) */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* These four fields are copy of relevant fields from
|
||||||
|
* STANDARD_INFORMATION attribute and used to sync it and FILE_NAME
|
||||||
|
* attribute in the index.
|
||||||
|
*/
|
||||||
|
time_t creation_time;
|
||||||
|
time_t last_data_change_time;
|
||||||
|
time_t last_mft_change_time;
|
||||||
|
time_t last_access_time;
|
||||||
|
/* NTFS 3.x extensions added by JPA */
|
||||||
|
/* only if NI_v3_Extensions is set in state */
|
||||||
|
le32 owner_id;
|
||||||
|
le32 security_id;
|
||||||
|
le64 quota_charged;
|
||||||
|
le64 usn;
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
NTFS_UPDATE_ATIME = 1 << 0,
|
||||||
|
NTFS_UPDATE_MTIME = 1 << 1,
|
||||||
|
NTFS_UPDATE_CTIME = 1 << 2,
|
||||||
|
} ntfs_time_update_flags;
|
||||||
|
|
||||||
|
#define NTFS_UPDATE_MCTIME (NTFS_UPDATE_MTIME | NTFS_UPDATE_CTIME)
|
||||||
|
#define NTFS_UPDATE_AMCTIME (NTFS_UPDATE_ATIME | NTFS_UPDATE_MCTIME)
|
||||||
|
|
||||||
|
extern ntfs_inode *ntfs_inode_base(ntfs_inode *ni);
|
||||||
|
|
||||||
|
extern ntfs_inode *ntfs_inode_allocate(ntfs_volume *vol);
|
||||||
|
|
||||||
|
extern ntfs_inode *ntfs_inode_open(ntfs_volume *vol, const MFT_REF mref);
|
||||||
|
|
||||||
|
extern int ntfs_inode_close(ntfs_inode *ni);
|
||||||
|
|
||||||
|
extern ntfs_inode *ntfs_extent_inode_open(ntfs_inode *base_ni,
|
||||||
|
const MFT_REF mref);
|
||||||
|
|
||||||
|
extern int ntfs_inode_attach_all_extents(ntfs_inode *ni);
|
||||||
|
|
||||||
|
extern void ntfs_inode_mark_dirty(ntfs_inode *ni);
|
||||||
|
|
||||||
|
extern void ntfs_inode_update_times(ntfs_inode *ni, ntfs_time_update_flags mask);
|
||||||
|
|
||||||
|
extern int ntfs_inode_sync(ntfs_inode *ni);
|
||||||
|
|
||||||
|
extern int ntfs_inode_add_attrlist(ntfs_inode *ni);
|
||||||
|
|
||||||
|
extern int ntfs_inode_free_space(ntfs_inode *ni, int size);
|
||||||
|
|
||||||
|
extern int ntfs_inode_badclus_bad(u64 mft_no, ATTR_RECORD *a);
|
||||||
|
|
||||||
|
extern int ntfs_inode_get_times(const char *path, char *value,
|
||||||
|
size_t size, ntfs_inode *ni);
|
||||||
|
extern int ntfs_inode_set_times(const char *path, const char *value,
|
||||||
|
size_t size, int flags, ntfs_inode *ni);
|
||||||
|
|
||||||
|
#endif /* defined _NTFS_INODE_H */
|
2661
source/libntfs/layout.h
Normal file
2661
source/libntfs/layout.h
Normal file
File diff suppressed because it is too large
Load Diff
735
source/libntfs/lcnalloc.c
Normal file
735
source/libntfs/lcnalloc.c
Normal file
@ -0,0 +1,735 @@
|
|||||||
|
/**
|
||||||
|
* lcnalloc.c - Cluster (de)allocation code. Originated from the Linux-NTFS project.
|
||||||
|
*
|
||||||
|
* Copyright (c) 2002-2004 Anton Altaparmakov
|
||||||
|
* Copyright (c) 2004 Yura Pakhuchiy
|
||||||
|
* Copyright (c) 2004-2008 Szabolcs Szakacsits
|
||||||
|
* Copyright (c) 2008-2009 Jean-Pierre Andre
|
||||||
|
*
|
||||||
|
* This program/include file 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/include file 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
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifdef HAVE_CONFIG_H
|
||||||
|
#include "config.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef HAVE_STDLIB_H
|
||||||
|
#include <stdlib.h>
|
||||||
|
#endif
|
||||||
|
#ifdef HAVE_STDIO_H
|
||||||
|
#include <stdio.h>
|
||||||
|
#endif
|
||||||
|
#ifdef HAVE_ERRNO_H
|
||||||
|
#include <errno.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "types.h"
|
||||||
|
#include "attrib.h"
|
||||||
|
#include "bitmap.h"
|
||||||
|
#include "debug.h"
|
||||||
|
#include "runlist.h"
|
||||||
|
#include "volume.h"
|
||||||
|
#include "lcnalloc.h"
|
||||||
|
#include "logging.h"
|
||||||
|
#include "misc.h"
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Plenty possibilities for big optimizations all over in the cluster
|
||||||
|
* allocation, however at the moment the dominant bottleneck (~ 90%) is
|
||||||
|
* the update of the mapping pairs which converges to the cubic Faulhaber's
|
||||||
|
* formula as the function of the number of extents (fragments, runs).
|
||||||
|
*/
|
||||||
|
#define NTFS_LCNALLOC_BSIZE 4096
|
||||||
|
#define NTFS_LCNALLOC_SKIP NTFS_LCNALLOC_BSIZE
|
||||||
|
|
||||||
|
enum {
|
||||||
|
ZONE_MFT = 1,
|
||||||
|
ZONE_DATA1 = 2,
|
||||||
|
ZONE_DATA2 = 4
|
||||||
|
} ;
|
||||||
|
|
||||||
|
static void ntfs_cluster_set_zone_pos(LCN start, LCN end, LCN *pos, LCN tc)
|
||||||
|
{
|
||||||
|
ntfs_log_trace("pos: %lld tc: %lld\n", (long long)*pos, (long long)tc);
|
||||||
|
|
||||||
|
if (tc >= end)
|
||||||
|
*pos = start;
|
||||||
|
else if (tc >= start)
|
||||||
|
*pos = tc;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void ntfs_cluster_update_zone_pos(ntfs_volume *vol, u8 zone, LCN tc)
|
||||||
|
{
|
||||||
|
ntfs_log_trace("tc = %lld, zone = %d\n", (long long)tc, zone);
|
||||||
|
|
||||||
|
if (zone == ZONE_MFT)
|
||||||
|
ntfs_cluster_set_zone_pos(vol->mft_lcn, vol->mft_zone_end,
|
||||||
|
&vol->mft_zone_pos, tc);
|
||||||
|
else if (zone == ZONE_DATA1)
|
||||||
|
ntfs_cluster_set_zone_pos(vol->mft_zone_end, vol->nr_clusters,
|
||||||
|
&vol->data1_zone_pos, tc);
|
||||||
|
else /* zone == ZONE_DATA2 */
|
||||||
|
ntfs_cluster_set_zone_pos(0, vol->mft_zone_start,
|
||||||
|
&vol->data2_zone_pos, tc);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Unmark full zones when a cluster has been freed in a full zone
|
||||||
|
*
|
||||||
|
* Next allocation will reuse the freed cluster
|
||||||
|
*/
|
||||||
|
|
||||||
|
static void update_full_status(ntfs_volume *vol, LCN lcn)
|
||||||
|
{
|
||||||
|
if (lcn >= vol->mft_zone_end) {
|
||||||
|
if (vol->full_zones & ZONE_DATA1) {
|
||||||
|
ntfs_cluster_update_zone_pos(vol, ZONE_DATA1, lcn);
|
||||||
|
vol->full_zones &= ~ZONE_DATA1;
|
||||||
|
}
|
||||||
|
} else
|
||||||
|
if (lcn < vol->mft_zone_start) {
|
||||||
|
if (vol->full_zones & ZONE_DATA2) {
|
||||||
|
ntfs_cluster_update_zone_pos(vol, ZONE_DATA2, lcn);
|
||||||
|
vol->full_zones &= ~ZONE_DATA2;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (vol->full_zones & ZONE_MFT) {
|
||||||
|
ntfs_cluster_update_zone_pos(vol, ZONE_MFT, lcn);
|
||||||
|
vol->full_zones &= ~ZONE_MFT;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static s64 max_empty_bit_range(unsigned char *buf, int size)
|
||||||
|
{
|
||||||
|
int i, j, run = 0;
|
||||||
|
int max_range = 0;
|
||||||
|
s64 start_pos = -1;
|
||||||
|
|
||||||
|
ntfs_log_trace("Entering\n");
|
||||||
|
|
||||||
|
i = 0;
|
||||||
|
while (i < size) {
|
||||||
|
switch (*buf) {
|
||||||
|
case 0 :
|
||||||
|
do {
|
||||||
|
buf++;
|
||||||
|
run += 8;
|
||||||
|
i++;
|
||||||
|
} while ((i < size) && !*buf);
|
||||||
|
break;
|
||||||
|
case 255 :
|
||||||
|
if (run > max_range) {
|
||||||
|
max_range = run;
|
||||||
|
start_pos = (s64)i * 8 - run;
|
||||||
|
}
|
||||||
|
run = 0;
|
||||||
|
do {
|
||||||
|
buf++;
|
||||||
|
i++;
|
||||||
|
} while ((i < size) && (*buf == 255));
|
||||||
|
break;
|
||||||
|
default :
|
||||||
|
for (j = 0; j < 8; j++) {
|
||||||
|
|
||||||
|
int bit = *buf & (1 << j);
|
||||||
|
|
||||||
|
if (bit) {
|
||||||
|
if (run > max_range) {
|
||||||
|
max_range = run;
|
||||||
|
start_pos = (s64)i * 8 + (j - run);
|
||||||
|
}
|
||||||
|
run = 0;
|
||||||
|
} else
|
||||||
|
run++;
|
||||||
|
}
|
||||||
|
i++;
|
||||||
|
buf++;
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (run > max_range)
|
||||||
|
start_pos = (s64)i * 8 - run;
|
||||||
|
|
||||||
|
return start_pos;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int bitmap_writeback(ntfs_volume *vol, s64 pos, s64 size, void *b,
|
||||||
|
u8 *writeback)
|
||||||
|
{
|
||||||
|
s64 written;
|
||||||
|
|
||||||
|
ntfs_log_trace("Entering\n");
|
||||||
|
|
||||||
|
if (!*writeback)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
*writeback = 0;
|
||||||
|
|
||||||
|
written = ntfs_attr_pwrite(vol->lcnbmp_na, pos, size, b);
|
||||||
|
if (written != size) {
|
||||||
|
if (!written)
|
||||||
|
errno = EIO;
|
||||||
|
ntfs_log_perror("Bitmap write error (%lld, %lld)",
|
||||||
|
(long long)pos, (long long)size);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ntfs_cluster_alloc - allocate clusters on an ntfs volume
|
||||||
|
* @vol: mounted ntfs volume on which to allocate the clusters
|
||||||
|
* @start_vcn: vcn to use for the first allocated cluster
|
||||||
|
* @count: number of clusters to allocate
|
||||||
|
* @start_lcn: starting lcn at which to allocate the clusters (or -1 if none)
|
||||||
|
* @zone: zone from which to allocate the clusters
|
||||||
|
*
|
||||||
|
* Allocate @count clusters preferably starting at cluster @start_lcn or at the
|
||||||
|
* current allocator position if @start_lcn is -1, on the mounted ntfs volume
|
||||||
|
* @vol. @zone is either DATA_ZONE for allocation of normal clusters and
|
||||||
|
* MFT_ZONE for allocation of clusters for the master file table, i.e. the
|
||||||
|
* $MFT/$DATA attribute.
|
||||||
|
*
|
||||||
|
* On success return a runlist describing the allocated cluster(s).
|
||||||
|
*
|
||||||
|
* On error return NULL with errno set to the error code.
|
||||||
|
*
|
||||||
|
* Notes on the allocation algorithm
|
||||||
|
* =================================
|
||||||
|
*
|
||||||
|
* There are two data zones. First is the area between the end of the mft zone
|
||||||
|
* and the end of the volume, and second is the area between the start of the
|
||||||
|
* volume and the start of the mft zone. On unmodified/standard NTFS 1.x
|
||||||
|
* volumes, the second data zone doesn't exist due to the mft zone being
|
||||||
|
* expanded to cover the start of the volume in order to reserve space for the
|
||||||
|
* mft bitmap attribute.
|
||||||
|
*
|
||||||
|
* The complexity stems from the need of implementing the mft vs data zoned
|
||||||
|
* approach and from the fact that we have access to the lcn bitmap via up to
|
||||||
|
* NTFS_LCNALLOC_BSIZE bytes at a time, so we need to cope with crossing over
|
||||||
|
* boundaries of two buffers. Further, the fact that the allocator allows for
|
||||||
|
* caller supplied hints as to the location of where allocation should begin
|
||||||
|
* and the fact that the allocator keeps track of where in the data zones the
|
||||||
|
* next natural allocation should occur, contribute to the complexity of the
|
||||||
|
* function. But it should all be worthwhile, because this allocator:
|
||||||
|
* 1) implements MFT zone reservation
|
||||||
|
* 2) causes reduction in fragmentation.
|
||||||
|
* The code is not optimized for speed.
|
||||||
|
*/
|
||||||
|
runlist *ntfs_cluster_alloc(ntfs_volume *vol, VCN start_vcn, s64 count,
|
||||||
|
LCN start_lcn, const NTFS_CLUSTER_ALLOCATION_ZONES zone)
|
||||||
|
{
|
||||||
|
LCN zone_start, zone_end; /* current search range */
|
||||||
|
LCN last_read_pos, lcn;
|
||||||
|
LCN bmp_pos; /* current bit position inside the bitmap */
|
||||||
|
LCN prev_lcn = 0, prev_run_len = 0;
|
||||||
|
s64 clusters, br;
|
||||||
|
runlist *rl = NULL, *trl;
|
||||||
|
u8 *buf, *byte, bit, writeback;
|
||||||
|
u8 pass = 1; /* 1: inside zone; 2: start of zone */
|
||||||
|
u8 search_zone; /* 4: data2 (start) 1: mft (middle) 2: data1 (end) */
|
||||||
|
u8 done_zones = 0;
|
||||||
|
u8 has_guess, used_zone_pos;
|
||||||
|
int err = 0, rlpos, rlsize, buf_size;
|
||||||
|
|
||||||
|
ntfs_log_enter("Entering with count = 0x%llx, start_lcn = 0x%llx, "
|
||||||
|
"zone = %s_ZONE.\n", (long long)count, (long long)
|
||||||
|
start_lcn, zone == MFT_ZONE ? "MFT" : "DATA");
|
||||||
|
|
||||||
|
if (!vol || count < 0 || start_lcn < -1 || !vol->lcnbmp_na ||
|
||||||
|
(s8)zone < FIRST_ZONE || zone > LAST_ZONE) {
|
||||||
|
errno = EINVAL;
|
||||||
|
ntfs_log_perror("%s: vcn: %lld, count: %lld, lcn: %lld",
|
||||||
|
__FUNCTION__, (long long)start_vcn,
|
||||||
|
(long long)count, (long long)start_lcn);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Return empty runlist if @count == 0 */
|
||||||
|
if (!count) {
|
||||||
|
rl = ntfs_malloc(0x1000);
|
||||||
|
if (rl) {
|
||||||
|
rl[0].vcn = start_vcn;
|
||||||
|
rl[0].lcn = LCN_RL_NOT_MAPPED;
|
||||||
|
rl[0].length = 0;
|
||||||
|
}
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
buf = ntfs_malloc(NTFS_LCNALLOC_BSIZE);
|
||||||
|
if (!buf)
|
||||||
|
goto out;
|
||||||
|
/*
|
||||||
|
* If no @start_lcn was requested, use the current zone
|
||||||
|
* position otherwise use the requested @start_lcn.
|
||||||
|
*/
|
||||||
|
has_guess = 1;
|
||||||
|
zone_start = start_lcn;
|
||||||
|
|
||||||
|
if (zone_start < 0) {
|
||||||
|
if (zone == DATA_ZONE)
|
||||||
|
zone_start = vol->data1_zone_pos;
|
||||||
|
else
|
||||||
|
zone_start = vol->mft_zone_pos;
|
||||||
|
has_guess = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
used_zone_pos = has_guess ? 0 : 1;
|
||||||
|
|
||||||
|
if (!zone_start || zone_start == vol->mft_zone_start ||
|
||||||
|
zone_start == vol->mft_zone_end)
|
||||||
|
pass = 2;
|
||||||
|
|
||||||
|
if (zone_start < vol->mft_zone_start) {
|
||||||
|
zone_end = vol->mft_zone_start;
|
||||||
|
search_zone = ZONE_DATA2;
|
||||||
|
} else if (zone_start < vol->mft_zone_end) {
|
||||||
|
zone_end = vol->mft_zone_end;
|
||||||
|
search_zone = ZONE_MFT;
|
||||||
|
} else {
|
||||||
|
zone_end = vol->nr_clusters;
|
||||||
|
search_zone = ZONE_DATA1;
|
||||||
|
}
|
||||||
|
|
||||||
|
bmp_pos = zone_start;
|
||||||
|
|
||||||
|
/* Loop until all clusters are allocated. */
|
||||||
|
clusters = count;
|
||||||
|
rlpos = rlsize = 0;
|
||||||
|
while (1) {
|
||||||
|
/* check whether we have exhausted the current zone */
|
||||||
|
if (search_zone & vol->full_zones)
|
||||||
|
goto zone_pass_done;
|
||||||
|
last_read_pos = bmp_pos >> 3;
|
||||||
|
br = ntfs_attr_pread(vol->lcnbmp_na, last_read_pos,
|
||||||
|
NTFS_LCNALLOC_BSIZE, buf);
|
||||||
|
if (br <= 0) {
|
||||||
|
if (!br)
|
||||||
|
goto zone_pass_done;
|
||||||
|
err = errno;
|
||||||
|
ntfs_log_perror("Reading $BITMAP failed");
|
||||||
|
goto err_ret;
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
* We might have read less than NTFS_LCNALLOC_BSIZE bytes
|
||||||
|
* if we are close to the end of the attribute.
|
||||||
|
*/
|
||||||
|
buf_size = (int)br << 3;
|
||||||
|
lcn = bmp_pos & 7;
|
||||||
|
bmp_pos &= ~7;
|
||||||
|
writeback = 0;
|
||||||
|
|
||||||
|
while (lcn < buf_size) {
|
||||||
|
byte = buf + (lcn >> 3);
|
||||||
|
bit = 1 << (lcn & 7);
|
||||||
|
if (has_guess) {
|
||||||
|
if (*byte & bit) {
|
||||||
|
has_guess = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
lcn = max_empty_bit_range(buf, br);
|
||||||
|
if (lcn < 0)
|
||||||
|
break;
|
||||||
|
has_guess = 1;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* First free bit is at lcn + bmp_pos. */
|
||||||
|
|
||||||
|
/* Reallocate memory if necessary. */
|
||||||
|
if ((rlpos + 2) * (int)sizeof(runlist) >= rlsize) {
|
||||||
|
rlsize += 4096;
|
||||||
|
trl = realloc(rl, rlsize);
|
||||||
|
if (!trl) {
|
||||||
|
err = ENOMEM;
|
||||||
|
ntfs_log_perror("realloc() failed");
|
||||||
|
goto wb_err_ret;
|
||||||
|
}
|
||||||
|
rl = trl;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Allocate the bitmap bit. */
|
||||||
|
*byte |= bit;
|
||||||
|
writeback = 1;
|
||||||
|
if (vol->free_clusters <= 0)
|
||||||
|
ntfs_log_error("Non-positive free clusters "
|
||||||
|
"(%lld)!\n",
|
||||||
|
(long long)vol->free_clusters);
|
||||||
|
else
|
||||||
|
vol->free_clusters--;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Coalesce with previous run if adjacent LCNs.
|
||||||
|
* Otherwise, append a new run.
|
||||||
|
*/
|
||||||
|
if (prev_lcn == lcn + bmp_pos - prev_run_len && rlpos) {
|
||||||
|
ntfs_log_debug("Cluster coalesce: prev_lcn: "
|
||||||
|
"%lld lcn: %lld bmp_pos: %lld "
|
||||||
|
"prev_run_len: %lld\n",
|
||||||
|
(long long)prev_lcn,
|
||||||
|
(long long)lcn, (long long)bmp_pos,
|
||||||
|
(long long)prev_run_len);
|
||||||
|
rl[rlpos - 1].length = ++prev_run_len;
|
||||||
|
} else {
|
||||||
|
if (rlpos)
|
||||||
|
rl[rlpos].vcn = rl[rlpos - 1].vcn +
|
||||||
|
prev_run_len;
|
||||||
|
else {
|
||||||
|
rl[rlpos].vcn = start_vcn;
|
||||||
|
ntfs_log_debug("Start_vcn: %lld\n",
|
||||||
|
(long long)start_vcn);
|
||||||
|
}
|
||||||
|
|
||||||
|
rl[rlpos].lcn = prev_lcn = lcn + bmp_pos;
|
||||||
|
rl[rlpos].length = prev_run_len = 1;
|
||||||
|
rlpos++;
|
||||||
|
}
|
||||||
|
|
||||||
|
ntfs_log_debug("RUN: %-16lld %-16lld %-16lld\n",
|
||||||
|
(long long)rl[rlpos - 1].vcn,
|
||||||
|
(long long)rl[rlpos - 1].lcn,
|
||||||
|
(long long)rl[rlpos - 1].length);
|
||||||
|
/* Done? */
|
||||||
|
if (!--clusters) {
|
||||||
|
if (used_zone_pos)
|
||||||
|
ntfs_cluster_update_zone_pos(vol,
|
||||||
|
search_zone, lcn + bmp_pos + 1 +
|
||||||
|
NTFS_LCNALLOC_SKIP);
|
||||||
|
goto done_ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
lcn++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bitmap_writeback(vol, last_read_pos, br, buf, &writeback)) {
|
||||||
|
err = errno;
|
||||||
|
goto err_ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!used_zone_pos) {
|
||||||
|
|
||||||
|
used_zone_pos = 1;
|
||||||
|
|
||||||
|
if (search_zone == ZONE_MFT)
|
||||||
|
zone_start = vol->mft_zone_pos;
|
||||||
|
else if (search_zone == ZONE_DATA1)
|
||||||
|
zone_start = vol->data1_zone_pos;
|
||||||
|
else
|
||||||
|
zone_start = vol->data2_zone_pos;
|
||||||
|
|
||||||
|
if (!zone_start || zone_start == vol->mft_zone_start ||
|
||||||
|
zone_start == vol->mft_zone_end)
|
||||||
|
pass = 2;
|
||||||
|
bmp_pos = zone_start;
|
||||||
|
} else
|
||||||
|
bmp_pos += buf_size;
|
||||||
|
|
||||||
|
if (bmp_pos < zone_end)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
zone_pass_done:
|
||||||
|
ntfs_log_trace("Finished current zone pass(%i).\n", pass);
|
||||||
|
if (pass == 1) {
|
||||||
|
pass = 2;
|
||||||
|
zone_end = zone_start;
|
||||||
|
|
||||||
|
if (search_zone == ZONE_MFT)
|
||||||
|
zone_start = vol->mft_zone_start;
|
||||||
|
else if (search_zone == ZONE_DATA1)
|
||||||
|
zone_start = vol->mft_zone_end;
|
||||||
|
else
|
||||||
|
zone_start = 0;
|
||||||
|
|
||||||
|
/* Sanity check. */
|
||||||
|
if (zone_end < zone_start)
|
||||||
|
zone_end = zone_start;
|
||||||
|
|
||||||
|
bmp_pos = zone_start;
|
||||||
|
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
/* pass == 2 */
|
||||||
|
done_zones_check:
|
||||||
|
done_zones |= search_zone;
|
||||||
|
vol->full_zones |= search_zone;
|
||||||
|
if (done_zones < (ZONE_MFT + ZONE_DATA1 + ZONE_DATA2)) {
|
||||||
|
ntfs_log_trace("Switching zone.\n");
|
||||||
|
pass = 1;
|
||||||
|
if (rlpos) {
|
||||||
|
LCN tc = tc = rl[rlpos - 1].lcn +
|
||||||
|
rl[rlpos - 1].length + NTFS_LCNALLOC_SKIP;
|
||||||
|
|
||||||
|
if (used_zone_pos)
|
||||||
|
ntfs_cluster_update_zone_pos(vol,
|
||||||
|
search_zone, tc);
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (search_zone) {
|
||||||
|
case ZONE_MFT:
|
||||||
|
ntfs_log_trace("Zone switch: mft -> data1\n");
|
||||||
|
switch_to_data1_zone: search_zone = ZONE_DATA1;
|
||||||
|
zone_start = vol->data1_zone_pos;
|
||||||
|
zone_end = vol->nr_clusters;
|
||||||
|
if (zone_start == vol->mft_zone_end)
|
||||||
|
pass = 2;
|
||||||
|
break;
|
||||||
|
case ZONE_DATA1:
|
||||||
|
ntfs_log_trace("Zone switch: data1 -> data2\n");
|
||||||
|
search_zone = ZONE_DATA2;
|
||||||
|
zone_start = vol->data2_zone_pos;
|
||||||
|
zone_end = vol->mft_zone_start;
|
||||||
|
if (!zone_start)
|
||||||
|
pass = 2;
|
||||||
|
break;
|
||||||
|
case ZONE_DATA2:
|
||||||
|
if (!(done_zones & ZONE_DATA1)) {
|
||||||
|
ntfs_log_trace("data2 -> data1\n");
|
||||||
|
goto switch_to_data1_zone;
|
||||||
|
}
|
||||||
|
ntfs_log_trace("Zone switch: data2 -> mft\n");
|
||||||
|
search_zone = ZONE_MFT;
|
||||||
|
zone_start = vol->mft_zone_pos;
|
||||||
|
zone_end = vol->mft_zone_end;
|
||||||
|
if (zone_start == vol->mft_zone_start)
|
||||||
|
pass = 2;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
bmp_pos = zone_start;
|
||||||
|
|
||||||
|
if (zone_start == zone_end) {
|
||||||
|
ntfs_log_trace("Empty zone, skipped.\n");
|
||||||
|
goto done_zones_check;
|
||||||
|
}
|
||||||
|
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
ntfs_log_trace("All zones are finished, no space on device.\n");
|
||||||
|
err = ENOSPC;
|
||||||
|
goto err_ret;
|
||||||
|
}
|
||||||
|
done_ret:
|
||||||
|
ntfs_log_debug("At done_ret.\n");
|
||||||
|
/* Add runlist terminator element. */
|
||||||
|
rl[rlpos].vcn = rl[rlpos - 1].vcn + rl[rlpos - 1].length;
|
||||||
|
rl[rlpos].lcn = LCN_RL_NOT_MAPPED;
|
||||||
|
rl[rlpos].length = 0;
|
||||||
|
if (bitmap_writeback(vol, last_read_pos, br, buf, &writeback)) {
|
||||||
|
err = errno;
|
||||||
|
goto err_ret;
|
||||||
|
}
|
||||||
|
done_err_ret:
|
||||||
|
free(buf);
|
||||||
|
if (err) {
|
||||||
|
errno = err;
|
||||||
|
ntfs_log_perror("Failed to allocate clusters");
|
||||||
|
rl = NULL;
|
||||||
|
}
|
||||||
|
out:
|
||||||
|
ntfs_log_leave("\n");
|
||||||
|
return rl;
|
||||||
|
|
||||||
|
wb_err_ret:
|
||||||
|
ntfs_log_trace("At wb_err_ret.\n");
|
||||||
|
if (bitmap_writeback(vol, last_read_pos, br, buf, &writeback))
|
||||||
|
err = errno;
|
||||||
|
err_ret:
|
||||||
|
ntfs_log_trace("At err_ret.\n");
|
||||||
|
if (rl) {
|
||||||
|
/* Add runlist terminator element. */
|
||||||
|
rl[rlpos].vcn = rl[rlpos - 1].vcn + rl[rlpos - 1].length;
|
||||||
|
rl[rlpos].lcn = LCN_RL_NOT_MAPPED;
|
||||||
|
rl[rlpos].length = 0;
|
||||||
|
ntfs_debug_runlist_dump(rl);
|
||||||
|
ntfs_cluster_free_from_rl(vol, rl);
|
||||||
|
free(rl);
|
||||||
|
rl = NULL;
|
||||||
|
}
|
||||||
|
goto done_err_ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ntfs_cluster_free_from_rl - free clusters from runlist
|
||||||
|
* @vol: mounted ntfs volume on which to free the clusters
|
||||||
|
* @rl: runlist from which deallocate clusters
|
||||||
|
*
|
||||||
|
* On success return 0 and on error return -1 with errno set to the error code.
|
||||||
|
*/
|
||||||
|
int ntfs_cluster_free_from_rl(ntfs_volume *vol, runlist *rl)
|
||||||
|
{
|
||||||
|
s64 nr_freed = 0;
|
||||||
|
int ret = -1;
|
||||||
|
|
||||||
|
ntfs_log_trace("Entering.\n");
|
||||||
|
|
||||||
|
for (; rl->length; rl++) {
|
||||||
|
|
||||||
|
ntfs_log_trace("Dealloc lcn 0x%llx, len 0x%llx.\n",
|
||||||
|
(long long)rl->lcn, (long long)rl->length);
|
||||||
|
|
||||||
|
if (rl->lcn >= 0) {
|
||||||
|
update_full_status(vol,rl->lcn);
|
||||||
|
if (ntfs_bitmap_clear_run(vol->lcnbmp_na, rl->lcn,
|
||||||
|
rl->length)) {
|
||||||
|
ntfs_log_perror("Cluster deallocation failed "
|
||||||
|
"(%lld, %lld)",
|
||||||
|
(long long)rl->lcn,
|
||||||
|
(long long)rl->length);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
nr_freed += rl->length ;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = 0;
|
||||||
|
out:
|
||||||
|
vol->free_clusters += nr_freed;
|
||||||
|
if (vol->free_clusters > vol->nr_clusters)
|
||||||
|
ntfs_log_error("Too many free clusters (%lld > %lld)!",
|
||||||
|
(long long)vol->free_clusters,
|
||||||
|
(long long)vol->nr_clusters);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ntfs_cluster_free - free clusters on an ntfs volume
|
||||||
|
* @vol: mounted ntfs volume on which to free the clusters
|
||||||
|
* @na: attribute whose runlist describes the clusters to free
|
||||||
|
* @start_vcn: vcn in @rl at which to start freeing clusters
|
||||||
|
* @count: number of clusters to free or -1 for all clusters
|
||||||
|
*
|
||||||
|
* Free @count clusters starting at the cluster @start_vcn in the runlist
|
||||||
|
* described by the attribute @na from the mounted ntfs volume @vol.
|
||||||
|
*
|
||||||
|
* If @count is -1, all clusters from @start_vcn to the end of the runlist
|
||||||
|
* are deallocated.
|
||||||
|
*
|
||||||
|
* On success return the number of deallocated clusters (not counting sparse
|
||||||
|
* clusters) and on error return -1 with errno set to the error code.
|
||||||
|
*/
|
||||||
|
int ntfs_cluster_free(ntfs_volume *vol, ntfs_attr *na, VCN start_vcn, s64 count)
|
||||||
|
{
|
||||||
|
runlist *rl;
|
||||||
|
s64 delta, to_free, nr_freed = 0;
|
||||||
|
int ret = -1;
|
||||||
|
|
||||||
|
if (!vol || !vol->lcnbmp_na || !na || start_vcn < 0 ||
|
||||||
|
(count < 0 && count != -1)) {
|
||||||
|
ntfs_log_trace("Invalid arguments!\n");
|
||||||
|
errno = EINVAL;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
ntfs_log_enter("Entering for inode 0x%llx, attr 0x%x, count 0x%llx, "
|
||||||
|
"vcn 0x%llx.\n", (unsigned long long)na->ni->mft_no,
|
||||||
|
na->type, (long long)count, (long long)start_vcn);
|
||||||
|
|
||||||
|
rl = ntfs_attr_find_vcn(na, start_vcn);
|
||||||
|
if (!rl) {
|
||||||
|
if (errno == ENOENT)
|
||||||
|
ret = 0;
|
||||||
|
goto leave;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (rl->lcn < 0 && rl->lcn != LCN_HOLE) {
|
||||||
|
errno = EIO;
|
||||||
|
ntfs_log_perror("%s: Unexpected lcn (%lld)", __FUNCTION__,
|
||||||
|
(long long)rl->lcn);
|
||||||
|
goto leave;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Find the starting cluster inside the run that needs freeing. */
|
||||||
|
delta = start_vcn - rl->vcn;
|
||||||
|
|
||||||
|
/* The number of clusters in this run that need freeing. */
|
||||||
|
to_free = rl->length - delta;
|
||||||
|
if (count >= 0 && to_free > count)
|
||||||
|
to_free = count;
|
||||||
|
|
||||||
|
if (rl->lcn != LCN_HOLE) {
|
||||||
|
/* Do the actual freeing of the clusters in this run. */
|
||||||
|
update_full_status(vol,rl->lcn + delta);
|
||||||
|
if (ntfs_bitmap_clear_run(vol->lcnbmp_na, rl->lcn + delta,
|
||||||
|
to_free))
|
||||||
|
goto leave;
|
||||||
|
nr_freed = to_free;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Go to the next run and adjust the number of clusters left to free. */
|
||||||
|
++rl;
|
||||||
|
if (count >= 0)
|
||||||
|
count -= to_free;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Loop over the remaining runs, using @count as a capping value, and
|
||||||
|
* free them.
|
||||||
|
*/
|
||||||
|
for (; rl->length && count != 0; ++rl) {
|
||||||
|
// FIXME: Need to try ntfs_attr_map_runlist() for attribute
|
||||||
|
// list support! (AIA)
|
||||||
|
if (rl->lcn < 0 && rl->lcn != LCN_HOLE) {
|
||||||
|
// FIXME: Eeek! We need rollback! (AIA)
|
||||||
|
errno = EIO;
|
||||||
|
ntfs_log_perror("%s: Invalid lcn (%lli)",
|
||||||
|
__FUNCTION__, (long long)rl->lcn);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* The number of clusters in this run that need freeing. */
|
||||||
|
to_free = rl->length;
|
||||||
|
if (count >= 0 && to_free > count)
|
||||||
|
to_free = count;
|
||||||
|
|
||||||
|
if (rl->lcn != LCN_HOLE) {
|
||||||
|
update_full_status(vol,rl->lcn);
|
||||||
|
if (ntfs_bitmap_clear_run(vol->lcnbmp_na, rl->lcn,
|
||||||
|
to_free)) {
|
||||||
|
// FIXME: Eeek! We need rollback! (AIA)
|
||||||
|
ntfs_log_perror("%s: Clearing bitmap run failed",
|
||||||
|
__FUNCTION__);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
nr_freed += to_free;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (count >= 0)
|
||||||
|
count -= to_free;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (count != -1 && count != 0) {
|
||||||
|
// FIXME: Eeek! BUG()
|
||||||
|
errno = EIO;
|
||||||
|
ntfs_log_perror("%s: count still not zero (%lld)", __FUNCTION__,
|
||||||
|
(long long)count);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = nr_freed;
|
||||||
|
out:
|
||||||
|
vol->free_clusters += nr_freed ;
|
||||||
|
if (vol->free_clusters > vol->nr_clusters)
|
||||||
|
ntfs_log_error("Too many free clusters (%lld > %lld)!",
|
||||||
|
(long long)vol->free_clusters,
|
||||||
|
(long long)vol->nr_clusters);
|
||||||
|
leave:
|
||||||
|
ntfs_log_leave("\n");
|
||||||
|
return ret;
|
||||||
|
}
|
50
source/libntfs/lcnalloc.h
Normal file
50
source/libntfs/lcnalloc.h
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
/*
|
||||||
|
* lcnalloc.h - Exports for cluster (de)allocation. Originated from the Linux-NTFS
|
||||||
|
* project.
|
||||||
|
*
|
||||||
|
* Copyright (c) 2002 Anton Altaparmakov
|
||||||
|
* Copyright (c) 2004 Yura Pakhuchiy
|
||||||
|
*
|
||||||
|
* This program/include file 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/include file 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
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _NTFS_LCNALLOC_H
|
||||||
|
#define _NTFS_LCNALLOC_H
|
||||||
|
|
||||||
|
#include "types.h"
|
||||||
|
#include "runlist.h"
|
||||||
|
#include "volume.h"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* enum NTFS_CLUSTER_ALLOCATION_ZONES -
|
||||||
|
*/
|
||||||
|
typedef enum {
|
||||||
|
FIRST_ZONE = 0, /* For sanity checking. */
|
||||||
|
MFT_ZONE = 0, /* Allocate from $MFT zone. */
|
||||||
|
DATA_ZONE = 1, /* Allocate from $DATA zone. */
|
||||||
|
LAST_ZONE = 1, /* For sanity checking. */
|
||||||
|
} NTFS_CLUSTER_ALLOCATION_ZONES;
|
||||||
|
|
||||||
|
extern runlist *ntfs_cluster_alloc(ntfs_volume *vol, VCN start_vcn, s64 count,
|
||||||
|
LCN start_lcn, const NTFS_CLUSTER_ALLOCATION_ZONES zone);
|
||||||
|
|
||||||
|
extern int ntfs_cluster_free_from_rl(ntfs_volume *vol, runlist *rl);
|
||||||
|
|
||||||
|
extern int ntfs_cluster_free(ntfs_volume *vol, ntfs_attr *na, VCN start_vcn,
|
||||||
|
s64 count);
|
||||||
|
|
||||||
|
#endif /* defined _NTFS_LCNALLOC_H */
|
||||||
|
|
737
source/libntfs/logfile.c
Normal file
737
source/libntfs/logfile.c
Normal file
@ -0,0 +1,737 @@
|
|||||||
|
/**
|
||||||
|
* logfile.c - NTFS journal handling. Originated from the Linux-NTFS project.
|
||||||
|
*
|
||||||
|
* Copyright (c) 2002-2005 Anton Altaparmakov
|
||||||
|
* Copyright (c) 2005 Yura Pakhuchiy
|
||||||
|
* Copyright (c) 2005-2009 Szabolcs Szakacsits
|
||||||
|
*
|
||||||
|
* This program/include file 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/include file 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
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifdef HAVE_CONFIG_H
|
||||||
|
#include "config.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef HAVE_STDLIB_H
|
||||||
|
#include <stdlib.h>
|
||||||
|
#endif
|
||||||
|
#ifdef HAVE_STRING_H
|
||||||
|
#include <string.h>
|
||||||
|
#endif
|
||||||
|
#ifdef HAVE_ERRNO_H
|
||||||
|
#include <errno.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "attrib.h"
|
||||||
|
#include "debug.h"
|
||||||
|
#include "logfile.h"
|
||||||
|
#include "volume.h"
|
||||||
|
#include "mst.h"
|
||||||
|
#include "logging.h"
|
||||||
|
#include "misc.h"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ntfs_check_restart_page_header - check the page header for consistency
|
||||||
|
* @rp: restart page header to check
|
||||||
|
* @pos: position in logfile at which the restart page header resides
|
||||||
|
*
|
||||||
|
* Check the restart page header @rp for consistency and return TRUE if it is
|
||||||
|
* consistent and FALSE otherwise.
|
||||||
|
*
|
||||||
|
* This function only needs NTFS_BLOCK_SIZE bytes in @rp, i.e. it does not
|
||||||
|
* require the full restart page.
|
||||||
|
*/
|
||||||
|
static BOOL ntfs_check_restart_page_header(RESTART_PAGE_HEADER *rp, s64 pos)
|
||||||
|
{
|
||||||
|
u32 logfile_system_page_size, logfile_log_page_size;
|
||||||
|
u16 ra_ofs, usa_count, usa_ofs, usa_end = 0;
|
||||||
|
BOOL have_usa = TRUE;
|
||||||
|
|
||||||
|
ntfs_log_trace("Entering.\n");
|
||||||
|
/*
|
||||||
|
* If the system or log page sizes are smaller than the ntfs block size
|
||||||
|
* or either is not a power of 2 we cannot handle this log file.
|
||||||
|
*/
|
||||||
|
logfile_system_page_size = le32_to_cpu(rp->system_page_size);
|
||||||
|
logfile_log_page_size = le32_to_cpu(rp->log_page_size);
|
||||||
|
if (logfile_system_page_size < NTFS_BLOCK_SIZE ||
|
||||||
|
logfile_log_page_size < NTFS_BLOCK_SIZE ||
|
||||||
|
logfile_system_page_size &
|
||||||
|
(logfile_system_page_size - 1) ||
|
||||||
|
logfile_log_page_size & (logfile_log_page_size - 1)) {
|
||||||
|
ntfs_log_error("$LogFile uses unsupported page size.\n");
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
* We must be either at !pos (1st restart page) or at pos = system page
|
||||||
|
* size (2nd restart page).
|
||||||
|
*/
|
||||||
|
if (pos && pos != logfile_system_page_size) {
|
||||||
|
ntfs_log_error("Found restart area in incorrect "
|
||||||
|
"position in $LogFile.\n");
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
/* We only know how to handle version 1.1. */
|
||||||
|
if (sle16_to_cpu(rp->major_ver) != 1 ||
|
||||||
|
sle16_to_cpu(rp->minor_ver) != 1) {
|
||||||
|
ntfs_log_error("$LogFile version %i.%i is not "
|
||||||
|
"supported. (This driver supports version "
|
||||||
|
"1.1 only.)\n", (int)sle16_to_cpu(rp->major_ver),
|
||||||
|
(int)sle16_to_cpu(rp->minor_ver));
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
* If chkdsk has been run the restart page may not be protected by an
|
||||||
|
* update sequence array.
|
||||||
|
*/
|
||||||
|
if (ntfs_is_chkd_record(rp->magic) && !le16_to_cpu(rp->usa_count)) {
|
||||||
|
have_usa = FALSE;
|
||||||
|
goto skip_usa_checks;
|
||||||
|
}
|
||||||
|
/* Verify the size of the update sequence array. */
|
||||||
|
usa_count = 1 + (logfile_system_page_size >> NTFS_BLOCK_SIZE_BITS);
|
||||||
|
if (usa_count != le16_to_cpu(rp->usa_count)) {
|
||||||
|
ntfs_log_error("$LogFile restart page specifies "
|
||||||
|
"inconsistent update sequence array count.\n");
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
/* Verify the position of the update sequence array. */
|
||||||
|
usa_ofs = le16_to_cpu(rp->usa_ofs);
|
||||||
|
usa_end = usa_ofs + usa_count * sizeof(u16);
|
||||||
|
if (usa_ofs < sizeof(RESTART_PAGE_HEADER) ||
|
||||||
|
usa_end > NTFS_BLOCK_SIZE - sizeof(u16)) {
|
||||||
|
ntfs_log_error("$LogFile restart page specifies "
|
||||||
|
"inconsistent update sequence array offset.\n");
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
skip_usa_checks:
|
||||||
|
/*
|
||||||
|
* Verify the position of the restart area. It must be:
|
||||||
|
* - aligned to 8-byte boundary,
|
||||||
|
* - after the update sequence array, and
|
||||||
|
* - within the system page size.
|
||||||
|
*/
|
||||||
|
ra_ofs = le16_to_cpu(rp->restart_area_offset);
|
||||||
|
if (ra_ofs & 7 || (have_usa ? ra_ofs < usa_end :
|
||||||
|
ra_ofs < sizeof(RESTART_PAGE_HEADER)) ||
|
||||||
|
ra_ofs > logfile_system_page_size) {
|
||||||
|
ntfs_log_error("$LogFile restart page specifies "
|
||||||
|
"inconsistent restart area offset.\n");
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
* Only restart pages modified by chkdsk are allowed to have chkdsk_lsn
|
||||||
|
* set.
|
||||||
|
*/
|
||||||
|
if (!ntfs_is_chkd_record(rp->magic) && sle64_to_cpu(rp->chkdsk_lsn)) {
|
||||||
|
ntfs_log_error("$LogFile restart page is not modified "
|
||||||
|
"by chkdsk but a chkdsk LSN is specified.\n");
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
ntfs_log_trace("Done.\n");
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ntfs_check_restart_area - check the restart area for consistency
|
||||||
|
* @rp: restart page whose restart area to check
|
||||||
|
*
|
||||||
|
* Check the restart area of the restart page @rp for consistency and return
|
||||||
|
* TRUE if it is consistent and FALSE otherwise.
|
||||||
|
*
|
||||||
|
* This function assumes that the restart page header has already been
|
||||||
|
* consistency checked.
|
||||||
|
*
|
||||||
|
* This function only needs NTFS_BLOCK_SIZE bytes in @rp, i.e. it does not
|
||||||
|
* require the full restart page.
|
||||||
|
*/
|
||||||
|
static BOOL ntfs_check_restart_area(RESTART_PAGE_HEADER *rp)
|
||||||
|
{
|
||||||
|
u64 file_size;
|
||||||
|
RESTART_AREA *ra;
|
||||||
|
u16 ra_ofs, ra_len, ca_ofs;
|
||||||
|
u8 fs_bits;
|
||||||
|
|
||||||
|
ntfs_log_trace("Entering.\n");
|
||||||
|
ra_ofs = le16_to_cpu(rp->restart_area_offset);
|
||||||
|
ra = (RESTART_AREA*)((u8*)rp + ra_ofs);
|
||||||
|
/*
|
||||||
|
* Everything before ra->file_size must be before the first word
|
||||||
|
* protected by an update sequence number. This ensures that it is
|
||||||
|
* safe to access ra->client_array_offset.
|
||||||
|
*/
|
||||||
|
if (ra_ofs + offsetof(RESTART_AREA, file_size) >
|
||||||
|
NTFS_BLOCK_SIZE - sizeof(u16)) {
|
||||||
|
ntfs_log_error("$LogFile restart area specifies "
|
||||||
|
"inconsistent file offset.\n");
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
* Now that we can access ra->client_array_offset, make sure everything
|
||||||
|
* up to the log client array is before the first word protected by an
|
||||||
|
* update sequence number. This ensures we can access all of the
|
||||||
|
* restart area elements safely. Also, the client array offset must be
|
||||||
|
* aligned to an 8-byte boundary.
|
||||||
|
*/
|
||||||
|
ca_ofs = le16_to_cpu(ra->client_array_offset);
|
||||||
|
if (((ca_ofs + 7) & ~7) != ca_ofs ||
|
||||||
|
ra_ofs + ca_ofs > (u16)(NTFS_BLOCK_SIZE -
|
||||||
|
sizeof(u16))) {
|
||||||
|
ntfs_log_error("$LogFile restart area specifies "
|
||||||
|
"inconsistent client array offset.\n");
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
* The restart area must end within the system page size both when
|
||||||
|
* calculated manually and as specified by ra->restart_area_length.
|
||||||
|
* Also, the calculated length must not exceed the specified length.
|
||||||
|
*/
|
||||||
|
ra_len = ca_ofs + le16_to_cpu(ra->log_clients) *
|
||||||
|
sizeof(LOG_CLIENT_RECORD);
|
||||||
|
if ((u32)(ra_ofs + ra_len) > le32_to_cpu(rp->system_page_size) ||
|
||||||
|
(u32)(ra_ofs + le16_to_cpu(ra->restart_area_length)) >
|
||||||
|
le32_to_cpu(rp->system_page_size) ||
|
||||||
|
ra_len > le16_to_cpu(ra->restart_area_length)) {
|
||||||
|
ntfs_log_error("$LogFile restart area is out of bounds "
|
||||||
|
"of the system page size specified by the "
|
||||||
|
"restart page header and/or the specified "
|
||||||
|
"restart area length is inconsistent.\n");
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
* The ra->client_free_list and ra->client_in_use_list must be either
|
||||||
|
* LOGFILE_NO_CLIENT or less than ra->log_clients or they are
|
||||||
|
* overflowing the client array.
|
||||||
|
*/
|
||||||
|
if ((ra->client_free_list != LOGFILE_NO_CLIENT &&
|
||||||
|
le16_to_cpu(ra->client_free_list) >=
|
||||||
|
le16_to_cpu(ra->log_clients)) ||
|
||||||
|
(ra->client_in_use_list != LOGFILE_NO_CLIENT &&
|
||||||
|
le16_to_cpu(ra->client_in_use_list) >=
|
||||||
|
le16_to_cpu(ra->log_clients))) {
|
||||||
|
ntfs_log_error("$LogFile restart area specifies "
|
||||||
|
"overflowing client free and/or in use lists.\n");
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
* Check ra->seq_number_bits against ra->file_size for consistency.
|
||||||
|
* We cannot just use ffs() because the file size is not a power of 2.
|
||||||
|
*/
|
||||||
|
file_size = (u64)sle64_to_cpu(ra->file_size);
|
||||||
|
fs_bits = 0;
|
||||||
|
while (file_size) {
|
||||||
|
file_size >>= 1;
|
||||||
|
fs_bits++;
|
||||||
|
}
|
||||||
|
if (le32_to_cpu(ra->seq_number_bits) != (u32)(67 - fs_bits)) {
|
||||||
|
ntfs_log_error("$LogFile restart area specifies "
|
||||||
|
"inconsistent sequence number bits.\n");
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
/* The log record header length must be a multiple of 8. */
|
||||||
|
if (((le16_to_cpu(ra->log_record_header_length) + 7) & ~7) !=
|
||||||
|
le16_to_cpu(ra->log_record_header_length)) {
|
||||||
|
ntfs_log_error("$LogFile restart area specifies "
|
||||||
|
"inconsistent log record header length.\n");
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
/* Ditto for the log page data offset. */
|
||||||
|
if (((le16_to_cpu(ra->log_page_data_offset) + 7) & ~7) !=
|
||||||
|
le16_to_cpu(ra->log_page_data_offset)) {
|
||||||
|
ntfs_log_error("$LogFile restart area specifies "
|
||||||
|
"inconsistent log page data offset.\n");
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
ntfs_log_trace("Done.\n");
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ntfs_check_log_client_array - check the log client array for consistency
|
||||||
|
* @rp: restart page whose log client array to check
|
||||||
|
*
|
||||||
|
* Check the log client array of the restart page @rp for consistency and
|
||||||
|
* return TRUE if it is consistent and FALSE otherwise.
|
||||||
|
*
|
||||||
|
* This function assumes that the restart page header and the restart area have
|
||||||
|
* already been consistency checked.
|
||||||
|
*
|
||||||
|
* Unlike ntfs_check_restart_page_header() and ntfs_check_restart_area(), this
|
||||||
|
* function needs @rp->system_page_size bytes in @rp, i.e. it requires the full
|
||||||
|
* restart page and the page must be multi sector transfer deprotected.
|
||||||
|
*/
|
||||||
|
static BOOL ntfs_check_log_client_array(RESTART_PAGE_HEADER *rp)
|
||||||
|
{
|
||||||
|
RESTART_AREA *ra;
|
||||||
|
LOG_CLIENT_RECORD *ca, *cr;
|
||||||
|
u16 nr_clients, idx;
|
||||||
|
BOOL in_free_list, idx_is_first;
|
||||||
|
|
||||||
|
ntfs_log_trace("Entering.\n");
|
||||||
|
ra = (RESTART_AREA*)((u8*)rp + le16_to_cpu(rp->restart_area_offset));
|
||||||
|
ca = (LOG_CLIENT_RECORD*)((u8*)ra +
|
||||||
|
le16_to_cpu(ra->client_array_offset));
|
||||||
|
/*
|
||||||
|
* Check the ra->client_free_list first and then check the
|
||||||
|
* ra->client_in_use_list. Check each of the log client records in
|
||||||
|
* each of the lists and check that the array does not overflow the
|
||||||
|
* ra->log_clients value. Also keep track of the number of records
|
||||||
|
* visited as there cannot be more than ra->log_clients records and
|
||||||
|
* that way we detect eventual loops in within a list.
|
||||||
|
*/
|
||||||
|
nr_clients = le16_to_cpu(ra->log_clients);
|
||||||
|
idx = le16_to_cpu(ra->client_free_list);
|
||||||
|
in_free_list = TRUE;
|
||||||
|
check_list:
|
||||||
|
for (idx_is_first = TRUE; idx != LOGFILE_NO_CLIENT_CPU; nr_clients--,
|
||||||
|
idx = le16_to_cpu(cr->next_client)) {
|
||||||
|
if (!nr_clients || idx >= le16_to_cpu(ra->log_clients))
|
||||||
|
goto err_out;
|
||||||
|
/* Set @cr to the current log client record. */
|
||||||
|
cr = ca + idx;
|
||||||
|
/* The first log client record must not have a prev_client. */
|
||||||
|
if (idx_is_first) {
|
||||||
|
if (cr->prev_client != LOGFILE_NO_CLIENT)
|
||||||
|
goto err_out;
|
||||||
|
idx_is_first = FALSE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/* Switch to and check the in use list if we just did the free list. */
|
||||||
|
if (in_free_list) {
|
||||||
|
in_free_list = FALSE;
|
||||||
|
idx = le16_to_cpu(ra->client_in_use_list);
|
||||||
|
goto check_list;
|
||||||
|
}
|
||||||
|
ntfs_log_trace("Done.\n");
|
||||||
|
return TRUE;
|
||||||
|
err_out:
|
||||||
|
ntfs_log_error("$LogFile log client array is corrupt.\n");
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ntfs_check_and_load_restart_page - check the restart page for consistency
|
||||||
|
* @log_na: opened ntfs attribute for journal $LogFile
|
||||||
|
* @rp: restart page to check
|
||||||
|
* @pos: position in @log_na at which the restart page resides
|
||||||
|
* @wrp: [OUT] copy of the multi sector transfer deprotected restart page
|
||||||
|
* @lsn: [OUT] set to the current logfile lsn on success
|
||||||
|
*
|
||||||
|
* Check the restart page @rp for consistency and return 0 if it is consistent
|
||||||
|
* and errno otherwise. The restart page may have been modified by chkdsk in
|
||||||
|
* which case its magic is CHKD instead of RSTR.
|
||||||
|
*
|
||||||
|
* This function only needs NTFS_BLOCK_SIZE bytes in @rp, i.e. it does not
|
||||||
|
* require the full restart page.
|
||||||
|
*
|
||||||
|
* If @wrp is not NULL, on success, *@wrp will point to a buffer containing a
|
||||||
|
* copy of the complete multi sector transfer deprotected page. On failure,
|
||||||
|
* *@wrp is undefined.
|
||||||
|
*
|
||||||
|
* Similarly, if @lsn is not NULL, on success *@lsn will be set to the current
|
||||||
|
* logfile lsn according to this restart page. On failure, *@lsn is undefined.
|
||||||
|
*
|
||||||
|
* The following error codes are defined:
|
||||||
|
* EINVAL - The restart page is inconsistent.
|
||||||
|
* ENOMEM - Not enough memory to load the restart page.
|
||||||
|
* EIO - Failed to reading from $LogFile.
|
||||||
|
*/
|
||||||
|
static int ntfs_check_and_load_restart_page(ntfs_attr *log_na,
|
||||||
|
RESTART_PAGE_HEADER *rp, s64 pos, RESTART_PAGE_HEADER **wrp,
|
||||||
|
LSN *lsn)
|
||||||
|
{
|
||||||
|
RESTART_AREA *ra;
|
||||||
|
RESTART_PAGE_HEADER *trp;
|
||||||
|
int err;
|
||||||
|
|
||||||
|
ntfs_log_trace("Entering.\n");
|
||||||
|
/* Check the restart page header for consistency. */
|
||||||
|
if (!ntfs_check_restart_page_header(rp, pos)) {
|
||||||
|
/* Error output already done inside the function. */
|
||||||
|
return EINVAL;
|
||||||
|
}
|
||||||
|
/* Check the restart area for consistency. */
|
||||||
|
if (!ntfs_check_restart_area(rp)) {
|
||||||
|
/* Error output already done inside the function. */
|
||||||
|
return EINVAL;
|
||||||
|
}
|
||||||
|
ra = (RESTART_AREA*)((u8*)rp + le16_to_cpu(rp->restart_area_offset));
|
||||||
|
/*
|
||||||
|
* Allocate a buffer to store the whole restart page so we can multi
|
||||||
|
* sector transfer deprotect it.
|
||||||
|
*/
|
||||||
|
trp = ntfs_malloc(le32_to_cpu(rp->system_page_size));
|
||||||
|
if (!trp)
|
||||||
|
return errno;
|
||||||
|
/*
|
||||||
|
* Read the whole of the restart page into the buffer. If it fits
|
||||||
|
* completely inside @rp, just copy it from there. Otherwise read it
|
||||||
|
* from disk.
|
||||||
|
*/
|
||||||
|
if (le32_to_cpu(rp->system_page_size) <= NTFS_BLOCK_SIZE)
|
||||||
|
memcpy(trp, rp, le32_to_cpu(rp->system_page_size));
|
||||||
|
else if (ntfs_attr_pread(log_na, pos,
|
||||||
|
le32_to_cpu(rp->system_page_size), trp) !=
|
||||||
|
le32_to_cpu(rp->system_page_size)) {
|
||||||
|
err = errno;
|
||||||
|
ntfs_log_error("Failed to read whole restart page into the "
|
||||||
|
"buffer.\n");
|
||||||
|
if (err != ENOMEM)
|
||||||
|
err = EIO;
|
||||||
|
goto err_out;
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
* Perform the multi sector transfer deprotection on the buffer if the
|
||||||
|
* restart page is protected.
|
||||||
|
*/
|
||||||
|
if ((!ntfs_is_chkd_record(trp->magic) || le16_to_cpu(trp->usa_count))
|
||||||
|
&& ntfs_mst_post_read_fixup((NTFS_RECORD*)trp,
|
||||||
|
le32_to_cpu(rp->system_page_size))) {
|
||||||
|
/*
|
||||||
|
* A multi sector tranfer error was detected. We only need to
|
||||||
|
* abort if the restart page contents exceed the multi sector
|
||||||
|
* transfer fixup of the first sector.
|
||||||
|
*/
|
||||||
|
if (le16_to_cpu(rp->restart_area_offset) +
|
||||||
|
le16_to_cpu(ra->restart_area_length) >
|
||||||
|
NTFS_BLOCK_SIZE - (int)sizeof(u16)) {
|
||||||
|
ntfs_log_error("Multi sector transfer error "
|
||||||
|
"detected in $LogFile restart page.\n");
|
||||||
|
err = EINVAL;
|
||||||
|
goto err_out;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
* If the restart page is modified by chkdsk or there are no active
|
||||||
|
* logfile clients, the logfile is consistent. Otherwise, need to
|
||||||
|
* check the log client records for consistency, too.
|
||||||
|
*/
|
||||||
|
err = 0;
|
||||||
|
if (ntfs_is_rstr_record(rp->magic) &&
|
||||||
|
ra->client_in_use_list != LOGFILE_NO_CLIENT) {
|
||||||
|
if (!ntfs_check_log_client_array(trp)) {
|
||||||
|
err = EINVAL;
|
||||||
|
goto err_out;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (lsn) {
|
||||||
|
if (ntfs_is_rstr_record(rp->magic))
|
||||||
|
*lsn = sle64_to_cpu(ra->current_lsn);
|
||||||
|
else /* if (ntfs_is_chkd_record(rp->magic)) */
|
||||||
|
*lsn = sle64_to_cpu(rp->chkdsk_lsn);
|
||||||
|
}
|
||||||
|
ntfs_log_trace("Done.\n");
|
||||||
|
if (wrp)
|
||||||
|
*wrp = trp;
|
||||||
|
else {
|
||||||
|
err_out:
|
||||||
|
free(trp);
|
||||||
|
}
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ntfs_check_logfile - check in the journal if the volume is consistent
|
||||||
|
* @log_na: ntfs attribute of loaded journal $LogFile to check
|
||||||
|
* @rp: [OUT] on success this is a copy of the current restart page
|
||||||
|
*
|
||||||
|
* Check the $LogFile journal for consistency and return TRUE if it is
|
||||||
|
* consistent and FALSE if not. On success, the current restart page is
|
||||||
|
* returned in *@rp. Caller must call ntfs_free(*@rp) when finished with it.
|
||||||
|
*
|
||||||
|
* At present we only check the two restart pages and ignore the log record
|
||||||
|
* pages.
|
||||||
|
*
|
||||||
|
* Note that the MstProtected flag is not set on the $LogFile inode and hence
|
||||||
|
* when reading pages they are not deprotected. This is because we do not know
|
||||||
|
* if the $LogFile was created on a system with a different page size to ours
|
||||||
|
* yet and mst deprotection would fail if our page size is smaller.
|
||||||
|
*/
|
||||||
|
BOOL ntfs_check_logfile(ntfs_attr *log_na, RESTART_PAGE_HEADER **rp)
|
||||||
|
{
|
||||||
|
s64 size, pos;
|
||||||
|
LSN rstr1_lsn, rstr2_lsn;
|
||||||
|
ntfs_volume *vol = log_na->ni->vol;
|
||||||
|
u8 *kaddr = NULL;
|
||||||
|
RESTART_PAGE_HEADER *rstr1_ph = NULL;
|
||||||
|
RESTART_PAGE_HEADER *rstr2_ph = NULL;
|
||||||
|
int log_page_size, log_page_mask, err;
|
||||||
|
BOOL logfile_is_empty = TRUE;
|
||||||
|
u8 log_page_bits;
|
||||||
|
|
||||||
|
ntfs_log_trace("Entering.\n");
|
||||||
|
/* An empty $LogFile must have been clean before it got emptied. */
|
||||||
|
if (NVolLogFileEmpty(vol))
|
||||||
|
goto is_empty;
|
||||||
|
size = log_na->data_size;
|
||||||
|
/* Make sure the file doesn't exceed the maximum allowed size. */
|
||||||
|
if (size > (s64)MaxLogFileSize)
|
||||||
|
size = MaxLogFileSize;
|
||||||
|
log_page_size = DefaultLogPageSize;
|
||||||
|
log_page_mask = log_page_size - 1;
|
||||||
|
/*
|
||||||
|
* Use generic_ffs() instead of ffs() to enable the compiler to
|
||||||
|
* optimize log_page_size and log_page_bits into constants.
|
||||||
|
*/
|
||||||
|
log_page_bits = ffs(log_page_size) - 1;
|
||||||
|
size &= ~(log_page_size - 1);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Ensure the log file is big enough to store at least the two restart
|
||||||
|
* pages and the minimum number of log record pages.
|
||||||
|
*/
|
||||||
|
if (size < log_page_size * 2 || (size - log_page_size * 2) >>
|
||||||
|
log_page_bits < MinLogRecordPages) {
|
||||||
|
ntfs_log_error("$LogFile is too small.\n");
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
/* Allocate memory for restart page. */
|
||||||
|
kaddr = ntfs_malloc(NTFS_BLOCK_SIZE);
|
||||||
|
if (!kaddr)
|
||||||
|
return FALSE;
|
||||||
|
/*
|
||||||
|
* Read through the file looking for a restart page. Since the restart
|
||||||
|
* page header is at the beginning of a page we only need to search at
|
||||||
|
* what could be the beginning of a page (for each page size) rather
|
||||||
|
* than scanning the whole file byte by byte. If all potential places
|
||||||
|
* contain empty and uninitialized records, the log file can be assumed
|
||||||
|
* to be empty.
|
||||||
|
*/
|
||||||
|
for (pos = 0; pos < size; pos <<= 1) {
|
||||||
|
/*
|
||||||
|
* Read first NTFS_BLOCK_SIZE bytes of potential restart page.
|
||||||
|
*/
|
||||||
|
if (ntfs_attr_pread(log_na, pos, NTFS_BLOCK_SIZE, kaddr) !=
|
||||||
|
NTFS_BLOCK_SIZE) {
|
||||||
|
ntfs_log_error("Failed to read first NTFS_BLOCK_SIZE "
|
||||||
|
"bytes of potential restart page.\n");
|
||||||
|
goto err_out;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* A non-empty block means the logfile is not empty while an
|
||||||
|
* empty block after a non-empty block has been encountered
|
||||||
|
* means we are done.
|
||||||
|
*/
|
||||||
|
if (!ntfs_is_empty_recordp((le32*)kaddr))
|
||||||
|
logfile_is_empty = FALSE;
|
||||||
|
else if (!logfile_is_empty)
|
||||||
|
break;
|
||||||
|
/*
|
||||||
|
* A log record page means there cannot be a restart page after
|
||||||
|
* this so no need to continue searching.
|
||||||
|
*/
|
||||||
|
if (ntfs_is_rcrd_recordp((le32*)kaddr))
|
||||||
|
break;
|
||||||
|
/* If not a (modified by chkdsk) restart page, continue. */
|
||||||
|
if (!ntfs_is_rstr_recordp((le32*)kaddr) &&
|
||||||
|
!ntfs_is_chkd_recordp((le32*)kaddr)) {
|
||||||
|
if (!pos)
|
||||||
|
pos = NTFS_BLOCK_SIZE >> 1;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
* Check the (modified by chkdsk) restart page for consistency
|
||||||
|
* and get a copy of the complete multi sector transfer
|
||||||
|
* deprotected restart page.
|
||||||
|
*/
|
||||||
|
err = ntfs_check_and_load_restart_page(log_na,
|
||||||
|
(RESTART_PAGE_HEADER*)kaddr, pos,
|
||||||
|
!rstr1_ph ? &rstr1_ph : &rstr2_ph,
|
||||||
|
!rstr1_ph ? &rstr1_lsn : &rstr2_lsn);
|
||||||
|
if (!err) {
|
||||||
|
/*
|
||||||
|
* If we have now found the first (modified by chkdsk)
|
||||||
|
* restart page, continue looking for the second one.
|
||||||
|
*/
|
||||||
|
if (!pos) {
|
||||||
|
pos = NTFS_BLOCK_SIZE >> 1;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
* We have now found the second (modified by chkdsk)
|
||||||
|
* restart page, so we can stop looking.
|
||||||
|
*/
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
* Error output already done inside the function. Note, we do
|
||||||
|
* not abort if the restart page was invalid as we might still
|
||||||
|
* find a valid one further in the file.
|
||||||
|
*/
|
||||||
|
if (err != EINVAL)
|
||||||
|
goto err_out;
|
||||||
|
/* Continue looking. */
|
||||||
|
if (!pos)
|
||||||
|
pos = NTFS_BLOCK_SIZE >> 1;
|
||||||
|
}
|
||||||
|
if (kaddr) {
|
||||||
|
free(kaddr);
|
||||||
|
kaddr = NULL;
|
||||||
|
}
|
||||||
|
if (logfile_is_empty) {
|
||||||
|
NVolSetLogFileEmpty(vol);
|
||||||
|
is_empty:
|
||||||
|
ntfs_log_trace("Done. ($LogFile is empty.)\n");
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
if (!rstr1_ph) {
|
||||||
|
if (rstr2_ph)
|
||||||
|
ntfs_log_error("BUG: rstr2_ph isn't NULL!\n");
|
||||||
|
ntfs_log_error("Did not find any restart pages in "
|
||||||
|
"$LogFile and it was not empty.\n");
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
/* If both restart pages were found, use the more recent one. */
|
||||||
|
if (rstr2_ph) {
|
||||||
|
/*
|
||||||
|
* If the second restart area is more recent, switch to it.
|
||||||
|
* Otherwise just throw it away.
|
||||||
|
*/
|
||||||
|
if (rstr2_lsn > rstr1_lsn) {
|
||||||
|
ntfs_log_debug("Using second restart page as it is more "
|
||||||
|
"recent.\n");
|
||||||
|
free(rstr1_ph);
|
||||||
|
rstr1_ph = rstr2_ph;
|
||||||
|
/* rstr1_lsn = rstr2_lsn; */
|
||||||
|
} else {
|
||||||
|
ntfs_log_debug("Using first restart page as it is more "
|
||||||
|
"recent.\n");
|
||||||
|
free(rstr2_ph);
|
||||||
|
}
|
||||||
|
rstr2_ph = NULL;
|
||||||
|
}
|
||||||
|
/* All consistency checks passed. */
|
||||||
|
if (rp)
|
||||||
|
*rp = rstr1_ph;
|
||||||
|
else
|
||||||
|
free(rstr1_ph);
|
||||||
|
ntfs_log_trace("Done.\n");
|
||||||
|
return TRUE;
|
||||||
|
err_out:
|
||||||
|
free(kaddr);
|
||||||
|
free(rstr1_ph);
|
||||||
|
free(rstr2_ph);
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ntfs_is_logfile_clean - check in the journal if the volume is clean
|
||||||
|
* @log_na: ntfs attribute of loaded journal $LogFile to check
|
||||||
|
* @rp: copy of the current restart page
|
||||||
|
*
|
||||||
|
* Analyze the $LogFile journal and return TRUE if it indicates the volume was
|
||||||
|
* shutdown cleanly and FALSE if not.
|
||||||
|
*
|
||||||
|
* At present we only look at the two restart pages and ignore the log record
|
||||||
|
* pages. This is a little bit crude in that there will be a very small number
|
||||||
|
* of cases where we think that a volume is dirty when in fact it is clean.
|
||||||
|
* This should only affect volumes that have not been shutdown cleanly but did
|
||||||
|
* not have any pending, non-check-pointed i/o, i.e. they were completely idle
|
||||||
|
* at least for the five seconds preceding the unclean shutdown.
|
||||||
|
*
|
||||||
|
* This function assumes that the $LogFile journal has already been consistency
|
||||||
|
* checked by a call to ntfs_check_logfile() and in particular if the $LogFile
|
||||||
|
* is empty this function requires that NVolLogFileEmpty() is true otherwise an
|
||||||
|
* empty volume will be reported as dirty.
|
||||||
|
*/
|
||||||
|
BOOL ntfs_is_logfile_clean(ntfs_attr *log_na, RESTART_PAGE_HEADER *rp)
|
||||||
|
{
|
||||||
|
RESTART_AREA *ra;
|
||||||
|
|
||||||
|
ntfs_log_trace("Entering.\n");
|
||||||
|
/* An empty $LogFile must have been clean before it got emptied. */
|
||||||
|
if (NVolLogFileEmpty(log_na->ni->vol)) {
|
||||||
|
ntfs_log_trace("$LogFile is empty\n");
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
if (!rp) {
|
||||||
|
ntfs_log_error("Restart page header is NULL\n");
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
if (!ntfs_is_rstr_record(rp->magic) &&
|
||||||
|
!ntfs_is_chkd_record(rp->magic)) {
|
||||||
|
ntfs_log_error("Restart page buffer is invalid\n");
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
ra = (RESTART_AREA*)((u8*)rp + le16_to_cpu(rp->restart_area_offset));
|
||||||
|
/*
|
||||||
|
* If the $LogFile has active clients, i.e. it is open, and we do not
|
||||||
|
* have the RESTART_VOLUME_IS_CLEAN bit set in the restart area flags,
|
||||||
|
* we assume there was an unclean shutdown.
|
||||||
|
*/
|
||||||
|
if (ra->client_in_use_list != LOGFILE_NO_CLIENT &&
|
||||||
|
!(ra->flags & RESTART_VOLUME_IS_CLEAN)) {
|
||||||
|
ntfs_log_error("The disk contains an unclean file system (%d, "
|
||||||
|
"%d).\n", le16_to_cpu(ra->client_in_use_list),
|
||||||
|
le16_to_cpu(ra->flags));
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
/* $LogFile indicates a clean shutdown. */
|
||||||
|
ntfs_log_trace("$LogFile indicates a clean shutdown\n");
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ntfs_empty_logfile - empty the contents of the $LogFile journal
|
||||||
|
* @na: ntfs attribute of journal $LogFile to empty
|
||||||
|
*
|
||||||
|
* Empty the contents of the $LogFile journal @na and return 0 on success and
|
||||||
|
* -1 on error.
|
||||||
|
*
|
||||||
|
* This function assumes that the $LogFile journal has already been consistency
|
||||||
|
* checked by a call to ntfs_check_logfile() and that ntfs_is_logfile_clean()
|
||||||
|
* has been used to ensure that the $LogFile is clean.
|
||||||
|
*/
|
||||||
|
int ntfs_empty_logfile(ntfs_attr *na)
|
||||||
|
{
|
||||||
|
s64 pos, count;
|
||||||
|
char buf[NTFS_BUF_SIZE];
|
||||||
|
|
||||||
|
ntfs_log_trace("Entering.\n");
|
||||||
|
|
||||||
|
if (NVolLogFileEmpty(na->ni->vol))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if (!NAttrNonResident(na)) {
|
||||||
|
errno = EIO;
|
||||||
|
ntfs_log_perror("Resident $LogFile $DATA attribute");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
memset(buf, -1, NTFS_BUF_SIZE);
|
||||||
|
|
||||||
|
pos = 0;
|
||||||
|
while ((count = na->data_size - pos) > 0) {
|
||||||
|
|
||||||
|
if (count > NTFS_BUF_SIZE)
|
||||||
|
count = NTFS_BUF_SIZE;
|
||||||
|
|
||||||
|
count = ntfs_attr_pwrite(na, pos, count, buf);
|
||||||
|
if (count <= 0) {
|
||||||
|
ntfs_log_perror("Failed to reset $LogFile");
|
||||||
|
if (count != -1)
|
||||||
|
errno = EIO;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
pos += count;
|
||||||
|
}
|
||||||
|
|
||||||
|
NVolSetLogFileEmpty(na->ni->vol);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
394
source/libntfs/logfile.h
Normal file
394
source/libntfs/logfile.h
Normal file
@ -0,0 +1,394 @@
|
|||||||
|
/*
|
||||||
|
* logfile.h - Exports for $LogFile handling. Originated from the Linux-NTFS project.
|
||||||
|
*
|
||||||
|
* Copyright (c) 2000-2005 Anton Altaparmakov
|
||||||
|
*
|
||||||
|
* This program/include file 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/include file 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
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _NTFS_LOGFILE_H
|
||||||
|
#define _NTFS_LOGFILE_H
|
||||||
|
|
||||||
|
#include "types.h"
|
||||||
|
#include "endians.h"
|
||||||
|
#include "layout.h"
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Journal ($LogFile) organization:
|
||||||
|
*
|
||||||
|
* Two restart areas present in the first two pages (restart pages, one restart
|
||||||
|
* area in each page). When the volume is dismounted they should be identical,
|
||||||
|
* except for the update sequence array which usually has a different update
|
||||||
|
* sequence number.
|
||||||
|
*
|
||||||
|
* These are followed by log records organized in pages headed by a log record
|
||||||
|
* header going up to log file size. Not all pages contain log records when a
|
||||||
|
* volume is first formatted, but as the volume ages, all records will be used.
|
||||||
|
* When the log file fills up, the records at the beginning are purged (by
|
||||||
|
* modifying the oldest_lsn to a higher value presumably) and writing begins
|
||||||
|
* at the beginning of the file. Effectively, the log file is viewed as a
|
||||||
|
* circular entity.
|
||||||
|
*
|
||||||
|
* NOTE: Windows NT, 2000, and XP all use log file version 1.1 but they accept
|
||||||
|
* versions <= 1.x, including 0.-1. (Yes, that is a minus one in there!) We
|
||||||
|
* probably only want to support 1.1 as this seems to be the current version
|
||||||
|
* and we don't know how that differs from the older versions. The only
|
||||||
|
* exception is if the journal is clean as marked by the two restart pages
|
||||||
|
* then it doesn't matter whether we are on an earlier version. We can just
|
||||||
|
* reinitialize the logfile and start again with version 1.1.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* Some $LogFile related constants. */
|
||||||
|
#define MaxLogFileSize 0x100000000ULL
|
||||||
|
#define DefaultLogPageSize 4096
|
||||||
|
#define MinLogRecordPages 48
|
||||||
|
|
||||||
|
/**
|
||||||
|
* struct RESTART_PAGE_HEADER - Log file restart page header.
|
||||||
|
*
|
||||||
|
* Begins the restart area.
|
||||||
|
*/
|
||||||
|
typedef struct {
|
||||||
|
/*Ofs*/
|
||||||
|
/* 0 NTFS_RECORD; -- Unfolded here as gcc doesn't like unnamed structs. */
|
||||||
|
/* 0*/ NTFS_RECORD_TYPES magic;/* The magic is "RSTR". */
|
||||||
|
/* 4*/ le16 usa_ofs; /* See NTFS_RECORD definition in layout.h.
|
||||||
|
When creating, set this to be immediately
|
||||||
|
after this header structure (without any
|
||||||
|
alignment). */
|
||||||
|
/* 6*/ le16 usa_count; /* See NTFS_RECORD definition in layout.h. */
|
||||||
|
|
||||||
|
/* 8*/ leLSN chkdsk_lsn; /* The last log file sequence number found by
|
||||||
|
chkdsk. Only used when the magic is changed
|
||||||
|
to "CHKD". Otherwise this is zero. */
|
||||||
|
/* 16*/ le32 system_page_size; /* Byte size of system pages when the log file
|
||||||
|
was created, has to be >= 512 and a power of
|
||||||
|
2. Use this to calculate the required size
|
||||||
|
of the usa (usa_count) and add it to usa_ofs.
|
||||||
|
Then verify that the result is less than the
|
||||||
|
value of the restart_area_offset. */
|
||||||
|
/* 20*/ le32 log_page_size; /* Byte size of log file pages, has to be >=
|
||||||
|
512 and a power of 2. The default is 4096
|
||||||
|
and is used when the system page size is
|
||||||
|
between 4096 and 8192. Otherwise this is
|
||||||
|
set to the system page size instead. */
|
||||||
|
/* 24*/ le16 restart_area_offset;/* Byte offset from the start of this header to
|
||||||
|
the RESTART_AREA. Value has to be aligned
|
||||||
|
to 8-byte boundary. When creating, set this
|
||||||
|
to be after the usa. */
|
||||||
|
/* 26*/ sle16 minor_ver; /* Log file minor version. Only check if major
|
||||||
|
version is 1. */
|
||||||
|
/* 28*/ sle16 major_ver; /* Log file major version. We only support
|
||||||
|
version 1.1. */
|
||||||
|
/* sizeof() = 30 (0x1e) bytes */
|
||||||
|
} __attribute__((__packed__)) RESTART_PAGE_HEADER;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Constant for the log client indices meaning that there are no client records
|
||||||
|
* in this particular client array. Also inside the client records themselves,
|
||||||
|
* this means that there are no client records preceding or following this one.
|
||||||
|
*/
|
||||||
|
#define LOGFILE_NO_CLIENT const_cpu_to_le16(0xffff)
|
||||||
|
#define LOGFILE_NO_CLIENT_CPU 0xffff
|
||||||
|
|
||||||
|
/*
|
||||||
|
* These are the so far known RESTART_AREA_* flags (16-bit) which contain
|
||||||
|
* information about the log file in which they are present.
|
||||||
|
*/
|
||||||
|
enum {
|
||||||
|
RESTART_VOLUME_IS_CLEAN = const_cpu_to_le16(0x0002),
|
||||||
|
RESTART_SPACE_FILLER = 0xffff, /* gcc: Force enum bit width to 16. */
|
||||||
|
} __attribute__((__packed__));
|
||||||
|
|
||||||
|
typedef le16 RESTART_AREA_FLAGS;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* struct RESTART_AREA - Log file restart area record.
|
||||||
|
*
|
||||||
|
* The offset of this record is found by adding the offset of the
|
||||||
|
* RESTART_PAGE_HEADER to the restart_area_offset value found in it.
|
||||||
|
* See notes at restart_area_offset above.
|
||||||
|
*/
|
||||||
|
typedef struct {
|
||||||
|
/*Ofs*/
|
||||||
|
/* 0*/ leLSN current_lsn; /* The current, i.e. last LSN inside the log
|
||||||
|
when the restart area was last written.
|
||||||
|
This happens often but what is the interval?
|
||||||
|
Is it just fixed time or is it every time a
|
||||||
|
check point is written or something else?
|
||||||
|
On create set to 0. */
|
||||||
|
/* 8*/ le16 log_clients; /* Number of log client records in the array of
|
||||||
|
log client records which follows this
|
||||||
|
restart area. Must be 1. */
|
||||||
|
/* 10*/ le16 client_free_list; /* The index of the first free log client record
|
||||||
|
in the array of log client records.
|
||||||
|
LOGFILE_NO_CLIENT means that there are no
|
||||||
|
free log client records in the array.
|
||||||
|
If != LOGFILE_NO_CLIENT, check that
|
||||||
|
log_clients > client_free_list. On Win2k
|
||||||
|
and presumably earlier, on a clean volume
|
||||||
|
this is != LOGFILE_NO_CLIENT, and it should
|
||||||
|
be 0, i.e. the first (and only) client
|
||||||
|
record is free and thus the logfile is
|
||||||
|
closed and hence clean. A dirty volume
|
||||||
|
would have left the logfile open and hence
|
||||||
|
this would be LOGFILE_NO_CLIENT. On WinXP
|
||||||
|
and presumably later, the logfile is always
|
||||||
|
open, even on clean shutdown so this should
|
||||||
|
always be LOGFILE_NO_CLIENT. */
|
||||||
|
/* 12*/ le16 client_in_use_list;/* The index of the first in-use log client
|
||||||
|
record in the array of log client records.
|
||||||
|
LOGFILE_NO_CLIENT means that there are no
|
||||||
|
in-use log client records in the array. If
|
||||||
|
!= LOGFILE_NO_CLIENT check that log_clients
|
||||||
|
> client_in_use_list. On Win2k and
|
||||||
|
presumably earlier, on a clean volume this
|
||||||
|
is LOGFILE_NO_CLIENT, i.e. there are no
|
||||||
|
client records in use and thus the logfile
|
||||||
|
is closed and hence clean. A dirty volume
|
||||||
|
would have left the logfile open and hence
|
||||||
|
this would be != LOGFILE_NO_CLIENT, and it
|
||||||
|
should be 0, i.e. the first (and only)
|
||||||
|
client record is in use. On WinXP and
|
||||||
|
presumably later, the logfile is always
|
||||||
|
open, even on clean shutdown so this should
|
||||||
|
always be 0. */
|
||||||
|
/* 14*/ RESTART_AREA_FLAGS flags;/* Flags modifying LFS behaviour. On Win2k
|
||||||
|
and presumably earlier this is always 0. On
|
||||||
|
WinXP and presumably later, if the logfile
|
||||||
|
was shutdown cleanly, the second bit,
|
||||||
|
RESTART_VOLUME_IS_CLEAN, is set. This bit
|
||||||
|
is cleared when the volume is mounted by
|
||||||
|
WinXP and set when the volume is dismounted,
|
||||||
|
thus if the logfile is dirty, this bit is
|
||||||
|
clear. Thus we don't need to check the
|
||||||
|
Windows version to determine if the logfile
|
||||||
|
is clean. Instead if the logfile is closed,
|
||||||
|
we know it must be clean. If it is open and
|
||||||
|
this bit is set, we also know it must be
|
||||||
|
clean. If on the other hand the logfile is
|
||||||
|
open and this bit is clear, we can be almost
|
||||||
|
certain that the logfile is dirty. */
|
||||||
|
/* 16*/ le32 seq_number_bits; /* How many bits to use for the sequence
|
||||||
|
number. This is calculated as 67 - the
|
||||||
|
number of bits required to store the logfile
|
||||||
|
size in bytes and this can be used in with
|
||||||
|
the specified file_size as a consistency
|
||||||
|
check. */
|
||||||
|
/* 20*/ le16 restart_area_length;/* Length of the restart area including the
|
||||||
|
client array. Following checks required if
|
||||||
|
version matches. Otherwise, skip them.
|
||||||
|
restart_area_offset + restart_area_length
|
||||||
|
has to be <= system_page_size. Also,
|
||||||
|
restart_area_length has to be >=
|
||||||
|
client_array_offset + (log_clients *
|
||||||
|
sizeof(log client record)). */
|
||||||
|
/* 22*/ le16 client_array_offset;/* Offset from the start of this record to
|
||||||
|
the first log client record if versions are
|
||||||
|
matched. When creating, set this to be
|
||||||
|
after this restart area structure, aligned
|
||||||
|
to 8-bytes boundary. If the versions do not
|
||||||
|
match, this is ignored and the offset is
|
||||||
|
assumed to be (sizeof(RESTART_AREA) + 7) &
|
||||||
|
~7, i.e. rounded up to first 8-byte
|
||||||
|
boundary. Either way, client_array_offset
|
||||||
|
has to be aligned to an 8-byte boundary.
|
||||||
|
Also, restart_area_offset +
|
||||||
|
client_array_offset has to be <= 510.
|
||||||
|
Finally, client_array_offset + (log_clients
|
||||||
|
* sizeof(log client record)) has to be <=
|
||||||
|
system_page_size. On Win2k and presumably
|
||||||
|
earlier, this is 0x30, i.e. immediately
|
||||||
|
following this record. On WinXP and
|
||||||
|
presumably later, this is 0x40, i.e. there
|
||||||
|
are 16 extra bytes between this record and
|
||||||
|
the client array. This probably means that
|
||||||
|
the RESTART_AREA record is actually bigger
|
||||||
|
in WinXP and later. */
|
||||||
|
/* 24*/ sle64 file_size; /* Usable byte size of the log file. If the
|
||||||
|
restart_area_offset + the offset of the
|
||||||
|
file_size are > 510 then corruption has
|
||||||
|
occurred. This is the very first check when
|
||||||
|
starting with the restart_area as if it
|
||||||
|
fails it means that some of the above values
|
||||||
|
will be corrupted by the multi sector
|
||||||
|
transfer protection. The file_size has to
|
||||||
|
be rounded down to be a multiple of the
|
||||||
|
log_page_size in the RESTART_PAGE_HEADER and
|
||||||
|
then it has to be at least big enough to
|
||||||
|
store the two restart pages and 48 (0x30)
|
||||||
|
log record pages. */
|
||||||
|
/* 32*/ le32 last_lsn_data_length;/* Length of data of last LSN, not including
|
||||||
|
the log record header. On create set to
|
||||||
|
0. */
|
||||||
|
/* 36*/ le16 log_record_header_length;/* Byte size of the log record header.
|
||||||
|
If the version matches then check that the
|
||||||
|
value of log_record_header_length is a
|
||||||
|
multiple of 8, i.e.
|
||||||
|
(log_record_header_length + 7) & ~7 ==
|
||||||
|
log_record_header_length. When creating set
|
||||||
|
it to sizeof(LOG_RECORD_HEADER), aligned to
|
||||||
|
8 bytes. */
|
||||||
|
/* 38*/ le16 log_page_data_offset;/* Offset to the start of data in a log record
|
||||||
|
page. Must be a multiple of 8. On create
|
||||||
|
set it to immediately after the update
|
||||||
|
sequence array of the log record page. */
|
||||||
|
/* 40*/ le32 restart_log_open_count;/* A counter that gets incremented every
|
||||||
|
time the logfile is restarted which happens
|
||||||
|
at mount time when the logfile is opened.
|
||||||
|
When creating set to a random value. Win2k
|
||||||
|
sets it to the low 32 bits of the current
|
||||||
|
system time in NTFS format (see time.h). */
|
||||||
|
/* 44*/ le32 reserved; /* Reserved/alignment to 8-byte boundary. */
|
||||||
|
/* sizeof() = 48 (0x30) bytes */
|
||||||
|
} __attribute__((__packed__)) RESTART_AREA;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* struct LOG_CLIENT_RECORD - Log client record.
|
||||||
|
*
|
||||||
|
* The offset of this record is found by adding the offset of the
|
||||||
|
* RESTART_AREA to the client_array_offset value found in it.
|
||||||
|
*/
|
||||||
|
typedef struct {
|
||||||
|
/*Ofs*/
|
||||||
|
/* 0*/ leLSN oldest_lsn; /* Oldest LSN needed by this client. On create
|
||||||
|
set to 0. */
|
||||||
|
/* 8*/ leLSN client_restart_lsn;/* LSN at which this client needs to restart
|
||||||
|
the volume, i.e. the current position within
|
||||||
|
the log file. At present, if clean this
|
||||||
|
should = current_lsn in restart area but it
|
||||||
|
probably also = current_lsn when dirty most
|
||||||
|
of the time. At create set to 0. */
|
||||||
|
/* 16*/ le16 prev_client; /* The offset to the previous log client record
|
||||||
|
in the array of log client records.
|
||||||
|
LOGFILE_NO_CLIENT means there is no previous
|
||||||
|
client record, i.e. this is the first one.
|
||||||
|
This is always LOGFILE_NO_CLIENT. */
|
||||||
|
/* 18*/ le16 next_client; /* The offset to the next log client record in
|
||||||
|
the array of log client records.
|
||||||
|
LOGFILE_NO_CLIENT means there are no next
|
||||||
|
client records, i.e. this is the last one.
|
||||||
|
This is always LOGFILE_NO_CLIENT. */
|
||||||
|
/* 20*/ le16 seq_number; /* On Win2k and presumably earlier, this is set
|
||||||
|
to zero every time the logfile is restarted
|
||||||
|
and it is incremented when the logfile is
|
||||||
|
closed at dismount time. Thus it is 0 when
|
||||||
|
dirty and 1 when clean. On WinXP and
|
||||||
|
presumably later, this is always 0. */
|
||||||
|
/* 22*/ u8 reserved[6]; /* Reserved/alignment. */
|
||||||
|
/* 28*/ le32 client_name_length;/* Length of client name in bytes. Should
|
||||||
|
always be 8. */
|
||||||
|
/* 32*/ ntfschar client_name[64];/* Name of the client in Unicode. Should
|
||||||
|
always be "NTFS" with the remaining bytes
|
||||||
|
set to 0. */
|
||||||
|
/* sizeof() = 160 (0xa0) bytes */
|
||||||
|
} __attribute__((__packed__)) LOG_CLIENT_RECORD;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* struct RECORD_PAGE_HEADER - Log page record page header.
|
||||||
|
*
|
||||||
|
* Each log page begins with this header and is followed by several LOG_RECORD
|
||||||
|
* structures, starting at offset 0x40 (the size of this structure and the
|
||||||
|
* following update sequence array and then aligned to 8 byte boundary, but is
|
||||||
|
* this specified anywhere?).
|
||||||
|
*/
|
||||||
|
typedef struct {
|
||||||
|
/* 0 NTFS_RECORD; -- Unfolded here as gcc doesn't like unnamed structs. */
|
||||||
|
NTFS_RECORD_TYPES magic;/* Usually the magic is "RCRD". */
|
||||||
|
u16 usa_ofs; /* See NTFS_RECORD definition in layout.h.
|
||||||
|
When creating, set this to be immediately
|
||||||
|
after this header structure (without any
|
||||||
|
alignment). */
|
||||||
|
u16 usa_count; /* See NTFS_RECORD definition in layout.h. */
|
||||||
|
|
||||||
|
union {
|
||||||
|
LSN last_lsn;
|
||||||
|
s64 file_offset;
|
||||||
|
} __attribute__((__packed__)) copy;
|
||||||
|
u32 flags;
|
||||||
|
u16 page_count;
|
||||||
|
u16 page_position;
|
||||||
|
union {
|
||||||
|
struct {
|
||||||
|
u16 next_record_offset;
|
||||||
|
u8 reserved[6];
|
||||||
|
LSN last_end_lsn;
|
||||||
|
} __attribute__((__packed__)) packed;
|
||||||
|
} __attribute__((__packed__)) header;
|
||||||
|
} __attribute__((__packed__)) RECORD_PAGE_HEADER;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* enum LOG_RECORD_FLAGS - Possible 16-bit flags for log records.
|
||||||
|
*
|
||||||
|
* (Or is it log record pages?)
|
||||||
|
*/
|
||||||
|
typedef enum {
|
||||||
|
LOG_RECORD_MULTI_PAGE = const_cpu_to_le16(0x0001), /* ??? */
|
||||||
|
LOG_RECORD_SIZE_PLACE_HOLDER = 0xffff,
|
||||||
|
/* This has nothing to do with the log record. It is only so
|
||||||
|
gcc knows to make the flags 16-bit. */
|
||||||
|
} __attribute__((__packed__)) LOG_RECORD_FLAGS;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* struct LOG_CLIENT_ID - The log client id structure identifying a log client.
|
||||||
|
*/
|
||||||
|
typedef struct {
|
||||||
|
u16 seq_number;
|
||||||
|
u16 client_index;
|
||||||
|
} __attribute__((__packed__)) LOG_CLIENT_ID;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* struct LOG_RECORD - Log record header.
|
||||||
|
*
|
||||||
|
* Each log record seems to have a constant size of 0x70 bytes.
|
||||||
|
*/
|
||||||
|
typedef struct {
|
||||||
|
LSN this_lsn;
|
||||||
|
LSN client_previous_lsn;
|
||||||
|
LSN client_undo_next_lsn;
|
||||||
|
u32 client_data_length;
|
||||||
|
LOG_CLIENT_ID client_id;
|
||||||
|
u32 record_type;
|
||||||
|
u32 transaction_id;
|
||||||
|
u16 flags;
|
||||||
|
u16 reserved_or_alignment[3];
|
||||||
|
/* Now are at ofs 0x30 into struct. */
|
||||||
|
u16 redo_operation;
|
||||||
|
u16 undo_operation;
|
||||||
|
u16 redo_offset;
|
||||||
|
u16 redo_length;
|
||||||
|
u16 undo_offset;
|
||||||
|
u16 undo_length;
|
||||||
|
u16 target_attribute;
|
||||||
|
u16 lcns_to_follow; /* Number of lcn_list entries
|
||||||
|
following this entry. */
|
||||||
|
/* Now at ofs 0x40. */
|
||||||
|
u16 record_offset;
|
||||||
|
u16 attribute_offset;
|
||||||
|
u32 alignment_or_reserved;
|
||||||
|
VCN target_vcn;
|
||||||
|
/* Now at ofs 0x50. */
|
||||||
|
struct { /* Only present if lcns_to_follow
|
||||||
|
is not 0. */
|
||||||
|
LCN lcn;
|
||||||
|
} __attribute__((__packed__)) lcn_list[0];
|
||||||
|
} __attribute__((__packed__)) LOG_RECORD;
|
||||||
|
|
||||||
|
extern BOOL ntfs_check_logfile(ntfs_attr *log_na, RESTART_PAGE_HEADER **rp);
|
||||||
|
extern BOOL ntfs_is_logfile_clean(ntfs_attr *log_na, RESTART_PAGE_HEADER *rp);
|
||||||
|
extern int ntfs_empty_logfile(ntfs_attr *na);
|
||||||
|
|
||||||
|
#endif /* defined _NTFS_LOGFILE_H */
|
613
source/libntfs/logging.c
Normal file
613
source/libntfs/logging.c
Normal file
@ -0,0 +1,613 @@
|
|||||||
|
/**
|
||||||
|
* logging.c - Centralised logging. Originated from the Linux-NTFS project.
|
||||||
|
*
|
||||||
|
* Copyright (c) 2005 Richard Russon
|
||||||
|
* Copyright (c) 2005-2008 Szabolcs Szakacsits
|
||||||
|
*
|
||||||
|
* This program/include file 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/include file 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
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifdef HAVE_CONFIG_H
|
||||||
|
#include "config.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef HAVE_STDIO_H
|
||||||
|
#include <stdio.h>
|
||||||
|
#endif
|
||||||
|
#ifdef HAVE_ERRNO_H
|
||||||
|
#include <errno.h>
|
||||||
|
#endif
|
||||||
|
#ifdef HAVE_STDARG_H
|
||||||
|
#include <stdarg.h>
|
||||||
|
#endif
|
||||||
|
#ifdef HAVE_STRING_H
|
||||||
|
#include <string.h>
|
||||||
|
#endif
|
||||||
|
#ifdef HAVE_STDLIB_H
|
||||||
|
#include <stdlib.h>
|
||||||
|
#endif
|
||||||
|
#ifdef HAVE_SYSLOG_H
|
||||||
|
#include <syslog.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "logging.h"
|
||||||
|
#include "misc.h"
|
||||||
|
|
||||||
|
#ifndef PATH_SEP
|
||||||
|
#define PATH_SEP '/'
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef DEBUG
|
||||||
|
static int tab;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Some gcc 3.x, 4.[01].X crash with internal compiler error. */
|
||||||
|
#if __GNUC__ <= 3 || (__GNUC__ == 4 && __GNUC_MINOR__ <= 1)
|
||||||
|
# define BROKEN_GCC_FORMAT_ATTRIBUTE
|
||||||
|
#else
|
||||||
|
# define BROKEN_GCC_FORMAT_ATTRIBUTE __attribute__((format(printf, 6, 0)))
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/**
|
||||||
|
* struct ntfs_logging - Control info for the logging system
|
||||||
|
* @levels: Bitfield of logging levels
|
||||||
|
* @flags: Flags which affect the output style
|
||||||
|
* @handler: Function to perform the actual logging
|
||||||
|
*/
|
||||||
|
struct ntfs_logging {
|
||||||
|
u32 levels;
|
||||||
|
u32 flags;
|
||||||
|
ntfs_log_handler *handler BROKEN_GCC_FORMAT_ATTRIBUTE;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ntfs_log
|
||||||
|
* This struct controls all the logging within the library and tools.
|
||||||
|
*/
|
||||||
|
static struct ntfs_logging ntfs_log = {
|
||||||
|
#ifdef DEBUG
|
||||||
|
NTFS_LOG_LEVEL_DEBUG | NTFS_LOG_LEVEL_TRACE | NTFS_LOG_LEVEL_ENTER |
|
||||||
|
NTFS_LOG_LEVEL_LEAVE |
|
||||||
|
#endif
|
||||||
|
NTFS_LOG_LEVEL_INFO | NTFS_LOG_LEVEL_QUIET | NTFS_LOG_LEVEL_WARNING |
|
||||||
|
NTFS_LOG_LEVEL_ERROR | NTFS_LOG_LEVEL_PERROR | NTFS_LOG_LEVEL_CRITICAL |
|
||||||
|
NTFS_LOG_LEVEL_PROGRESS,
|
||||||
|
NTFS_LOG_FLAG_ONLYNAME,
|
||||||
|
#ifdef DEBUG
|
||||||
|
ntfs_log_handler_outerr
|
||||||
|
#else
|
||||||
|
ntfs_log_handler_null
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ntfs_log_get_levels - Get a list of the current logging levels
|
||||||
|
*
|
||||||
|
* Find out which logging levels are enabled.
|
||||||
|
*
|
||||||
|
* Returns: Log levels in a 32-bit field
|
||||||
|
*/
|
||||||
|
u32 ntfs_log_get_levels(void)
|
||||||
|
{
|
||||||
|
return ntfs_log.levels;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ntfs_log_set_levels - Enable extra logging levels
|
||||||
|
* @levels: 32-bit field of log levels to set
|
||||||
|
*
|
||||||
|
* Enable one or more logging levels.
|
||||||
|
* The logging levels are named: NTFS_LOG_LEVEL_*.
|
||||||
|
*
|
||||||
|
* Returns: Log levels that were enabled before the call
|
||||||
|
*/
|
||||||
|
u32 ntfs_log_set_levels(u32 levels)
|
||||||
|
{
|
||||||
|
u32 old;
|
||||||
|
old = ntfs_log.levels;
|
||||||
|
ntfs_log.levels |= levels;
|
||||||
|
return old;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ntfs_log_clear_levels - Disable some logging levels
|
||||||
|
* @levels: 32-bit field of log levels to clear
|
||||||
|
*
|
||||||
|
* Disable one or more logging levels.
|
||||||
|
* The logging levels are named: NTFS_LOG_LEVEL_*.
|
||||||
|
*
|
||||||
|
* Returns: Log levels that were enabled before the call
|
||||||
|
*/
|
||||||
|
u32 ntfs_log_clear_levels(u32 levels)
|
||||||
|
{
|
||||||
|
u32 old;
|
||||||
|
old = ntfs_log.levels;
|
||||||
|
ntfs_log.levels &= (~levels);
|
||||||
|
return old;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ntfs_log_get_flags - Get a list of logging style flags
|
||||||
|
*
|
||||||
|
* Find out which logging flags are enabled.
|
||||||
|
*
|
||||||
|
* Returns: Logging flags in a 32-bit field
|
||||||
|
*/
|
||||||
|
u32 ntfs_log_get_flags(void)
|
||||||
|
{
|
||||||
|
return ntfs_log.flags;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ntfs_log_set_flags - Enable extra logging style flags
|
||||||
|
* @flags: 32-bit field of logging flags to set
|
||||||
|
*
|
||||||
|
* Enable one or more logging flags.
|
||||||
|
* The log flags are named: NTFS_LOG_LEVEL_*.
|
||||||
|
*
|
||||||
|
* Returns: Logging flags that were enabled before the call
|
||||||
|
*/
|
||||||
|
u32 ntfs_log_set_flags(u32 flags)
|
||||||
|
{
|
||||||
|
u32 old;
|
||||||
|
old = ntfs_log.flags;
|
||||||
|
ntfs_log.flags |= flags;
|
||||||
|
return old;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ntfs_log_clear_flags - Disable some logging styles
|
||||||
|
* @flags: 32-bit field of logging flags to clear
|
||||||
|
*
|
||||||
|
* Disable one or more logging flags.
|
||||||
|
* The log flags are named: NTFS_LOG_LEVEL_*.
|
||||||
|
*
|
||||||
|
* Returns: Logging flags that were enabled before the call
|
||||||
|
*/
|
||||||
|
u32 ntfs_log_clear_flags(u32 flags)
|
||||||
|
{
|
||||||
|
u32 old;
|
||||||
|
old = ntfs_log.flags;
|
||||||
|
ntfs_log.flags &= (~flags);
|
||||||
|
return old;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ntfs_log_get_stream - Default output streams for logging levels
|
||||||
|
* @level: Log level
|
||||||
|
*
|
||||||
|
* By default, urgent messages are sent to "stderr".
|
||||||
|
* Other messages are sent to "stdout".
|
||||||
|
*
|
||||||
|
* Returns: "string" Prefix to be used
|
||||||
|
*/
|
||||||
|
static FILE * ntfs_log_get_stream(u32 level)
|
||||||
|
{
|
||||||
|
FILE *stream;
|
||||||
|
|
||||||
|
switch (level) {
|
||||||
|
case NTFS_LOG_LEVEL_INFO:
|
||||||
|
case NTFS_LOG_LEVEL_QUIET:
|
||||||
|
case NTFS_LOG_LEVEL_PROGRESS:
|
||||||
|
case NTFS_LOG_LEVEL_VERBOSE:
|
||||||
|
stream = stdout;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case NTFS_LOG_LEVEL_DEBUG:
|
||||||
|
case NTFS_LOG_LEVEL_TRACE:
|
||||||
|
case NTFS_LOG_LEVEL_ENTER:
|
||||||
|
case NTFS_LOG_LEVEL_LEAVE:
|
||||||
|
case NTFS_LOG_LEVEL_WARNING:
|
||||||
|
case NTFS_LOG_LEVEL_ERROR:
|
||||||
|
case NTFS_LOG_LEVEL_CRITICAL:
|
||||||
|
case NTFS_LOG_LEVEL_PERROR:
|
||||||
|
default:
|
||||||
|
stream = stderr;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return stream;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ntfs_log_get_prefix - Default prefixes for logging levels
|
||||||
|
* @level: Log level to be prefixed
|
||||||
|
*
|
||||||
|
* Prefixing the logging output can make it easier to parse.
|
||||||
|
*
|
||||||
|
* Returns: "string" Prefix to be used
|
||||||
|
*/
|
||||||
|
static const char * ntfs_log_get_prefix(u32 level)
|
||||||
|
{
|
||||||
|
const char *prefix;
|
||||||
|
|
||||||
|
switch (level) {
|
||||||
|
case NTFS_LOG_LEVEL_DEBUG:
|
||||||
|
prefix = "DEBUG: ";
|
||||||
|
break;
|
||||||
|
case NTFS_LOG_LEVEL_TRACE:
|
||||||
|
prefix = "TRACE: ";
|
||||||
|
break;
|
||||||
|
case NTFS_LOG_LEVEL_QUIET:
|
||||||
|
prefix = "QUIET: ";
|
||||||
|
break;
|
||||||
|
case NTFS_LOG_LEVEL_INFO:
|
||||||
|
prefix = "INFO: ";
|
||||||
|
break;
|
||||||
|
case NTFS_LOG_LEVEL_VERBOSE:
|
||||||
|
prefix = "VERBOSE: ";
|
||||||
|
break;
|
||||||
|
case NTFS_LOG_LEVEL_PROGRESS:
|
||||||
|
prefix = "PROGRESS: ";
|
||||||
|
break;
|
||||||
|
case NTFS_LOG_LEVEL_WARNING:
|
||||||
|
prefix = "WARNING: ";
|
||||||
|
break;
|
||||||
|
case NTFS_LOG_LEVEL_ERROR:
|
||||||
|
prefix = "ERROR: ";
|
||||||
|
break;
|
||||||
|
case NTFS_LOG_LEVEL_PERROR:
|
||||||
|
prefix = "ERROR: ";
|
||||||
|
break;
|
||||||
|
case NTFS_LOG_LEVEL_CRITICAL:
|
||||||
|
prefix = "CRITICAL: ";
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
prefix = "";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return prefix;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ntfs_log_set_handler - Provide an alternate logging handler
|
||||||
|
* @handler: function to perform the logging
|
||||||
|
*
|
||||||
|
* This alternate handler will be called for all future logging requests.
|
||||||
|
* If no @handler is specified, logging will revert to the default handler.
|
||||||
|
*/
|
||||||
|
void ntfs_log_set_handler(ntfs_log_handler *handler)
|
||||||
|
{
|
||||||
|
if (handler) {
|
||||||
|
ntfs_log.handler = handler;
|
||||||
|
#ifdef HAVE_SYSLOG_H
|
||||||
|
if (handler == ntfs_log_handler_syslog)
|
||||||
|
openlog("ntfs-3g", LOG_PID, LOG_USER);
|
||||||
|
#endif
|
||||||
|
} else
|
||||||
|
ntfs_log.handler = ntfs_log_handler_null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ntfs_log_redirect - Pass on the request to the real handler
|
||||||
|
* @function: Function in which the log line occurred
|
||||||
|
* @file: File in which the log line occurred
|
||||||
|
* @line: Line number on which the log line occurred
|
||||||
|
* @level: Level at which the line is logged
|
||||||
|
* @data: User specified data, possibly specific to a handler
|
||||||
|
* @format: printf-style formatting string
|
||||||
|
* @...: Arguments to be formatted
|
||||||
|
*
|
||||||
|
* This is just a redirector function. The arguments are simply passed to the
|
||||||
|
* main logging handler (as defined in the global logging struct @ntfs_log).
|
||||||
|
*
|
||||||
|
* Returns: -1 Error occurred
|
||||||
|
* 0 Message wasn't logged
|
||||||
|
* num Number of output characters
|
||||||
|
*/
|
||||||
|
int ntfs_log_redirect(const char *function, const char *file,
|
||||||
|
int line, u32 level, void *data, const char *format, ...)
|
||||||
|
{
|
||||||
|
int olderr = errno;
|
||||||
|
int ret;
|
||||||
|
va_list args;
|
||||||
|
|
||||||
|
if (!(ntfs_log.levels & level)) /* Don't log this message */
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
va_start(args, format);
|
||||||
|
errno = olderr;
|
||||||
|
ret = ntfs_log.handler(function, file, line, level, data, format, args);
|
||||||
|
va_end(args);
|
||||||
|
|
||||||
|
errno = olderr;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ntfs_log_handler_syslog - syslog logging handler
|
||||||
|
* @function: Function in which the log line occurred
|
||||||
|
* @file: File in which the log line occurred
|
||||||
|
* @line: Line number on which the log line occurred
|
||||||
|
* @level: Level at which the line is logged
|
||||||
|
* @data: User specified data, possibly specific to a handler
|
||||||
|
* @format: printf-style formatting string
|
||||||
|
* @args: Arguments to be formatted
|
||||||
|
*
|
||||||
|
* A simple syslog logging handler. Ignores colors.
|
||||||
|
*
|
||||||
|
* Returns: -1 Error occurred
|
||||||
|
* 0 Message wasn't logged
|
||||||
|
* num Number of output characters
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef HAVE_SYSLOG_H
|
||||||
|
|
||||||
|
#define LOG_LINE_LEN 512
|
||||||
|
|
||||||
|
int ntfs_log_handler_syslog(const char *function __attribute__((unused)),
|
||||||
|
const char *file __attribute__((unused)),
|
||||||
|
int line __attribute__((unused)), u32 level,
|
||||||
|
void *data __attribute__((unused)),
|
||||||
|
const char *format, va_list args)
|
||||||
|
{
|
||||||
|
char logbuf[LOG_LINE_LEN];
|
||||||
|
int ret, olderr = errno;
|
||||||
|
|
||||||
|
#ifndef DEBUG
|
||||||
|
if ((level & NTFS_LOG_LEVEL_PERROR) && errno == ENOSPC)
|
||||||
|
return 1;
|
||||||
|
#endif
|
||||||
|
ret = vsnprintf(logbuf, LOG_LINE_LEN, format, args);
|
||||||
|
if (ret < 0) {
|
||||||
|
vsyslog(LOG_NOTICE, format, args);
|
||||||
|
ret = 1;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((LOG_LINE_LEN > ret + 3) && (level & NTFS_LOG_LEVEL_PERROR)) {
|
||||||
|
strncat(logbuf, ": ", LOG_LINE_LEN - ret - 1);
|
||||||
|
strncat(logbuf, strerror(olderr), LOG_LINE_LEN - (ret + 3));
|
||||||
|
ret = strlen(logbuf);
|
||||||
|
}
|
||||||
|
|
||||||
|
syslog(LOG_NOTICE, "%s", logbuf);
|
||||||
|
out:
|
||||||
|
errno = olderr;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ntfs_log_handler_fprintf - Basic logging handler
|
||||||
|
* @function: Function in which the log line occurred
|
||||||
|
* @file: File in which the log line occurred
|
||||||
|
* @line: Line number on which the log line occurred
|
||||||
|
* @level: Level at which the line is logged
|
||||||
|
* @data: User specified data, possibly specific to a handler
|
||||||
|
* @format: printf-style formatting string
|
||||||
|
* @args: Arguments to be formatted
|
||||||
|
*
|
||||||
|
* A simple logging handler. This is where the log line is finally displayed.
|
||||||
|
* It is more likely that you will want to set the handler to either
|
||||||
|
* ntfs_log_handler_outerr or ntfs_log_handler_stderr.
|
||||||
|
*
|
||||||
|
* Note: For this handler, @data is a pointer to a FILE output stream.
|
||||||
|
* If @data is NULL, nothing will be displayed.
|
||||||
|
*
|
||||||
|
* Returns: -1 Error occurred
|
||||||
|
* 0 Message wasn't logged
|
||||||
|
* num Number of output characters
|
||||||
|
*/
|
||||||
|
int ntfs_log_handler_fprintf(const char *function, const char *file,
|
||||||
|
int line, u32 level, void *data, const char *format, va_list args)
|
||||||
|
{
|
||||||
|
#ifdef DEBUG
|
||||||
|
int i;
|
||||||
|
#endif
|
||||||
|
int ret = 0;
|
||||||
|
int olderr = errno;
|
||||||
|
FILE *stream;
|
||||||
|
|
||||||
|
if (!data) /* Interpret data as a FILE stream. */
|
||||||
|
return 0; /* If it's NULL, we can't do anything. */
|
||||||
|
stream = (FILE*)data;
|
||||||
|
|
||||||
|
#ifdef DEBUG
|
||||||
|
if (level == NTFS_LOG_LEVEL_LEAVE) {
|
||||||
|
if (tab)
|
||||||
|
tab--;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < tab; i++)
|
||||||
|
ret += fprintf(stream, " ");
|
||||||
|
#endif
|
||||||
|
if ((ntfs_log.flags & NTFS_LOG_FLAG_ONLYNAME) &&
|
||||||
|
(strchr(file, PATH_SEP))) /* Abbreviate the filename */
|
||||||
|
file = strrchr(file, PATH_SEP) + 1;
|
||||||
|
|
||||||
|
if (ntfs_log.flags & NTFS_LOG_FLAG_PREFIX) /* Prefix the output */
|
||||||
|
ret += fprintf(stream, "%s", ntfs_log_get_prefix(level));
|
||||||
|
|
||||||
|
if (ntfs_log.flags & NTFS_LOG_FLAG_FILENAME) /* Source filename */
|
||||||
|
ret += fprintf(stream, "%s ", file);
|
||||||
|
|
||||||
|
if (ntfs_log.flags & NTFS_LOG_FLAG_LINE) /* Source line number */
|
||||||
|
ret += fprintf(stream, "(%d) ", line);
|
||||||
|
|
||||||
|
if ((ntfs_log.flags & NTFS_LOG_FLAG_FUNCTION) || /* Source function */
|
||||||
|
(level & NTFS_LOG_LEVEL_TRACE) || (level & NTFS_LOG_LEVEL_ENTER))
|
||||||
|
ret += fprintf(stream, "%s(): ", function);
|
||||||
|
|
||||||
|
ret += vfprintf(stream, format, args);
|
||||||
|
|
||||||
|
if (level & NTFS_LOG_LEVEL_PERROR)
|
||||||
|
ret += fprintf(stream, ": %s\n", strerror(olderr));
|
||||||
|
|
||||||
|
#ifdef DEBUG
|
||||||
|
if (level == NTFS_LOG_LEVEL_ENTER)
|
||||||
|
tab++;
|
||||||
|
#endif
|
||||||
|
fflush(stream);
|
||||||
|
errno = olderr;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ntfs_log_handler_null - Null logging handler (no output)
|
||||||
|
* @function: Function in which the log line occurred
|
||||||
|
* @file: File in which the log line occurred
|
||||||
|
* @line: Line number on which the log line occurred
|
||||||
|
* @level: Level at which the line is logged
|
||||||
|
* @data: User specified data, possibly specific to a handler
|
||||||
|
* @format: printf-style formatting string
|
||||||
|
* @args: Arguments to be formatted
|
||||||
|
*
|
||||||
|
* This handler produces no output. It provides a way to temporarily disable
|
||||||
|
* logging, without having to change the levels and flags.
|
||||||
|
*
|
||||||
|
* Returns: 0 Message wasn't logged
|
||||||
|
*/
|
||||||
|
int ntfs_log_handler_null(const char *function __attribute__((unused)), const char *file __attribute__((unused)),
|
||||||
|
int line __attribute__((unused)), u32 level __attribute__((unused)), void *data __attribute__((unused)),
|
||||||
|
const char *format __attribute__((unused)), va_list args __attribute__((unused)))
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ntfs_log_handler_stdout - All logs go to stdout
|
||||||
|
* @function: Function in which the log line occurred
|
||||||
|
* @file: File in which the log line occurred
|
||||||
|
* @line: Line number on which the log line occurred
|
||||||
|
* @level: Level at which the line is logged
|
||||||
|
* @data: User specified data, possibly specific to a handler
|
||||||
|
* @format: printf-style formatting string
|
||||||
|
* @args: Arguments to be formatted
|
||||||
|
*
|
||||||
|
* Display a log message to stdout.
|
||||||
|
*
|
||||||
|
* Note: For this handler, @data is a pointer to a FILE output stream.
|
||||||
|
* If @data is NULL, then stdout will be used.
|
||||||
|
*
|
||||||
|
* Note: This function calls ntfs_log_handler_fprintf to do the main work.
|
||||||
|
*
|
||||||
|
* Returns: -1 Error occurred
|
||||||
|
* 0 Message wasn't logged
|
||||||
|
* num Number of output characters
|
||||||
|
*/
|
||||||
|
int ntfs_log_handler_stdout(const char *function, const char *file,
|
||||||
|
int line, u32 level, void *data, const char *format, va_list args)
|
||||||
|
{
|
||||||
|
if (!data)
|
||||||
|
data = stdout;
|
||||||
|
|
||||||
|
return ntfs_log_handler_fprintf(function, file, line, level, data, format, args);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ntfs_log_handler_outerr - Logs go to stdout/stderr depending on level
|
||||||
|
* @function: Function in which the log line occurred
|
||||||
|
* @file: File in which the log line occurred
|
||||||
|
* @line: Line number on which the log line occurred
|
||||||
|
* @level: Level at which the line is logged
|
||||||
|
* @data: User specified data, possibly specific to a handler
|
||||||
|
* @format: printf-style formatting string
|
||||||
|
* @args: Arguments to be formatted
|
||||||
|
*
|
||||||
|
* Display a log message. The output stream will be determined by the log
|
||||||
|
* level.
|
||||||
|
*
|
||||||
|
* Note: For this handler, @data is a pointer to a FILE output stream.
|
||||||
|
* If @data is NULL, the function ntfs_log_get_stream will be called
|
||||||
|
*
|
||||||
|
* Note: This function calls ntfs_log_handler_fprintf to do the main work.
|
||||||
|
*
|
||||||
|
* Returns: -1 Error occurred
|
||||||
|
* 0 Message wasn't logged
|
||||||
|
* num Number of output characters
|
||||||
|
*/
|
||||||
|
int ntfs_log_handler_outerr(const char *function, const char *file,
|
||||||
|
int line, u32 level, void *data, const char *format, va_list args)
|
||||||
|
{
|
||||||
|
if (!data)
|
||||||
|
data = ntfs_log_get_stream(level);
|
||||||
|
|
||||||
|
return ntfs_log_handler_fprintf(function, file, line, level, data, format, args);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ntfs_log_handler_stderr - All logs go to stderr
|
||||||
|
* @function: Function in which the log line occurred
|
||||||
|
* @file: File in which the log line occurred
|
||||||
|
* @line: Line number on which the log line occurred
|
||||||
|
* @level: Level at which the line is logged
|
||||||
|
* @data: User specified data, possibly specific to a handler
|
||||||
|
* @format: printf-style formatting string
|
||||||
|
* @args: Arguments to be formatted
|
||||||
|
*
|
||||||
|
* Display a log message to stderr.
|
||||||
|
*
|
||||||
|
* Note: For this handler, @data is a pointer to a FILE output stream.
|
||||||
|
* If @data is NULL, then stdout will be used.
|
||||||
|
*
|
||||||
|
* Note: This function calls ntfs_log_handler_fprintf to do the main work.
|
||||||
|
*
|
||||||
|
* Returns: -1 Error occurred
|
||||||
|
* 0 Message wasn't logged
|
||||||
|
* num Number of output characters
|
||||||
|
*/
|
||||||
|
int ntfs_log_handler_stderr(const char *function, const char *file,
|
||||||
|
int line, u32 level, void *data, const char *format, va_list args)
|
||||||
|
{
|
||||||
|
if (!data)
|
||||||
|
data = stderr;
|
||||||
|
|
||||||
|
return ntfs_log_handler_fprintf(function, file, line, level, data, format, args);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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
|
||||||
|
*/
|
||||||
|
BOOL 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;
|
||||||
|
}
|
||||||
|
|
118
source/libntfs/logging.h
Normal file
118
source/libntfs/logging.h
Normal file
@ -0,0 +1,118 @@
|
|||||||
|
/*
|
||||||
|
* logging.h - Centralised logging. Originated from the Linux-NTFS project.
|
||||||
|
*
|
||||||
|
* Copyright (c) 2005 Richard Russon
|
||||||
|
* Copyright (c) 2007-2008 Szabolcs Szakacsits
|
||||||
|
*
|
||||||
|
* This program/include file 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/include file 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
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _LOGGING_H_
|
||||||
|
#define _LOGGING_H_
|
||||||
|
|
||||||
|
#ifdef HAVE_CONFIG_H
|
||||||
|
#include "config.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef HAVE_STDARG_H
|
||||||
|
#include <stdarg.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "types.h"
|
||||||
|
|
||||||
|
/* Function prototype for the logging handlers */
|
||||||
|
typedef int (ntfs_log_handler)(const char *function, const char *file, int line,
|
||||||
|
u32 level, void *data, const char *format, va_list args);
|
||||||
|
|
||||||
|
/* Set the logging handler from one of the functions, below. */
|
||||||
|
void ntfs_log_set_handler(ntfs_log_handler *handler
|
||||||
|
__attribute__((format(printf, 6, 0))));
|
||||||
|
|
||||||
|
/* Logging handlers */
|
||||||
|
ntfs_log_handler ntfs_log_handler_syslog __attribute__((format(printf, 6, 0)));
|
||||||
|
ntfs_log_handler ntfs_log_handler_fprintf __attribute__((format(printf, 6, 0)));
|
||||||
|
ntfs_log_handler ntfs_log_handler_null __attribute__((format(printf, 6, 0)));
|
||||||
|
ntfs_log_handler ntfs_log_handler_stdout __attribute__((format(printf, 6, 0)));
|
||||||
|
ntfs_log_handler ntfs_log_handler_outerr __attribute__((format(printf, 6, 0)));
|
||||||
|
ntfs_log_handler ntfs_log_handler_stderr __attribute__((format(printf, 6, 0)));
|
||||||
|
|
||||||
|
/* Enable/disable certain log levels */
|
||||||
|
u32 ntfs_log_set_levels(u32 levels);
|
||||||
|
u32 ntfs_log_clear_levels(u32 levels);
|
||||||
|
u32 ntfs_log_get_levels(void);
|
||||||
|
|
||||||
|
/* Enable/disable certain log flags */
|
||||||
|
u32 ntfs_log_set_flags(u32 flags);
|
||||||
|
u32 ntfs_log_clear_flags(u32 flags);
|
||||||
|
u32 ntfs_log_get_flags(void);
|
||||||
|
|
||||||
|
/* Turn command-line options into logging flags */
|
||||||
|
BOOL ntfs_log_parse_option(const char *option);
|
||||||
|
|
||||||
|
int ntfs_log_redirect(const char *function, const char *file, int line,
|
||||||
|
u32 level, void *data, const char *format, ...)
|
||||||
|
__attribute__((format(printf, 6, 7)));
|
||||||
|
|
||||||
|
/* Logging levels - Determine what gets logged */
|
||||||
|
#define NTFS_LOG_LEVEL_DEBUG (1 << 0) /* x = 42 */
|
||||||
|
#define NTFS_LOG_LEVEL_TRACE (1 << 1) /* Entering function x() */
|
||||||
|
#define NTFS_LOG_LEVEL_QUIET (1 << 2) /* Quietable output */
|
||||||
|
#define NTFS_LOG_LEVEL_INFO (1 << 3) /* Volume needs defragmenting */
|
||||||
|
#define NTFS_LOG_LEVEL_VERBOSE (1 << 4) /* Forced to continue */
|
||||||
|
#define NTFS_LOG_LEVEL_PROGRESS (1 << 5) /* 54% complete */
|
||||||
|
#define NTFS_LOG_LEVEL_WARNING (1 << 6) /* You should backup before starting */
|
||||||
|
#define NTFS_LOG_LEVEL_ERROR (1 << 7) /* Operation failed, no damage done */
|
||||||
|
#define NTFS_LOG_LEVEL_PERROR (1 << 8) /* Message : standard error description */
|
||||||
|
#define NTFS_LOG_LEVEL_CRITICAL (1 << 9) /* Operation failed,damage may have occurred */
|
||||||
|
#define NTFS_LOG_LEVEL_ENTER (1 << 10) /* Enter a function */
|
||||||
|
#define NTFS_LOG_LEVEL_LEAVE (1 << 11) /* Leave a function */
|
||||||
|
|
||||||
|
/* Logging style flags - Manage the style of the output */
|
||||||
|
#define NTFS_LOG_FLAG_PREFIX (1 << 0) /* Prefix messages with "ERROR: ", etc */
|
||||||
|
#define NTFS_LOG_FLAG_FILENAME (1 << 1) /* Show the file origin of the message */
|
||||||
|
#define NTFS_LOG_FLAG_LINE (1 << 2) /* Show the line number of the message */
|
||||||
|
#define NTFS_LOG_FLAG_FUNCTION (1 << 3) /* Show the function name containing the message */
|
||||||
|
#define NTFS_LOG_FLAG_ONLYNAME (1 << 4) /* Only display the filename, not the pathname */
|
||||||
|
|
||||||
|
/* Macros to simplify logging. One for each level defined above.
|
||||||
|
* Note, ntfs_log_debug/trace have effect only if DEBUG is defined.
|
||||||
|
*/
|
||||||
|
#define ntfs_log_critical(FORMAT, ARGS...) ntfs_log_redirect(__FUNCTION__,__FILE__,__LINE__,NTFS_LOG_LEVEL_CRITICAL,NULL,FORMAT,##ARGS)
|
||||||
|
#define ntfs_log_error(FORMAT, ARGS...) ntfs_log_redirect(__FUNCTION__,__FILE__,__LINE__,NTFS_LOG_LEVEL_ERROR,NULL,FORMAT,##ARGS)
|
||||||
|
#define ntfs_log_info(FORMAT, ARGS...) ntfs_log_redirect(__FUNCTION__,__FILE__,__LINE__,NTFS_LOG_LEVEL_INFO,NULL,FORMAT,##ARGS)
|
||||||
|
#define ntfs_log_perror(FORMAT, ARGS...) ntfs_log_redirect(__FUNCTION__,__FILE__,__LINE__,NTFS_LOG_LEVEL_PERROR,NULL,FORMAT,##ARGS)
|
||||||
|
#define ntfs_log_progress(FORMAT, ARGS...) ntfs_log_redirect(__FUNCTION__,__FILE__,__LINE__,NTFS_LOG_LEVEL_PROGRESS,NULL,FORMAT,##ARGS)
|
||||||
|
#define ntfs_log_quiet(FORMAT, ARGS...) ntfs_log_redirect(__FUNCTION__,__FILE__,__LINE__,NTFS_LOG_LEVEL_QUIET,NULL,FORMAT,##ARGS)
|
||||||
|
#define ntfs_log_verbose(FORMAT, ARGS...) ntfs_log_redirect(__FUNCTION__,__FILE__,__LINE__,NTFS_LOG_LEVEL_VERBOSE,NULL,FORMAT,##ARGS)
|
||||||
|
#define ntfs_log_warning(FORMAT, ARGS...) ntfs_log_redirect(__FUNCTION__,__FILE__,__LINE__,NTFS_LOG_LEVEL_WARNING,NULL,FORMAT,##ARGS)
|
||||||
|
|
||||||
|
/* By default debug and trace messages are compiled into the program,
|
||||||
|
* but not displayed.
|
||||||
|
*/
|
||||||
|
#ifdef DEBUG
|
||||||
|
#define ntfs_log_debug(FORMAT, ARGS...) ntfs_log_redirect(__FUNCTION__,__FILE__,__LINE__,NTFS_LOG_LEVEL_DEBUG,NULL,FORMAT,##ARGS)
|
||||||
|
#define ntfs_log_trace(FORMAT, ARGS...) ntfs_log_redirect(__FUNCTION__,__FILE__,__LINE__,NTFS_LOG_LEVEL_TRACE,NULL,FORMAT,##ARGS)
|
||||||
|
#define ntfs_log_enter(FORMAT, ARGS...) ntfs_log_redirect(__FUNCTION__,__FILE__,__LINE__,NTFS_LOG_LEVEL_ENTER,NULL,FORMAT,##ARGS)
|
||||||
|
#define ntfs_log_leave(FORMAT, ARGS...) ntfs_log_redirect(__FUNCTION__,__FILE__,__LINE__,NTFS_LOG_LEVEL_LEAVE,NULL,FORMAT,##ARGS)
|
||||||
|
#else
|
||||||
|
#define ntfs_log_debug(FORMAT, ARGS...)do {} while (0)
|
||||||
|
#define ntfs_log_trace(FORMAT, ARGS...)do {} while (0)
|
||||||
|
#define ntfs_log_enter(FORMAT, ARGS...)do {} while (0)
|
||||||
|
#define ntfs_log_leave(FORMAT, ARGS...)do {} while (0)
|
||||||
|
#endif /* DEBUG */
|
||||||
|
|
||||||
|
#endif /* _LOGGING_H_ */
|
||||||
|
|
55
source/libntfs/mem_allocate.h
Normal file
55
source/libntfs/mem_allocate.h
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
/**
|
||||||
|
* mem_allocate.h - Memory allocation and destruction calls.
|
||||||
|
*
|
||||||
|
* Copyright (c) 2009 Rhys "Shareese" Koedijk
|
||||||
|
* Copyright (c) 2006 Michael "Chishm" Chisholm
|
||||||
|
*
|
||||||
|
* This program/include file 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/include file 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; if not, write to the Free Software Foundation,
|
||||||
|
* Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _MEM_ALLOCATE_H
|
||||||
|
#define _MEM_ALLOCATE_H
|
||||||
|
|
||||||
|
#include <malloc.h>
|
||||||
|
|
||||||
|
#ifdef _NTFS_SYS_MEM_ALLOC
|
||||||
|
|
||||||
|
static inline void* ntfs_alloc (size_t size) {
|
||||||
|
return malloc(size);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void* ntfs_align (size_t size) {
|
||||||
|
#ifdef __wii__
|
||||||
|
return memalign(32, size);
|
||||||
|
#else
|
||||||
|
return malloc(size);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void ntfs_free (void* mem) {
|
||||||
|
free(mem);
|
||||||
|
}
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
void* ntfs_alloc (size_t size);
|
||||||
|
void* ntfs_align (size_t size);
|
||||||
|
void ntfs_free (void* mem);
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#endif /* _MEM_ALLOCATE_H */
|
1904
source/libntfs/mft.c
Normal file
1904
source/libntfs/mft.c
Normal file
File diff suppressed because it is too large
Load Diff
132
source/libntfs/mft.h
Normal file
132
source/libntfs/mft.h
Normal file
@ -0,0 +1,132 @@
|
|||||||
|
/*
|
||||||
|
* mft.h - Exports for MFT record handling. Originated from the Linux-NTFS project.
|
||||||
|
*
|
||||||
|
* Copyright (c) 2000-2002 Anton Altaparmakov
|
||||||
|
* Copyright (c) 2004-2005 Richard Russon
|
||||||
|
* Copyright (c) 2006-2008 Szabolcs Szakacsits
|
||||||
|
*
|
||||||
|
* This program/include file 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/include file 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
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _NTFS_MFT_H
|
||||||
|
#define _NTFS_MFT_H
|
||||||
|
|
||||||
|
#include "volume.h"
|
||||||
|
#include "inode.h"
|
||||||
|
#include "layout.h"
|
||||||
|
#include "logging.h"
|
||||||
|
|
||||||
|
extern int ntfs_mft_records_read(const ntfs_volume *vol, const MFT_REF mref,
|
||||||
|
const s64 count, MFT_RECORD *b);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ntfs_mft_record_read - read a record from the mft
|
||||||
|
* @vol: volume to read from
|
||||||
|
* @mref: mft record number to read
|
||||||
|
* @b: output data buffer
|
||||||
|
*
|
||||||
|
* Read the mft record specified by @mref from volume @vol into buffer @b.
|
||||||
|
* Return 0 on success or -1 on error, with errno set to the error code.
|
||||||
|
*
|
||||||
|
* The read mft record is mst deprotected and is hence ready to use. The caller
|
||||||
|
* should check the record with is_baad_record() in case mst deprotection
|
||||||
|
* failed.
|
||||||
|
*
|
||||||
|
* NOTE: @b has to be at least of size vol->mft_record_size.
|
||||||
|
*/
|
||||||
|
static __inline__ int ntfs_mft_record_read(const ntfs_volume *vol,
|
||||||
|
const MFT_REF mref, MFT_RECORD *b)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
ntfs_log_enter("Entering for inode %lld\n", (long long)MREF(mref));
|
||||||
|
ret = ntfs_mft_records_read(vol, mref, 1, b);
|
||||||
|
ntfs_log_leave("\n");
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
extern int ntfs_mft_record_check(const ntfs_volume *vol, const MFT_REF mref,
|
||||||
|
MFT_RECORD *m);
|
||||||
|
|
||||||
|
extern int ntfs_file_record_read(const ntfs_volume *vol, const MFT_REF mref,
|
||||||
|
MFT_RECORD **mrec, ATTR_RECORD **attr);
|
||||||
|
|
||||||
|
extern int ntfs_mft_records_write(const ntfs_volume *vol, const MFT_REF mref,
|
||||||
|
const s64 count, MFT_RECORD *b);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ntfs_mft_record_write - write an mft record to disk
|
||||||
|
* @vol: volume to write to
|
||||||
|
* @mref: mft record number to write
|
||||||
|
* @b: data buffer containing the mft record to write
|
||||||
|
*
|
||||||
|
* Write the mft record specified by @mref from buffer @b to volume @vol.
|
||||||
|
* Return 0 on success or -1 on error, with errno set to the error code.
|
||||||
|
*
|
||||||
|
* Before the mft record is written, it is mst protected. After the write, it
|
||||||
|
* is deprotected again, thus resulting in an increase in the update sequence
|
||||||
|
* number inside the buffer @b.
|
||||||
|
*
|
||||||
|
* NOTE: @b has to be at least of size vol->mft_record_size.
|
||||||
|
*/
|
||||||
|
static __inline__ int ntfs_mft_record_write(const ntfs_volume *vol,
|
||||||
|
const MFT_REF mref, MFT_RECORD *b)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
ntfs_log_enter("Entering for inode %lld\n", (long long)MREF(mref));
|
||||||
|
ret = ntfs_mft_records_write(vol, mref, 1, b);
|
||||||
|
ntfs_log_leave("\n");
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ntfs_mft_record_get_data_size - return number of bytes used in mft record @b
|
||||||
|
* @m: mft record to get the data size of
|
||||||
|
*
|
||||||
|
* Takes the mft record @m and returns the number of bytes used in the record
|
||||||
|
* or 0 on error (i.e. @m is not a valid mft record). Zero is not a valid size
|
||||||
|
* for an mft record as it at least has to have the MFT_RECORD itself and a
|
||||||
|
* zero length attribute of type AT_END, thus making the minimum size 56 bytes.
|
||||||
|
*
|
||||||
|
* Aside: The size is independent of NTFS versions 1.x/3.x because the 8-byte
|
||||||
|
* alignment of the first attribute mask the difference in MFT_RECORD size
|
||||||
|
* between NTFS 1.x and 3.x. Also, you would expect every mft record to
|
||||||
|
* contain an update sequence array as well but that could in theory be
|
||||||
|
* non-existent (don't know if Windows' NTFS driver/chkdsk wouldn't view this
|
||||||
|
* as corruption in itself though).
|
||||||
|
*/
|
||||||
|
static __inline__ u32 ntfs_mft_record_get_data_size(const MFT_RECORD *m)
|
||||||
|
{
|
||||||
|
if (!m || !ntfs_is_mft_record(m->magic))
|
||||||
|
return 0;
|
||||||
|
/* Get the number of used bytes and return it. */
|
||||||
|
return le32_to_cpu(m->bytes_in_use);
|
||||||
|
}
|
||||||
|
|
||||||
|
extern int ntfs_mft_record_layout(const ntfs_volume *vol, const MFT_REF mref,
|
||||||
|
MFT_RECORD *mrec);
|
||||||
|
|
||||||
|
extern int ntfs_mft_record_format(const ntfs_volume *vol, const MFT_REF mref);
|
||||||
|
|
||||||
|
extern ntfs_inode *ntfs_mft_record_alloc(ntfs_volume *vol, ntfs_inode *base_ni);
|
||||||
|
|
||||||
|
extern int ntfs_mft_record_free(ntfs_volume *vol, ntfs_inode *ni);
|
||||||
|
|
||||||
|
extern int ntfs_mft_usn_dec(MFT_RECORD *mrec);
|
||||||
|
|
||||||
|
#endif /* defined _NTFS_MFT_H */
|
||||||
|
|
364
source/libntfs/misc.c
Normal file
364
source/libntfs/misc.c
Normal file
@ -0,0 +1,364 @@
|
|||||||
|
/**
|
||||||
|
* misc.c : miscellaneous :
|
||||||
|
* - dealing with errors in memory allocation
|
||||||
|
* - data caching
|
||||||
|
*
|
||||||
|
* Copyright (c) 2008 Jean-Pierre Andre
|
||||||
|
*
|
||||||
|
* This program/include file 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/include file 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
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifdef HAVE_CONFIG_H
|
||||||
|
#include "config.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef HAVE_STDLIB_H
|
||||||
|
#include <stdlib.h>
|
||||||
|
#endif
|
||||||
|
#ifdef HAVE_STRING_H
|
||||||
|
#include <string.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "types.h"
|
||||||
|
#include "security.h"
|
||||||
|
#include "misc.h"
|
||||||
|
#include "logging.h"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ntfs_calloc
|
||||||
|
*
|
||||||
|
* Return a pointer to the allocated memory or NULL if the request fails.
|
||||||
|
*/
|
||||||
|
void *ntfs_calloc(size_t size)
|
||||||
|
{
|
||||||
|
void *p;
|
||||||
|
|
||||||
|
p = calloc(1, size);
|
||||||
|
if (!p)
|
||||||
|
ntfs_log_perror("Failed to calloc %lld bytes", (long long)size);
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
|
||||||
|
void *ntfs_malloc(size_t size)
|
||||||
|
{
|
||||||
|
void *p;
|
||||||
|
|
||||||
|
p = malloc(size);
|
||||||
|
if (!p)
|
||||||
|
ntfs_log_perror("Failed to malloc %lld bytes", (long long)size);
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* General functions to deal with LRU caches
|
||||||
|
*
|
||||||
|
* The cached data have to be organized in a structure in which
|
||||||
|
* the first fields must follow a mandatory pattern and further
|
||||||
|
* fields may contain any fixed size data. They are stored in an
|
||||||
|
* LRU list.
|
||||||
|
*
|
||||||
|
* A compare function must be provided for finding a wanted entry
|
||||||
|
* in the cache. Another function may be provided for invalidating
|
||||||
|
* an entry to facilitate multiple invalidation.
|
||||||
|
*
|
||||||
|
* These functions never return error codes. When there is a
|
||||||
|
* shortage of memory, data is simply not cached.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Fetch an entry from cache
|
||||||
|
*
|
||||||
|
* returns the cache entry, or NULL if not available
|
||||||
|
*/
|
||||||
|
|
||||||
|
struct CACHED_GENERIC *ntfs_fetch_cache(struct CACHE_HEADER *cache,
|
||||||
|
const struct CACHED_GENERIC *wanted, cache_compare compare)
|
||||||
|
{
|
||||||
|
struct CACHED_GENERIC *current;
|
||||||
|
struct CACHED_GENERIC *previous;
|
||||||
|
|
||||||
|
current = (struct CACHED_GENERIC*)NULL;
|
||||||
|
if (cache) {
|
||||||
|
/*
|
||||||
|
* Search sequentially in LRU list
|
||||||
|
*/
|
||||||
|
current = cache->most_recent_entry;
|
||||||
|
previous = (struct CACHED_GENERIC*)NULL;
|
||||||
|
while (current
|
||||||
|
&& compare(current, wanted)) {
|
||||||
|
previous = current;
|
||||||
|
current = current->next;
|
||||||
|
}
|
||||||
|
if (current)
|
||||||
|
cache->hits++;
|
||||||
|
if (current && previous) {
|
||||||
|
/*
|
||||||
|
* found and not at head of list, unlink from current
|
||||||
|
* position and relink as head of list
|
||||||
|
*/
|
||||||
|
previous->next = current->next;
|
||||||
|
current->next = cache->most_recent_entry;
|
||||||
|
cache->most_recent_entry = current;
|
||||||
|
}
|
||||||
|
cache->reads++;
|
||||||
|
}
|
||||||
|
return (current);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Enter an inode number into cache
|
||||||
|
* returns the cache entry or NULL if not possible
|
||||||
|
*/
|
||||||
|
|
||||||
|
struct CACHED_GENERIC *ntfs_enter_cache(struct CACHE_HEADER *cache,
|
||||||
|
const struct CACHED_GENERIC *item, cache_compare compare)
|
||||||
|
{
|
||||||
|
struct CACHED_GENERIC *current;
|
||||||
|
struct CACHED_GENERIC *previous;
|
||||||
|
struct CACHED_GENERIC *before;
|
||||||
|
|
||||||
|
current = (struct CACHED_GENERIC*)NULL;
|
||||||
|
if (cache) {
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Search sequentially in LRU list to locate the end,
|
||||||
|
* and find out whether the entry is already in list
|
||||||
|
* As we normally go to the end, no statistics is
|
||||||
|
* kept.
|
||||||
|
*/
|
||||||
|
current = cache->most_recent_entry;
|
||||||
|
previous = (struct CACHED_GENERIC*)NULL;
|
||||||
|
before = (struct CACHED_GENERIC*)NULL;
|
||||||
|
while (current
|
||||||
|
&& compare(current, item)) {
|
||||||
|
before = previous;
|
||||||
|
previous = current;
|
||||||
|
current = current->next;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!current) {
|
||||||
|
/*
|
||||||
|
* Not in list, get a free entry or reuse the
|
||||||
|
* last entry, and relink as head of list
|
||||||
|
* Note : we assume at least three entries, so
|
||||||
|
* before, previous and first are different when
|
||||||
|
* an entry is reused.
|
||||||
|
*/
|
||||||
|
|
||||||
|
if (cache->free_entry) {
|
||||||
|
current = cache->free_entry;
|
||||||
|
cache->free_entry = cache->free_entry->next;
|
||||||
|
if (item->varsize) {
|
||||||
|
current->variable = ntfs_malloc(
|
||||||
|
item->varsize);
|
||||||
|
} else
|
||||||
|
current->variable = (void*)NULL;
|
||||||
|
current->varsize = item->varsize;
|
||||||
|
} else {
|
||||||
|
before->next = (struct CACHED_GENERIC*)NULL;
|
||||||
|
current = previous;
|
||||||
|
if (item->varsize) {
|
||||||
|
if (current->varsize)
|
||||||
|
current->variable = realloc(
|
||||||
|
current->variable,
|
||||||
|
item->varsize);
|
||||||
|
else
|
||||||
|
current->variable = ntfs_malloc(
|
||||||
|
item->varsize);
|
||||||
|
} else {
|
||||||
|
if (current->varsize)
|
||||||
|
free(current->variable);
|
||||||
|
current->variable = (void*)NULL;
|
||||||
|
}
|
||||||
|
current->varsize = item->varsize;
|
||||||
|
}
|
||||||
|
current->next = cache->most_recent_entry;
|
||||||
|
cache->most_recent_entry = current;
|
||||||
|
memcpy(current->fixed, item->fixed, cache->fixed_size);
|
||||||
|
if (item->varsize) {
|
||||||
|
if (current->variable) {
|
||||||
|
memcpy(current->variable,
|
||||||
|
item->variable, item->varsize);
|
||||||
|
} else {
|
||||||
|
/*
|
||||||
|
* no more memory for variable part
|
||||||
|
* recycle entry in free list
|
||||||
|
* not an error, just uncacheable
|
||||||
|
*/
|
||||||
|
cache->most_recent_entry = current->next;
|
||||||
|
current->next = cache->free_entry;
|
||||||
|
cache->free_entry = current;
|
||||||
|
current = (struct CACHED_GENERIC*)NULL;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
current->variable = (void*)NULL;
|
||||||
|
current->varsize = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
cache->writes++;
|
||||||
|
}
|
||||||
|
return (current);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Invalidate entries in cache
|
||||||
|
*
|
||||||
|
* Several entries may have to be invalidated (at least for inodes
|
||||||
|
* associated to directories which have been renamed), a different
|
||||||
|
* compare function may be provided to select entries to invalidate
|
||||||
|
*
|
||||||
|
* Returns the number of deleted entries, this can be used by
|
||||||
|
* the caller to signal a cache corruption if the entry was
|
||||||
|
* supposed to be found.
|
||||||
|
*/
|
||||||
|
|
||||||
|
int ntfs_invalidate_cache(struct CACHE_HEADER *cache,
|
||||||
|
const struct CACHED_GENERIC *item, cache_compare compare)
|
||||||
|
{
|
||||||
|
struct CACHED_GENERIC *current;
|
||||||
|
struct CACHED_GENERIC *previous;
|
||||||
|
int count;
|
||||||
|
|
||||||
|
current = (struct CACHED_GENERIC*)NULL;
|
||||||
|
count = 0;
|
||||||
|
if (cache) {
|
||||||
|
/*
|
||||||
|
* Search sequentially in LRU list
|
||||||
|
*/
|
||||||
|
current = cache->most_recent_entry;
|
||||||
|
previous = (struct CACHED_GENERIC*)NULL;
|
||||||
|
while (current) {
|
||||||
|
if (!compare(current, item)) {
|
||||||
|
/*
|
||||||
|
* Relink into free list
|
||||||
|
*/
|
||||||
|
if (previous)
|
||||||
|
previous->next = current->next;
|
||||||
|
else
|
||||||
|
cache->most_recent_entry = current->next;
|
||||||
|
current->next = cache->free_entry;
|
||||||
|
cache->free_entry = current;
|
||||||
|
if (current->variable)
|
||||||
|
free(current->variable);
|
||||||
|
current->varsize = 0;
|
||||||
|
if (previous)
|
||||||
|
current = previous->next;
|
||||||
|
else
|
||||||
|
current = cache->most_recent_entry;
|
||||||
|
count++;
|
||||||
|
} else {
|
||||||
|
previous = current;
|
||||||
|
current = current->next;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return (count);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Free memory allocated to a cache
|
||||||
|
*/
|
||||||
|
|
||||||
|
static void ntfs_free_cache(struct CACHE_HEADER *cache)
|
||||||
|
{
|
||||||
|
struct CACHED_GENERIC *entry;
|
||||||
|
|
||||||
|
if (cache) {
|
||||||
|
for (entry=cache->most_recent_entry; entry; entry=entry->next)
|
||||||
|
if (entry->variable)
|
||||||
|
free(entry->variable);
|
||||||
|
free(cache);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Create a cache
|
||||||
|
*
|
||||||
|
* Returns the cache header, or NULL if the cache could not be created
|
||||||
|
*/
|
||||||
|
|
||||||
|
static struct CACHE_HEADER *ntfs_create_cache(const char *name,
|
||||||
|
int full_item_size, int item_count)
|
||||||
|
{
|
||||||
|
struct CACHE_HEADER *cache;
|
||||||
|
struct CACHED_GENERIC *p;
|
||||||
|
struct CACHED_GENERIC *q;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
cache = (struct CACHE_HEADER*)
|
||||||
|
ntfs_malloc(sizeof(struct CACHE_HEADER)
|
||||||
|
+ item_count*full_item_size);
|
||||||
|
if (cache) {
|
||||||
|
cache->name = name;
|
||||||
|
cache->fixed_size = full_item_size - sizeof(struct CACHED_GENERIC);
|
||||||
|
cache->reads = 0;
|
||||||
|
cache->writes = 0;
|
||||||
|
cache->hits = 0;
|
||||||
|
/* chain the entries, and mark an invalid entry */
|
||||||
|
cache->most_recent_entry = (struct CACHED_GENERIC*)NULL;
|
||||||
|
cache->free_entry = &cache->entry[0];
|
||||||
|
p = &cache->entry[0];
|
||||||
|
for (i=0; i<(item_count - 1); i++) {
|
||||||
|
q = (struct CACHED_GENERIC*)((char*)p + full_item_size);
|
||||||
|
p->next = q;
|
||||||
|
p->variable = (void*)NULL;
|
||||||
|
p->varsize = 0;
|
||||||
|
p = q;
|
||||||
|
}
|
||||||
|
/* special for the last entry */
|
||||||
|
p->next = (struct CACHED_GENERIC*)NULL;
|
||||||
|
p->variable = (void*)NULL;
|
||||||
|
p->varsize = 0;
|
||||||
|
}
|
||||||
|
return (cache);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Create all LRU caches
|
||||||
|
*
|
||||||
|
* No error return, if creation is not possible, cacheing will
|
||||||
|
* just be not available
|
||||||
|
*/
|
||||||
|
|
||||||
|
void ntfs_create_lru_caches(ntfs_volume *vol)
|
||||||
|
{
|
||||||
|
#if CACHE_INODE_SIZE
|
||||||
|
/* inode cache */
|
||||||
|
vol->xinode_cache = ntfs_create_cache("inode",
|
||||||
|
sizeof(struct CACHED_INODE), CACHE_INODE_SIZE);
|
||||||
|
#endif
|
||||||
|
vol->securid_cache = ntfs_create_cache("securid",
|
||||||
|
sizeof(struct CACHED_SECURID), CACHE_SECURID_SIZE);
|
||||||
|
#if CACHE_LEGACY_SIZE
|
||||||
|
vol->legacy_cache = ntfs_create_cache("legacy",
|
||||||
|
sizeof(struct CACHED_PERMISSIONS_LEGACY), CACHE_LEGACY_SIZE);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Free all LRU caches
|
||||||
|
*/
|
||||||
|
|
||||||
|
void ntfs_free_lru_caches(ntfs_volume *vol)
|
||||||
|
{
|
||||||
|
#if CACHE_INODE_SIZE
|
||||||
|
ntfs_free_cache(vol->xinode_cache);
|
||||||
|
#endif
|
||||||
|
ntfs_free_cache(vol->securid_cache);
|
||||||
|
#if CACHE_LEGACY_SIZE
|
||||||
|
ntfs_free_cache(vol->legacy_cache);
|
||||||
|
#endif
|
||||||
|
}
|
74
source/libntfs/misc.h
Normal file
74
source/libntfs/misc.h
Normal file
@ -0,0 +1,74 @@
|
|||||||
|
/*
|
||||||
|
* misc.h : miscellaneous exports
|
||||||
|
* - memory allocation
|
||||||
|
* - LRU caches
|
||||||
|
*
|
||||||
|
* Copyright (c) 2008 Jean-Pierre Andre
|
||||||
|
*
|
||||||
|
* This program/include file 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/include file 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
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _NTFS_MISC_H_
|
||||||
|
#define _NTFS_MISC_H_
|
||||||
|
|
||||||
|
#include "volume.h"
|
||||||
|
|
||||||
|
struct CACHED_GENERIC {
|
||||||
|
struct CACHED_GENERIC *next;
|
||||||
|
void *variable;
|
||||||
|
size_t varsize;
|
||||||
|
void *fixed[0];
|
||||||
|
} ;
|
||||||
|
|
||||||
|
struct CACHED_INODE {
|
||||||
|
struct CACHED_INODE *next;
|
||||||
|
const char *pathname;
|
||||||
|
size_t varsize;
|
||||||
|
/* above fields must match "struct CACHED_GENERIC" */
|
||||||
|
u64 inum;
|
||||||
|
} ;
|
||||||
|
|
||||||
|
typedef int (*cache_compare)(const struct CACHED_GENERIC *cached,
|
||||||
|
const struct CACHED_GENERIC *item);
|
||||||
|
|
||||||
|
struct CACHE_HEADER {
|
||||||
|
const char *name;
|
||||||
|
struct CACHED_GENERIC *most_recent_entry;
|
||||||
|
struct CACHED_GENERIC *free_entry;
|
||||||
|
unsigned long reads;
|
||||||
|
unsigned long writes;
|
||||||
|
unsigned long hits;
|
||||||
|
int fixed_size;
|
||||||
|
struct CACHED_GENERIC entry[0];
|
||||||
|
} ;
|
||||||
|
|
||||||
|
/* cast to generic, avoiding gcc warnings */
|
||||||
|
#define GENERIC(pstr) ((const struct CACHED_GENERIC*)(const void*)(pstr))
|
||||||
|
|
||||||
|
struct CACHED_GENERIC *ntfs_fetch_cache(struct CACHE_HEADER *cache,
|
||||||
|
const struct CACHED_GENERIC *wanted, cache_compare compare);
|
||||||
|
struct CACHED_GENERIC *ntfs_enter_cache(struct CACHE_HEADER *cache,
|
||||||
|
const struct CACHED_GENERIC *item, cache_compare compare);
|
||||||
|
int ntfs_invalidate_cache(struct CACHE_HEADER *cache,
|
||||||
|
const struct CACHED_GENERIC *item, cache_compare compare);
|
||||||
|
void ntfs_create_lru_caches(ntfs_volume *vol);
|
||||||
|
void ntfs_free_lru_caches(ntfs_volume *vol);
|
||||||
|
|
||||||
|
void *ntfs_calloc(size_t size);
|
||||||
|
void *ntfs_malloc(size_t size);
|
||||||
|
|
||||||
|
#endif /* _NTFS_MISC_H_ */
|
||||||
|
|
231
source/libntfs/mst.c
Normal file
231
source/libntfs/mst.c
Normal file
@ -0,0 +1,231 @@
|
|||||||
|
/**
|
||||||
|
* mst.c - Multi sector fixup handling code. Originated from the Linux-NTFS project.
|
||||||
|
*
|
||||||
|
* Copyright (c) 2000-2004 Anton Altaparmakov
|
||||||
|
* Copyright (c) 2006-2009 Szabolcs Szakacsits
|
||||||
|
*
|
||||||
|
* This program/include file 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/include file 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
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifdef HAVE_CONFIG_H
|
||||||
|
#include "config.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef HAVE_ERRNO_H
|
||||||
|
#include <errno.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "mst.h"
|
||||||
|
#include "logging.h"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ntfs_mst_post_read_fixup - deprotect multi sector transfer protected data
|
||||||
|
* @b: pointer to the data to deprotect
|
||||||
|
* @size: size in bytes of @b
|
||||||
|
*
|
||||||
|
* Perform the necessary post read multi sector transfer fixups and detect the
|
||||||
|
* presence of incomplete multi sector transfers. - In that case, overwrite the
|
||||||
|
* magic of the ntfs record header being processed with "BAAD" (in memory only!)
|
||||||
|
* and abort processing.
|
||||||
|
*
|
||||||
|
* Return 0 on success and -1 on error, with errno set to the error code. The
|
||||||
|
* following error codes are defined:
|
||||||
|
* EINVAL Invalid arguments or invalid NTFS record in buffer @b.
|
||||||
|
* EIO Multi sector transfer error was detected. Magic of the NTFS
|
||||||
|
* record in @b will have been set to "BAAD".
|
||||||
|
*/
|
||||||
|
int ntfs_mst_post_read_fixup(NTFS_RECORD *b, const u32 size)
|
||||||
|
{
|
||||||
|
u16 usa_ofs, usa_count, usn;
|
||||||
|
u16 *usa_pos, *data_pos;
|
||||||
|
|
||||||
|
ntfs_log_trace("Entering\n");
|
||||||
|
|
||||||
|
/* Setup the variables. */
|
||||||
|
usa_ofs = le16_to_cpu(b->usa_ofs);
|
||||||
|
/* Decrement usa_count to get number of fixups. */
|
||||||
|
usa_count = le16_to_cpu(b->usa_count) - 1;
|
||||||
|
/* Size and alignment checks. */
|
||||||
|
if (size & (NTFS_BLOCK_SIZE - 1) || usa_ofs & 1 ||
|
||||||
|
(u32)(usa_ofs + (usa_count * 2)) > size ||
|
||||||
|
(size >> NTFS_BLOCK_SIZE_BITS) != usa_count) {
|
||||||
|
errno = EINVAL;
|
||||||
|
ntfs_log_perror("%s: magic: 0x%08x size: %d usa_ofs: %d "
|
||||||
|
"usa_count: %d", __FUNCTION__, *(le32 *)b,
|
||||||
|
size, usa_ofs, usa_count);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
/* Position of usn in update sequence array. */
|
||||||
|
usa_pos = (u16*)b + usa_ofs/sizeof(u16);
|
||||||
|
/*
|
||||||
|
* The update sequence number which has to be equal to each of the
|
||||||
|
* u16 values before they are fixed up. Note no need to care for
|
||||||
|
* endianness since we are comparing and moving data for on disk
|
||||||
|
* structures which means the data is consistent. - If it is
|
||||||
|
* consistency the wrong endianness it doesn't make any difference.
|
||||||
|
*/
|
||||||
|
usn = *usa_pos;
|
||||||
|
/*
|
||||||
|
* Position in protected data of first u16 that needs fixing up.
|
||||||
|
*/
|
||||||
|
data_pos = (u16*)b + NTFS_BLOCK_SIZE/sizeof(u16) - 1;
|
||||||
|
/*
|
||||||
|
* Check for incomplete multi sector transfer(s).
|
||||||
|
*/
|
||||||
|
while (usa_count--) {
|
||||||
|
if (*data_pos != usn) {
|
||||||
|
/*
|
||||||
|
* Incomplete multi sector transfer detected! )-:
|
||||||
|
* Set the magic to "BAAD" and return failure.
|
||||||
|
* Note that magic_BAAD is already converted to le32.
|
||||||
|
*/
|
||||||
|
errno = EIO;
|
||||||
|
ntfs_log_perror("Incomplete multi-sector transfer: "
|
||||||
|
"magic: 0x%08x size: %d usa_ofs: %d usa_count:"
|
||||||
|
" %d data: %d usn: %d", *(le32 *)b, size,
|
||||||
|
usa_ofs, usa_count, *data_pos, usn);
|
||||||
|
b->magic = magic_BAAD;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
data_pos += NTFS_BLOCK_SIZE/sizeof(u16);
|
||||||
|
}
|
||||||
|
/* Re-setup the variables. */
|
||||||
|
usa_count = le16_to_cpu(b->usa_count) - 1;
|
||||||
|
data_pos = (u16*)b + NTFS_BLOCK_SIZE/sizeof(u16) - 1;
|
||||||
|
/* Fixup all sectors. */
|
||||||
|
while (usa_count--) {
|
||||||
|
/*
|
||||||
|
* Increment position in usa and restore original data from
|
||||||
|
* the usa into the data buffer.
|
||||||
|
*/
|
||||||
|
*data_pos = *(++usa_pos);
|
||||||
|
/* Increment position in data as well. */
|
||||||
|
data_pos += NTFS_BLOCK_SIZE/sizeof(u16);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ntfs_mst_pre_write_fixup - apply multi sector transfer protection
|
||||||
|
* @b: pointer to the data to protect
|
||||||
|
* @size: size in bytes of @b
|
||||||
|
*
|
||||||
|
* Perform the necessary pre write multi sector transfer fixup on the data
|
||||||
|
* pointer to by @b of @size.
|
||||||
|
*
|
||||||
|
* Return 0 if fixups applied successfully or -1 if no fixups were performed
|
||||||
|
* due to errors. In that case errno i set to the error code (EINVAL).
|
||||||
|
*
|
||||||
|
* NOTE: We consider the absence / invalidity of an update sequence array to
|
||||||
|
* mean error. This means that you have to create a valid update sequence
|
||||||
|
* array header in the ntfs record before calling this function, otherwise it
|
||||||
|
* will fail (the header needs to contain the position of the update sequence
|
||||||
|
* array together with the number of elements in the array). You also need to
|
||||||
|
* initialise the update sequence number before calling this function
|
||||||
|
* otherwise a random word will be used (whatever was in the record at that
|
||||||
|
* position at that time).
|
||||||
|
*/
|
||||||
|
int ntfs_mst_pre_write_fixup(NTFS_RECORD *b, const u32 size)
|
||||||
|
{
|
||||||
|
u16 usa_ofs, usa_count, usn;
|
||||||
|
u16 *usa_pos, *data_pos;
|
||||||
|
|
||||||
|
ntfs_log_trace("Entering\n");
|
||||||
|
|
||||||
|
/* Sanity check + only fixup if it makes sense. */
|
||||||
|
if (!b || ntfs_is_baad_record(b->magic) ||
|
||||||
|
ntfs_is_hole_record(b->magic)) {
|
||||||
|
errno = EINVAL;
|
||||||
|
ntfs_log_perror("%s: bad argument", __FUNCTION__);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
/* Setup the variables. */
|
||||||
|
usa_ofs = le16_to_cpu(b->usa_ofs);
|
||||||
|
/* Decrement usa_count to get number of fixups. */
|
||||||
|
usa_count = le16_to_cpu(b->usa_count) - 1;
|
||||||
|
/* Size and alignment checks. */
|
||||||
|
if (size & (NTFS_BLOCK_SIZE - 1) || usa_ofs & 1 ||
|
||||||
|
(u32)(usa_ofs + (usa_count * 2)) > size ||
|
||||||
|
(size >> NTFS_BLOCK_SIZE_BITS) != usa_count) {
|
||||||
|
errno = EINVAL;
|
||||||
|
ntfs_log_perror("%s", __FUNCTION__);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
/* Position of usn in update sequence array. */
|
||||||
|
usa_pos = (u16*)((u8*)b + usa_ofs);
|
||||||
|
/*
|
||||||
|
* Cyclically increment the update sequence number
|
||||||
|
* (skipping 0 and -1, i.e. 0xffff).
|
||||||
|
*/
|
||||||
|
usn = le16_to_cpup(usa_pos) + 1;
|
||||||
|
if (usn == 0xffff || !usn)
|
||||||
|
usn = 1;
|
||||||
|
usn = cpu_to_le16(usn);
|
||||||
|
*usa_pos = usn;
|
||||||
|
/* Position in data of first u16 that needs fixing up. */
|
||||||
|
data_pos = (u16*)b + NTFS_BLOCK_SIZE/sizeof(u16) - 1;
|
||||||
|
/* Fixup all sectors. */
|
||||||
|
while (usa_count--) {
|
||||||
|
/*
|
||||||
|
* Increment the position in the usa and save the
|
||||||
|
* original data from the data buffer into the usa.
|
||||||
|
*/
|
||||||
|
*(++usa_pos) = *data_pos;
|
||||||
|
/* Apply fixup to data. */
|
||||||
|
*data_pos = usn;
|
||||||
|
/* Increment position in data as well. */
|
||||||
|
data_pos += NTFS_BLOCK_SIZE/sizeof(u16);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ntfs_mst_post_write_fixup - deprotect multi sector transfer protected data
|
||||||
|
* @b: pointer to the data to deprotect
|
||||||
|
*
|
||||||
|
* Perform the necessary post write multi sector transfer fixup, not checking
|
||||||
|
* for any errors, because we assume we have just used
|
||||||
|
* ntfs_mst_pre_write_fixup(), thus the data will be fine or we would never
|
||||||
|
* have gotten here.
|
||||||
|
*/
|
||||||
|
void ntfs_mst_post_write_fixup(NTFS_RECORD *b)
|
||||||
|
{
|
||||||
|
u16 *usa_pos, *data_pos;
|
||||||
|
|
||||||
|
u16 usa_ofs = le16_to_cpu(b->usa_ofs);
|
||||||
|
u16 usa_count = le16_to_cpu(b->usa_count) - 1;
|
||||||
|
|
||||||
|
ntfs_log_trace("Entering\n");
|
||||||
|
|
||||||
|
/* Position of usn in update sequence array. */
|
||||||
|
usa_pos = (u16*)b + usa_ofs/sizeof(u16);
|
||||||
|
|
||||||
|
/* Position in protected data of first u16 that needs fixing up. */
|
||||||
|
data_pos = (u16*)b + NTFS_BLOCK_SIZE/sizeof(u16) - 1;
|
||||||
|
|
||||||
|
/* Fixup all sectors. */
|
||||||
|
while (usa_count--) {
|
||||||
|
/*
|
||||||
|
* Increment position in usa and restore original data from
|
||||||
|
* the usa into the data buffer.
|
||||||
|
*/
|
||||||
|
*data_pos = *(++usa_pos);
|
||||||
|
|
||||||
|
/* Increment position in data as well. */
|
||||||
|
data_pos += NTFS_BLOCK_SIZE/sizeof(u16);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
34
source/libntfs/mst.h
Normal file
34
source/libntfs/mst.h
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
/*
|
||||||
|
* mst.h - Exports for multi sector transfer fixup functions.
|
||||||
|
* Originated from the Linux-NTFS project.
|
||||||
|
*
|
||||||
|
* Copyright (c) 2000-2002 Anton Altaparmakov
|
||||||
|
*
|
||||||
|
* This program/include file 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/include file 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
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _NTFS_MST_H
|
||||||
|
#define _NTFS_MST_H
|
||||||
|
|
||||||
|
#include "types.h"
|
||||||
|
#include "layout.h"
|
||||||
|
|
||||||
|
extern int ntfs_mst_post_read_fixup(NTFS_RECORD *b, const u32 size);
|
||||||
|
extern int ntfs_mst_pre_write_fixup(NTFS_RECORD *b, const u32 size);
|
||||||
|
extern void ntfs_mst_post_write_fixup(NTFS_RECORD *b);
|
||||||
|
|
||||||
|
#endif /* defined _NTFS_MST_H */
|
||||||
|
|
681
source/libntfs/ntfs.c
Normal file
681
source/libntfs/ntfs.c
Normal file
@ -0,0 +1,681 @@
|
|||||||
|
/**
|
||||||
|
* ntfs.c - Simple functionality for startup, mounting and unmounting of NTFS-based devices.
|
||||||
|
*
|
||||||
|
* Copyright (c) 2009 Rhys "Shareese" Koedijk
|
||||||
|
* Copyright (c) 2006 Michael "Chishm" Chisholm
|
||||||
|
*
|
||||||
|
* This program/include file 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/include file 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; if not, write to the Free Software Foundation,
|
||||||
|
* Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifdef HAVE_CONFIG_H
|
||||||
|
#include "config.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef HAVE_STDLIB_H
|
||||||
|
#include <stdlib.h>
|
||||||
|
#endif
|
||||||
|
#ifdef HAVE_ERRNO_H
|
||||||
|
#include <errno.h>
|
||||||
|
#endif
|
||||||
|
#ifdef HAVE_STRING_H
|
||||||
|
#include <string.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "ntfs.h"
|
||||||
|
#include "ntfsinternal.h"
|
||||||
|
#include "ntfsfile.h"
|
||||||
|
#include "ntfsdir.h"
|
||||||
|
#include "gekko_io.h"
|
||||||
|
#include "cache.h"
|
||||||
|
|
||||||
|
// NTFS device driver devoptab
|
||||||
|
static const devoptab_t devops_ntfs = {
|
||||||
|
NULL, /* Device name */
|
||||||
|
sizeof (ntfs_file_state),
|
||||||
|
ntfs_open_r,
|
||||||
|
ntfs_close_r,
|
||||||
|
ntfs_write_r,
|
||||||
|
ntfs_read_r,
|
||||||
|
ntfs_seek_r,
|
||||||
|
ntfs_fstat_r,
|
||||||
|
ntfs_stat_r,
|
||||||
|
ntfs_link_r,
|
||||||
|
ntfs_unlink_r,
|
||||||
|
ntfs_chdir_r,
|
||||||
|
ntfs_rename_r,
|
||||||
|
ntfs_mkdir_r,
|
||||||
|
sizeof (ntfs_dir_state),
|
||||||
|
ntfs_diropen_r,
|
||||||
|
ntfs_dirreset_r,
|
||||||
|
ntfs_dirnext_r,
|
||||||
|
ntfs_dirclose_r,
|
||||||
|
ntfs_statvfs_r,
|
||||||
|
ntfs_ftruncate_r,
|
||||||
|
ntfs_fsync_r,
|
||||||
|
NULL /* Device data */
|
||||||
|
};
|
||||||
|
|
||||||
|
void ntfsInit (void)
|
||||||
|
{
|
||||||
|
static bool isInit = false;
|
||||||
|
|
||||||
|
// Initialise ntfs-3g (if not already done so)
|
||||||
|
if (!isInit) {
|
||||||
|
isInit = true;
|
||||||
|
|
||||||
|
// Set the log handler
|
||||||
|
#ifdef NTFS_ENABLE_LOG
|
||||||
|
ntfs_log_set_handler(ntfs_log_handler_stderr);
|
||||||
|
#else
|
||||||
|
ntfs_log_set_handler(ntfs_log_handler_null);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Set our current local
|
||||||
|
ntfs_set_locale();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
int ntfsMountAll (ntfs_md **mounts, u32 flags)
|
||||||
|
{
|
||||||
|
const INTERFACE_ID *discs = ntfsGetDiscInterfaces();
|
||||||
|
const INTERFACE_ID *disc = NULL;
|
||||||
|
ntfs_md mount_points[NTFS_MAX_MOUNTS];
|
||||||
|
sec_t *partitions = NULL;
|
||||||
|
int mount_count = 0;
|
||||||
|
int partition_count = 0;
|
||||||
|
char name[128];
|
||||||
|
int i, j, k;
|
||||||
|
|
||||||
|
// Initialise ntfs-3g
|
||||||
|
ntfsInit();
|
||||||
|
|
||||||
|
// Find and mount all NTFS partitions on all known devices
|
||||||
|
for (i = 0; discs[i].name != NULL && discs[i].interface != NULL; i++) {
|
||||||
|
disc = &discs[i];
|
||||||
|
partition_count = ntfsFindPartitions(disc->interface, &partitions);
|
||||||
|
if (partition_count > 0 && partitions) {
|
||||||
|
for (j = 0, k = 0; j < partition_count; j++) {
|
||||||
|
|
||||||
|
// Find the next unused mount name
|
||||||
|
do {
|
||||||
|
sprintf(name, "%s%i", NTFS_MOUNT_PREFIX, k++);
|
||||||
|
if (k >= NTFS_MAX_MOUNTS) {
|
||||||
|
ntfs_free(partitions);
|
||||||
|
errno = EADDRNOTAVAIL;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
} while (ntfsGetDevice(name, false));
|
||||||
|
|
||||||
|
// Mount the partition
|
||||||
|
if (mount_count < NTFS_MAX_MOUNTS) {
|
||||||
|
if (ntfsMount(name, disc->interface, partitions[j], CACHE_DEFAULT_PAGE_SIZE, CACHE_DEFAULT_PAGE_COUNT, flags)) {
|
||||||
|
strcpy(mount_points[mount_count].name, name);
|
||||||
|
mount_points[mount_count].interface = disc->interface;
|
||||||
|
mount_points[mount_count].startSector = partitions[j];
|
||||||
|
mount_count++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
ntfs_free(partitions);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return the mounts (if any)
|
||||||
|
if (mount_count > 0 && mounts) {
|
||||||
|
*mounts = (ntfs_md*)ntfs_alloc(sizeof(ntfs_md) * mount_count);
|
||||||
|
if (*mounts) {
|
||||||
|
memcpy(*mounts, &mount_points, sizeof(ntfs_md) * mount_count);
|
||||||
|
return mount_count;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int ntfsMountDevice (const DISC_INTERFACE *interface, ntfs_md **mounts, u32 flags)
|
||||||
|
{
|
||||||
|
const INTERFACE_ID *discs = ntfsGetDiscInterfaces();
|
||||||
|
const INTERFACE_ID *disc = NULL;
|
||||||
|
ntfs_md mount_points[NTFS_MAX_MOUNTS];
|
||||||
|
sec_t *partitions = NULL;
|
||||||
|
int mount_count = 0;
|
||||||
|
int partition_count = 0;
|
||||||
|
char name[128];
|
||||||
|
int i, j, k;
|
||||||
|
|
||||||
|
// Sanity check
|
||||||
|
if (!interface) {
|
||||||
|
errno = EINVAL;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initialise ntfs-3g
|
||||||
|
ntfsInit();
|
||||||
|
|
||||||
|
// Find the specified device then find and mount all NTFS partitions on it
|
||||||
|
for (i = 0; discs[i].name != NULL && discs[i].interface != NULL; i++) {
|
||||||
|
if (discs[i].interface == interface) {
|
||||||
|
disc = &discs[i];
|
||||||
|
partition_count = ntfsFindPartitions(disc->interface, &partitions);
|
||||||
|
if (partition_count > 0 && partitions) {
|
||||||
|
for (j = 0, k = 0; j < partition_count; j++) {
|
||||||
|
|
||||||
|
// Find the next unused mount name
|
||||||
|
do {
|
||||||
|
sprintf(name, "%s%i", NTFS_MOUNT_PREFIX, k++);
|
||||||
|
if (k >= NTFS_MAX_MOUNTS) {
|
||||||
|
ntfs_free(partitions);
|
||||||
|
errno = EADDRNOTAVAIL;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
} while (ntfsGetDevice(name, false));
|
||||||
|
|
||||||
|
// Mount the partition
|
||||||
|
if (mount_count < NTFS_MAX_MOUNTS) {
|
||||||
|
if (ntfsMount(name, disc->interface, partitions[j], CACHE_DEFAULT_PAGE_SIZE, CACHE_DEFAULT_PAGE_COUNT, flags)) {
|
||||||
|
strcpy(mount_points[mount_count].name, name);
|
||||||
|
mount_points[mount_count].interface = disc->interface;
|
||||||
|
mount_points[mount_count].startSector = partitions[j];
|
||||||
|
mount_count++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
ntfs_free(partitions);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we couldn't find the device then return with error status
|
||||||
|
if (!disc) {
|
||||||
|
errno = ENODEV;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return the mounts (if any)
|
||||||
|
if (mount_count > 0 && mounts) {
|
||||||
|
*mounts = (ntfs_md*)ntfs_alloc(sizeof(ntfs_md) * mount_count);
|
||||||
|
if (*mounts) {
|
||||||
|
memcpy(*mounts, &mount_points, sizeof(ntfs_md) * mount_count);
|
||||||
|
return mount_count;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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 (!(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;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ntfsUnmount (const char *name, bool force)
|
||||||
|
{
|
||||||
|
ntfs_vd *vd = NULL;
|
||||||
|
|
||||||
|
// Get the devices volume descriptor
|
||||||
|
vd = ntfsGetVolume(name);
|
||||||
|
if (!vd)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Remove the device from the devoptab table
|
||||||
|
ntfsRemoveDevice(name);
|
||||||
|
|
||||||
|
// Deinitialise the volume descriptor
|
||||||
|
ntfsDeinitVolume(vd);
|
||||||
|
|
||||||
|
// Unmount the volume
|
||||||
|
ntfs_umount(vd->vol, force);
|
||||||
|
|
||||||
|
// Free the volume descriptor
|
||||||
|
ntfs_free(vd);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *ntfsGetVolumeName (const char *name)
|
||||||
|
{
|
||||||
|
ntfs_vd *vd = NULL;
|
||||||
|
|
||||||
|
// Sanity check
|
||||||
|
if (!name) {
|
||||||
|
errno = EINVAL;
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the devices volume descriptor
|
||||||
|
vd = ntfsGetVolume(name);
|
||||||
|
if (!vd) {
|
||||||
|
errno = ENODEV;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the volumes name
|
||||||
|
return vd->vol->vol_name;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ntfsSetVolumeName (const char *name, const char *volumeName)
|
||||||
|
{
|
||||||
|
ntfs_vd *vd = NULL;
|
||||||
|
ntfs_attr *na = NULL;
|
||||||
|
ntfschar *ulabel = NULL;
|
||||||
|
int ulabel_len;
|
||||||
|
char *label = NULL;
|
||||||
|
|
||||||
|
// Sanity check
|
||||||
|
if (!name) {
|
||||||
|
errno = EINVAL;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the devices volume descriptor
|
||||||
|
vd = ntfsGetVolume(name);
|
||||||
|
if (!vd) {
|
||||||
|
errno = ENODEV;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Lock
|
||||||
|
ntfsLock(vd);
|
||||||
|
|
||||||
|
// Allocate a buffer to hold the new volume name
|
||||||
|
label = ntfs_alloc(strlen(volumeName) + 1);
|
||||||
|
if (!label) {
|
||||||
|
ntfsUnlock(vd);
|
||||||
|
errno = EINVAL;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Copy the new volume name
|
||||||
|
memset(label, 0, strlen(volumeName) + 1);
|
||||||
|
strcpy(label, volumeName);
|
||||||
|
|
||||||
|
// Convert the new volume name to unicode
|
||||||
|
ulabel_len = ntfsLocalToUnicode(label, &ulabel) * sizeof(ntfschar);
|
||||||
|
if (ulabel_len < 0) {
|
||||||
|
ntfs_free(label);
|
||||||
|
ntfsUnlock(vd);
|
||||||
|
errno = EINVAL;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if the volume name attribute exists
|
||||||
|
na = ntfs_attr_open(vd->vol->vol_ni, AT_VOLUME_NAME, NULL, 0);
|
||||||
|
if (na) {
|
||||||
|
|
||||||
|
// It does, resize it to match the length of the new volume name
|
||||||
|
if (ntfs_attr_truncate(na, ulabel_len)) {
|
||||||
|
ntfs_free(label);
|
||||||
|
ntfs_free(ulabel);
|
||||||
|
ntfsUnlock(vd);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write the new volume name
|
||||||
|
if (ntfs_attr_pwrite(na, 0, ulabel_len, ulabel) != ulabel_len) {
|
||||||
|
ntfs_free(label);
|
||||||
|
ntfs_free(ulabel);
|
||||||
|
ntfsUnlock(vd);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
|
||||||
|
// It doesn't, create it now
|
||||||
|
if (ntfs_attr_add(vd->vol->vol_ni, AT_VOLUME_NAME, NULL, 0, (u8*)ulabel, ulabel_len)) {
|
||||||
|
ntfs_free(label);
|
||||||
|
ntfs_free(ulabel);
|
||||||
|
ntfsUnlock(vd);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update the volumes name (as it has now been changed)
|
||||||
|
if(vd->vol->vol_name) {
|
||||||
|
ntfs_free(vd->vol->vol_name);
|
||||||
|
vd->vol->vol_name = label;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close the volume name attribute
|
||||||
|
if (na)
|
||||||
|
ntfs_attr_close(na);
|
||||||
|
|
||||||
|
// Sync the volume node
|
||||||
|
if (ntfs_inode_sync(vd->vol->vol_ni)) {
|
||||||
|
ntfs_free(ulabel);
|
||||||
|
ntfsUnlock(vd);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clean up
|
||||||
|
ntfs_free(ulabel);
|
||||||
|
|
||||||
|
// Unlock
|
||||||
|
ntfsUnlock(vd);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
const devoptab_t *ntfsGetDevOpTab (void)
|
||||||
|
{
|
||||||
|
return &devops_ntfs;
|
||||||
|
}
|
||||||
|
|
161
source/libntfs/ntfs.h
Normal file
161
source/libntfs/ntfs.h
Normal file
@ -0,0 +1,161 @@
|
|||||||
|
/**
|
||||||
|
* ntfs.h - Simple functionality for startup, mounting and unmounting of NTFS-based devices.
|
||||||
|
*
|
||||||
|
* Copyright (c) 2009 Rhys "Shareese" Koedijk
|
||||||
|
* Copyright (c) 2006 Michael "Chishm" Chisholm
|
||||||
|
*
|
||||||
|
* This program/include file 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/include file 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; if not, write to the Free Software Foundation,
|
||||||
|
* Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _LIBNTFS_H
|
||||||
|
#define _LIBNTFS_H
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <gctypes.h>
|
||||||
|
#include <gccore.h>
|
||||||
|
#include <ogc/disc_io.h>
|
||||||
|
|
||||||
|
/* NTFS errno values */
|
||||||
|
#define ENOPART 3000 /* No partition was found */
|
||||||
|
#define EINVALPART 3001 /* Specified partition is invalid or not supported */
|
||||||
|
#define EDIRTY 3002 /* Volume is dirty and NTFS_RECOVER was not specified during mount */
|
||||||
|
#define EHIBERNATED 3003 /* Volume is hibernated and NTFS_IGNORE_HIBERFILE was not specified during mount */
|
||||||
|
|
||||||
|
/* NTFS cache options */
|
||||||
|
#define CACHE_DEFAULT_PAGE_COUNT 8 /* The default number of pages in the cache */
|
||||||
|
#define CACHE_DEFAULT_PAGE_SIZE 128 /* The default number of sectors per cache page */
|
||||||
|
|
||||||
|
/* NTFS mount flags */
|
||||||
|
#define NTFS_DEFAULT 0x00000000 /* Standard mount, expects a clean, non-hibernated volume */
|
||||||
|
#define NTFS_SHOW_HIDDEN_FILES 0x00000001 /* Display hidden files when enumerating directories */
|
||||||
|
#define NTFS_SHOW_SYSTEM_FILES 0x00000002 /* Display system files when enumerating directories */
|
||||||
|
#define NTFS_UPDATE_ACCESS_TIMES 0x00000004 /* Update file and directory access times */
|
||||||
|
#define NTFS_RECOVER 0x00000008 /* Reset $LogFile if dirty (i.e. from unclean disconnect) */
|
||||||
|
#define NTFS_IGNORE_HIBERFILE 0x00000010 /* Mount even if volume is hibernated */
|
||||||
|
#define NTFS_SU NTFS_SHOW_HIDDEN_FILES & NTFS_SHOW_SYSTEM_FILES
|
||||||
|
#define NTFS_FORCE NTFS_RECOVER & NTFS_IGNORE_HIBERFILE
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ntfs_md - NTFS mount descriptor
|
||||||
|
*/
|
||||||
|
typedef struct _ntfs_md {
|
||||||
|
char name[32]; /* Mount name (can be accessed as "name:/") */
|
||||||
|
const DISC_INTERFACE *interface; /* Block device containing the mounted partition */
|
||||||
|
sec_t startSector; /* Local block address to first sector of partition */
|
||||||
|
} ntfs_md;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find all NTFS partitions on a block device.
|
||||||
|
*
|
||||||
|
* @param INTERFACE The block device to search
|
||||||
|
* @param PARTITIONS (out) A pointer to receive the array of partition start sectors
|
||||||
|
*
|
||||||
|
* @return The number of entries in PARTITIONS or -1 if an error occurred (see errno)
|
||||||
|
* @note The caller is responsible for freeing PARTITIONS when finished with it
|
||||||
|
*/
|
||||||
|
extern int ntfsFindPartitions (const DISC_INTERFACE *interface, sec_t **partitions);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Mount all NTFS partitions on all inserted block devices.
|
||||||
|
*
|
||||||
|
* @param MOUNTS (out) A pointer to receive the array of mount descriptors
|
||||||
|
* @param FLAGS Additional mounting flags. (see above)
|
||||||
|
*
|
||||||
|
* @return The number of entries in MOUNTS or -1 if an error occurred (see errno)
|
||||||
|
* @note The caller is responsible for freeing MOUNTS when finished with it
|
||||||
|
* @note All device caches are setup using default values (see above)
|
||||||
|
*/
|
||||||
|
extern int ntfsMountAll (ntfs_md **mounts, u32 flags);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Mount all NTFS partitions on a block devices.
|
||||||
|
*
|
||||||
|
* @param INTERFACE The block device to mount.
|
||||||
|
* @param MOUNTS (out) A pointer to receive the array of mount descriptors
|
||||||
|
* @param FLAGS Additional mounting flags. (see above)
|
||||||
|
*
|
||||||
|
* @return The number of entries in MOUNTS or -1 if an error occurred (see errno)
|
||||||
|
* @note The caller is responsible for freeing MOUNTS when finished with it
|
||||||
|
* @note The device cache is setup using default values (see above)
|
||||||
|
*/
|
||||||
|
extern int ntfsMountDevice (const DISC_INTERFACE* interface, ntfs_md **mounts, u32 flags);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Mount a NTFS partition from a specific sector on a block device.
|
||||||
|
*
|
||||||
|
* @param NAME The name to mount the device under (can then be accessed as "NAME:/")
|
||||||
|
* @param INTERFACE The block device to mount
|
||||||
|
* @param STARTSECTOR The sector the partition begins at (see @ntfsFindPartitions)
|
||||||
|
* @param CACHEPAGECOUNT The total number of pages in the device cache
|
||||||
|
* @param CACHEPAGESIZE The number of sectors per cache page
|
||||||
|
* @param FLAGS Additional mounting flags (see above)
|
||||||
|
*
|
||||||
|
* @return True if mount was successful, false if no partition was found or an error occurred (see errno)
|
||||||
|
* @note ntfsFindPartitions should be used first to locate the partitions start sector
|
||||||
|
*/
|
||||||
|
extern bool ntfsMount (const char *name, const DISC_INTERFACE *interface, sec_t startSector, u32 cachePageCount, u32 cachePageSize, u32 flags);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unmount a NTFS partition.
|
||||||
|
*
|
||||||
|
* @param NAME The name of mount used in ntfsMountSimple() and ntfsMount()
|
||||||
|
* @param FORCE If true unmount even if the device is busy (may lead to data lose)
|
||||||
|
*/
|
||||||
|
extern void ntfsUnmount (const char *name, bool force);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the volume name of a mounted NTFS partition.
|
||||||
|
*
|
||||||
|
* @param NAME The name of mount (see @ntfsMountAll, @ntfsMountDevice, and @ntfsMount)
|
||||||
|
*
|
||||||
|
* @return The volumes name if successful or NULL if an error occurred (see errno)
|
||||||
|
*/
|
||||||
|
extern const char *ntfsGetVolumeName (const char *name);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the volume name of a mounted NTFS partition.
|
||||||
|
*
|
||||||
|
* @param NAME The name of mount (see @ntfsMountAll, @ntfsMountDevice, and @ntfsMount)
|
||||||
|
* @param VOLUMENAME The new volume name
|
||||||
|
*
|
||||||
|
* @return True if mount was successful, false if an error occurred (see errno)
|
||||||
|
* @note The mount must be write-enabled else this will fail
|
||||||
|
*/
|
||||||
|
extern bool ntfsSetVolumeName (const char *name, const char *volumeName);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* _LIBNTFS_H */
|
||||||
|
|
||||||
|
/*
|
||||||
|
typedef struct _FileInfo FileInfo;
|
||||||
|
|
||||||
|
struct _FileInfo
|
||||||
|
{
|
||||||
|
u64 offset[64];
|
||||||
|
s64 sector[64];
|
||||||
|
u64 count[64];
|
||||||
|
u32 num;
|
||||||
|
u64 filesize;
|
||||||
|
};
|
||||||
|
*/
|
||||||
|
typedef int (*_ntfs_frag_append_t)(void *ff, u32 offset, u32 sector, u32 count);
|
||||||
|
int _NTFS_get_fragments (const char *path, _ntfs_frag_append_t append_fragment, void *callback_data);
|
||||||
|
|
613
source/libntfs/ntfsdir.c
Normal file
613
source/libntfs/ntfsdir.c
Normal file
@ -0,0 +1,613 @@
|
|||||||
|
/**
|
||||||
|
* ntfs_dir.c - devoptab directory routines for NTFS-based devices.
|
||||||
|
*
|
||||||
|
* Copyright (c) 2009 Rhys "Shareese" Koedijk
|
||||||
|
* Copyright (c) 2006 Michael "Chishm" Chisholm
|
||||||
|
*
|
||||||
|
* This program/include file 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/include file 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; if not, write to the Free Software Foundation,
|
||||||
|
* Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifdef HAVE_CONFIG_H
|
||||||
|
#include "config.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef HAVE_STDLIB_H
|
||||||
|
#include <stdlib.h>
|
||||||
|
#endif
|
||||||
|
#ifdef HAVE_SYS_STATVFS_H
|
||||||
|
#include <sys/statvfs.h>
|
||||||
|
#endif
|
||||||
|
#ifdef HAVE_SYS_STAT_H
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#endif
|
||||||
|
#ifdef HAVE_ERRNO_H
|
||||||
|
#include <errno.h>
|
||||||
|
#endif
|
||||||
|
#ifdef HAVE_STRING_H
|
||||||
|
#include <string.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "ntfsinternal.h"
|
||||||
|
#include "ntfsdir.h"
|
||||||
|
#include "device.h"
|
||||||
|
#include <sys/dir.h>
|
||||||
|
|
||||||
|
#define STATE(x) ((ntfs_dir_state*)(x)->dirStruct)
|
||||||
|
|
||||||
|
void ntfsCloseDir (ntfs_dir_state *dir)
|
||||||
|
{
|
||||||
|
// Sanity check
|
||||||
|
if (!dir || !dir->vd)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Free the directory entries (if any)
|
||||||
|
while (dir->first) {
|
||||||
|
ntfs_dir_entry *next = dir->first->next;
|
||||||
|
ntfs_free(dir->first->name);
|
||||||
|
ntfs_free(dir->first);
|
||||||
|
dir->first = next;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close the directory (if open)
|
||||||
|
if (dir->ni)
|
||||||
|
ntfsCloseEntry(dir->vd, dir->ni);
|
||||||
|
|
||||||
|
// Reset the directory state
|
||||||
|
dir->ni = NULL;
|
||||||
|
dir->first = NULL;
|
||||||
|
dir->current = NULL;
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int ntfs_stat_r (struct _reent *r, const char *path, struct stat *st)
|
||||||
|
{
|
||||||
|
ntfs_log_trace("path %s, st %p\n", path, st);
|
||||||
|
|
||||||
|
ntfs_vd *vd = NULL;
|
||||||
|
ntfs_inode *ni = NULL;
|
||||||
|
|
||||||
|
// Get the volume descriptor for this path
|
||||||
|
vd = ntfsGetVolume(path);
|
||||||
|
if (!vd) {
|
||||||
|
r->_errno = ENODEV;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Short circuit cases were we don't actually have to do anything
|
||||||
|
if (!st)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
// Lock
|
||||||
|
ntfsLock(vd);
|
||||||
|
|
||||||
|
// Find the entry
|
||||||
|
ni = ntfsOpenEntry(vd, path);
|
||||||
|
if (!ni) {
|
||||||
|
r->_errno = errno;
|
||||||
|
ntfsUnlock(vd);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the entry stats
|
||||||
|
int ret = ntfsStat(vd, ni, st);
|
||||||
|
if (ret)
|
||||||
|
r->_errno = errno;
|
||||||
|
|
||||||
|
// Close the entry
|
||||||
|
ntfsCloseEntry(vd, ni);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int ntfs_link_r (struct _reent *r, const char *existing, const char *newLink)
|
||||||
|
{
|
||||||
|
ntfs_log_trace("existing %s, newLink %s\n", existing, newLink);
|
||||||
|
|
||||||
|
ntfs_vd *vd = NULL;
|
||||||
|
ntfs_inode *ni = NULL;
|
||||||
|
|
||||||
|
// Get the volume descriptor for this path
|
||||||
|
vd = ntfsGetVolume(existing);
|
||||||
|
if (!vd) {
|
||||||
|
r->_errno = ENODEV;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Lock
|
||||||
|
ntfsLock(vd);
|
||||||
|
|
||||||
|
// Create a symbolic link between the two paths
|
||||||
|
ni = ntfsCreate(vd, existing, S_IFLNK, newLink);
|
||||||
|
if (!ni) {
|
||||||
|
ntfsUnlock(vd);
|
||||||
|
r->_errno = errno;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close the symbolic link
|
||||||
|
ntfsCloseEntry(vd, ni);
|
||||||
|
|
||||||
|
// Unlock
|
||||||
|
ntfsUnlock(vd);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int ntfs_unlink_r (struct _reent *r, const char *name)
|
||||||
|
{
|
||||||
|
ntfs_log_trace("name %s\n", name);
|
||||||
|
|
||||||
|
// Unlink the entry
|
||||||
|
int ret = ntfsUnlink(ntfsGetVolume(name), name);
|
||||||
|
if (ret)
|
||||||
|
r->_errno = errno;
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
int ntfs_chdir_r (struct _reent *r, const char *name)
|
||||||
|
{
|
||||||
|
ntfs_log_trace("name %s\n", name);
|
||||||
|
|
||||||
|
ntfs_vd *vd = NULL;
|
||||||
|
ntfs_inode *ni = NULL;
|
||||||
|
|
||||||
|
// Get the volume descriptor for this path
|
||||||
|
vd = ntfsGetVolume(name);
|
||||||
|
if (!vd) {
|
||||||
|
r->_errno = ENODEV;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Lock
|
||||||
|
ntfsLock(vd);
|
||||||
|
|
||||||
|
// Find the directory
|
||||||
|
ni = ntfsOpenEntry(vd, name);
|
||||||
|
if (!ni) {
|
||||||
|
ntfsUnlock(vd);
|
||||||
|
r->_errno = ENOENT;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ensure that this directory is indeed a directory
|
||||||
|
if (!(ni->mrec->flags && MFT_RECORD_IS_DIRECTORY)) {
|
||||||
|
ntfsCloseEntry(vd, ni);
|
||||||
|
ntfsUnlock(vd);
|
||||||
|
r->_errno = ENOTDIR;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close the old current directory (if any)
|
||||||
|
if (vd->cwd_ni)
|
||||||
|
ntfsCloseEntry(vd, vd->cwd_ni);
|
||||||
|
|
||||||
|
// Set the new current directory
|
||||||
|
vd->cwd_ni = ni;
|
||||||
|
|
||||||
|
// Unlock
|
||||||
|
ntfsUnlock(vd);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int ntfs_rename_r (struct _reent *r, const char *oldName, const char *newName)
|
||||||
|
{
|
||||||
|
ntfs_log_trace("oldName %s, newName %s\n", oldName, newName);
|
||||||
|
|
||||||
|
ntfs_vd *vd = NULL;
|
||||||
|
ntfs_inode *ni = NULL;
|
||||||
|
|
||||||
|
// Get the volume descriptor for this path
|
||||||
|
vd = ntfsGetVolume(oldName);
|
||||||
|
if (!vd) {
|
||||||
|
r->_errno = ENODEV;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Lock
|
||||||
|
ntfsLock(vd);
|
||||||
|
|
||||||
|
// You cannot rename between devices
|
||||||
|
if(vd != ntfsGetVolume(newName)) {
|
||||||
|
ntfsUnlock(vd);
|
||||||
|
r->_errno = EXDEV;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check that there is no existing entry with the new name
|
||||||
|
ni = ntfsOpenEntry(vd, newName);
|
||||||
|
if (ni) {
|
||||||
|
ntfsCloseEntry(vd, ni);
|
||||||
|
ntfsUnlock(vd);
|
||||||
|
r->_errno = EEXIST;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Link the old entry with the new one
|
||||||
|
if (ntfsLink(vd, oldName, newName)) {
|
||||||
|
ntfsUnlock(vd);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unlink the old entry
|
||||||
|
if (ntfsUnlink(vd, oldName)) {
|
||||||
|
if (ntfsUnlink(vd, newName)) {
|
||||||
|
ntfsUnlock(vd);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
ntfsUnlock(vd);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unlock
|
||||||
|
ntfsUnlock(vd);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int ntfs_mkdir_r (struct _reent *r, const char *path, int mode)
|
||||||
|
{
|
||||||
|
ntfs_log_trace("path %s, mode %i\n", path, mode);
|
||||||
|
|
||||||
|
ntfs_vd *vd = NULL;
|
||||||
|
ntfs_inode *ni = NULL;
|
||||||
|
|
||||||
|
// Get the volume descriptor for this path
|
||||||
|
vd = ntfsGetVolume(path);
|
||||||
|
if (!vd) {
|
||||||
|
r->_errno = ENODEV;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Lock
|
||||||
|
ntfsLock(vd);
|
||||||
|
|
||||||
|
// Create the directory
|
||||||
|
ni = ntfsCreate(vd, path, S_IFDIR, NULL);
|
||||||
|
if (!ni) {
|
||||||
|
ntfsUnlock(vd);
|
||||||
|
r->_errno = errno;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close the directory
|
||||||
|
ntfsCloseEntry(vd, ni);
|
||||||
|
|
||||||
|
// Unlock
|
||||||
|
ntfsUnlock(vd);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int ntfs_statvfs_r (struct _reent *r, const char *path, struct statvfs *buf)
|
||||||
|
{
|
||||||
|
ntfs_log_trace("path %s, buf %p\n", path, buf);
|
||||||
|
|
||||||
|
ntfs_vd *vd = NULL;
|
||||||
|
s64 size;
|
||||||
|
int delta_bits;
|
||||||
|
|
||||||
|
// Get the volume descriptor for this path
|
||||||
|
vd = ntfsGetVolume(path);
|
||||||
|
if (!vd) {
|
||||||
|
r->_errno = ENODEV;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Short circuit cases were we don't actually have to do anything
|
||||||
|
if (!buf)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
// Lock
|
||||||
|
ntfsLock(vd);
|
||||||
|
|
||||||
|
// Zero out the stat buffer
|
||||||
|
memset(buf, 0, sizeof(struct statvfs));
|
||||||
|
|
||||||
|
// File system block size
|
||||||
|
buf->f_bsize = vd->vol->cluster_size;
|
||||||
|
|
||||||
|
// Fundamental file system block size
|
||||||
|
buf->f_frsize = vd->vol->cluster_size;
|
||||||
|
|
||||||
|
// Total number of blocks on file system in units of f_frsize
|
||||||
|
buf->f_blocks = vd->vol->nr_clusters;
|
||||||
|
|
||||||
|
// Free blocks available for all and for non-privileged processes
|
||||||
|
size = MAX(vd->vol->free_clusters, 0);
|
||||||
|
buf->f_bfree = buf->f_bavail = size;
|
||||||
|
|
||||||
|
// Free inodes on the free space
|
||||||
|
delta_bits = vd->vol->cluster_size_bits - vd->vol->mft_record_size_bits;
|
||||||
|
if (delta_bits >= 0)
|
||||||
|
size <<= delta_bits;
|
||||||
|
else
|
||||||
|
size >>= -delta_bits;
|
||||||
|
|
||||||
|
// Number of inodes at this point in time
|
||||||
|
buf->f_files = (vd->vol->mftbmp_na->allocated_size << 3) + size;
|
||||||
|
|
||||||
|
// Free inodes available for all and for non-privileged processes
|
||||||
|
size += vd->vol->free_mft_records;
|
||||||
|
buf->f_ffree = buf->f_favail = MAX(size, 0);
|
||||||
|
|
||||||
|
// File system id
|
||||||
|
buf->f_fsid = vd->id;
|
||||||
|
|
||||||
|
// Bit mask of f_flag values.
|
||||||
|
buf->f_flag = (NVolReadOnly(vd->vol) ? ST_RDONLY : 0);
|
||||||
|
|
||||||
|
// Maximum length of filenames
|
||||||
|
buf->f_namemax = NTFS_MAX_NAME_LEN;
|
||||||
|
|
||||||
|
// Unlock
|
||||||
|
ntfsUnlock(vd);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* PRIVATE: Callback for directory walking
|
||||||
|
*/
|
||||||
|
int ntfs_readdir_filler (DIR_ITER *dirState, const ntfschar *name, const int name_len, const int name_type,
|
||||||
|
const s64 pos, const MFT_REF mref, const unsigned dt_type)
|
||||||
|
{
|
||||||
|
ntfs_dir_state *dir = STATE(dirState);
|
||||||
|
ntfs_dir_entry *entry = NULL;
|
||||||
|
char *entry_name = NULL;
|
||||||
|
|
||||||
|
// Sanity check
|
||||||
|
if (!dir || !dir->vd) {
|
||||||
|
errno = EINVAL;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ignore DOS file names
|
||||||
|
if (name_type == FILE_NAME_DOS) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Preliminary check that this entry can be enumerated (as described by the volume descriptor)
|
||||||
|
if (MREF(mref) == FILE_root || MREF(mref) >= FILE_first_user || dir->vd->showSystemFiles) {
|
||||||
|
|
||||||
|
// Convert the entry name to our current local
|
||||||
|
if (ntfsUnicodeToLocal(name, name_len, &entry_name, 0) < 0) {
|
||||||
|
ntfs_free(entry);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If this is not the parent or self directory reference
|
||||||
|
if ((strcmp(entry_name, ".") != 0) && (strcmp(entry_name, "..") != 0)) {
|
||||||
|
|
||||||
|
// Open the entry
|
||||||
|
ntfs_inode *ni = ntfs_pathname_to_inode(dir->vd->vol, dir->ni, entry_name);
|
||||||
|
if (!ni) {
|
||||||
|
ntfs_free(entry);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Double check that this entry can be emuerated (as described by the volume descriptor)
|
||||||
|
if (((ni->flags & FILE_ATTR_HIDDEN) && !dir->vd->showHiddenFiles) ||
|
||||||
|
((ni->flags & FILE_ATTR_SYSTEM) && !dir->vd->showSystemFiles)) {
|
||||||
|
ntfs_inode_close(ni);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close the entry
|
||||||
|
ntfs_inode_close(ni);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Allocate a new directory entry
|
||||||
|
entry = ntfs_alloc(sizeof(ntfs_dir_entry));
|
||||||
|
if (!entry)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
// Setup the entry
|
||||||
|
entry->name = entry_name;
|
||||||
|
entry->next = NULL;
|
||||||
|
|
||||||
|
// Link the entry to the directory
|
||||||
|
if (!dir->first) {
|
||||||
|
dir->first = entry;
|
||||||
|
} else {
|
||||||
|
ntfs_dir_entry *last = dir->first;
|
||||||
|
while (last->next) last = last->next;
|
||||||
|
last->next = entry;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
DIR_ITER *ntfs_diropen_r (struct _reent *r, DIR_ITER *dirState, const char *path)
|
||||||
|
{
|
||||||
|
ntfs_log_trace("dirState %p, path %s\n", dirState, path);
|
||||||
|
|
||||||
|
ntfs_dir_state* dir = STATE(dirState);
|
||||||
|
s64 position = 0;
|
||||||
|
|
||||||
|
// Get the volume descriptor for this path
|
||||||
|
dir->vd = ntfsGetVolume(path);
|
||||||
|
if (!dir->vd) {
|
||||||
|
r->_errno = ENODEV;
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Lock
|
||||||
|
ntfsLock(dir->vd);
|
||||||
|
|
||||||
|
// Find the directory
|
||||||
|
dir->ni = ntfsOpenEntry(dir->vd, path);
|
||||||
|
if (!dir->ni) {
|
||||||
|
ntfsUnlock(dir->vd);
|
||||||
|
r->_errno = ENOENT;
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ensure that this directory is indeed a directory
|
||||||
|
if (!(dir->ni->mrec->flags && MFT_RECORD_IS_DIRECTORY)) {
|
||||||
|
ntfsCloseEntry(dir->vd, dir->ni);
|
||||||
|
ntfsUnlock(dir->vd);
|
||||||
|
r->_errno = ENOTDIR;
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read the directory
|
||||||
|
dir->first = dir->current = NULL;
|
||||||
|
if (ntfs_readdir(dir->ni, &position, dirState, (ntfs_filldir_t)ntfs_readdir_filler)) {
|
||||||
|
ntfsCloseDir(dir);
|
||||||
|
ntfsUnlock(dir->vd);
|
||||||
|
r->_errno = errno;
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Move to the first entry in the directory
|
||||||
|
dir->current = dir->first;
|
||||||
|
|
||||||
|
// Update directory times
|
||||||
|
ntfsUpdateTimes(dir->vd, dir->ni, NTFS_UPDATE_ATIME);
|
||||||
|
|
||||||
|
// Insert the directory into the double-linked FILO list of open directories
|
||||||
|
if (dir->vd->firstOpenDir) {
|
||||||
|
dir->nextOpenDir = dir->vd->firstOpenDir;
|
||||||
|
dir->vd->firstOpenDir->prevOpenDir = dir;
|
||||||
|
} else {
|
||||||
|
dir->nextOpenDir = NULL;
|
||||||
|
}
|
||||||
|
dir->prevOpenDir = NULL;
|
||||||
|
dir->vd->firstOpenDir = dir;
|
||||||
|
dir->vd->openDirCount++;
|
||||||
|
|
||||||
|
// Unlock
|
||||||
|
ntfsUnlock(dir->vd);
|
||||||
|
|
||||||
|
return dirState;
|
||||||
|
}
|
||||||
|
|
||||||
|
int ntfs_dirreset_r (struct _reent *r, DIR_ITER *dirState)
|
||||||
|
{
|
||||||
|
ntfs_log_trace("dirState %p\n", dirState);
|
||||||
|
|
||||||
|
ntfs_dir_state* dir = STATE(dirState);
|
||||||
|
|
||||||
|
// Sanity check
|
||||||
|
if (!dir || !dir->vd || !dir->ni) {
|
||||||
|
r->_errno = EBADF;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Lock
|
||||||
|
ntfsLock(dir->vd);
|
||||||
|
|
||||||
|
// Move to the first entry in the directory
|
||||||
|
dir->current = dir->first;
|
||||||
|
|
||||||
|
// Update directory times
|
||||||
|
ntfsUpdateTimes(dir->vd, dir->ni, NTFS_UPDATE_ATIME);
|
||||||
|
|
||||||
|
// Unlock
|
||||||
|
ntfsUnlock(dir->vd);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int ntfs_dirnext_r (struct _reent *r, DIR_ITER *dirState, char *filename, struct stat *filestat)
|
||||||
|
{
|
||||||
|
ntfs_log_trace("dirState %p, filename %p, filestat %p\n", dirState, filename, filestat);
|
||||||
|
//printf("dirnext %p %p %p\n", dirState, filename, filestat);
|
||||||
|
|
||||||
|
ntfs_dir_state* dir = STATE(dirState);
|
||||||
|
ntfs_inode *ni = NULL;
|
||||||
|
|
||||||
|
// Sanity check
|
||||||
|
if (!dir || !dir->vd || !dir->ni) {
|
||||||
|
r->_errno = EBADF;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Lock
|
||||||
|
ntfsLock(dir->vd);
|
||||||
|
|
||||||
|
// Check that there is a entry waiting to be fetched
|
||||||
|
if (!dir->current) {
|
||||||
|
ntfsUnlock(dir->vd);
|
||||||
|
r->_errno = ENOENT;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fetch the current entry
|
||||||
|
strcpy(filename, dir->current->name);
|
||||||
|
if(filestat != NULL) {
|
||||||
|
// ntfsOpenEntry requires full path, or path relative to cwd
|
||||||
|
// so we set cwd temporarily
|
||||||
|
ntfs_inode *tmp_cwd_ni = dir->vd->cwd_ni;
|
||||||
|
dir->vd->cwd_ni = dir->ni;
|
||||||
|
ni = ntfsOpenEntry(dir->vd, dir->current->name);
|
||||||
|
dir->vd->cwd_ni = tmp_cwd_ni;
|
||||||
|
//printf("openEn: %p\n", ni);
|
||||||
|
if (ni) {
|
||||||
|
ntfsStat(dir->vd, ni, filestat);
|
||||||
|
ntfsCloseEntry(dir->vd, ni);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Move to the next entry in the directory
|
||||||
|
dir->current = dir->current->next;
|
||||||
|
|
||||||
|
// Update directory times
|
||||||
|
ntfsUpdateTimes(dir->vd, dir->ni, NTFS_UPDATE_ATIME);
|
||||||
|
|
||||||
|
// Unlock
|
||||||
|
ntfsUnlock(dir->vd);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int ntfs_dirclose_r (struct _reent *r, DIR_ITER *dirState)
|
||||||
|
{
|
||||||
|
ntfs_log_trace("dirState %p\n", dirState);
|
||||||
|
|
||||||
|
ntfs_dir_state* dir = STATE(dirState);
|
||||||
|
|
||||||
|
// Sanity check
|
||||||
|
if (!dir || !dir->vd) {
|
||||||
|
r->_errno = EBADF;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Lock
|
||||||
|
ntfsLock(dir->vd);
|
||||||
|
|
||||||
|
// Close the directory
|
||||||
|
ntfsCloseDir(dir);
|
||||||
|
|
||||||
|
// Remove the directory from the double-linked FILO list of open directories
|
||||||
|
dir->vd->openDirCount--;
|
||||||
|
if (dir->nextOpenDir)
|
||||||
|
dir->nextOpenDir->prevOpenDir = dir->prevOpenDir;
|
||||||
|
if (dir->prevOpenDir)
|
||||||
|
dir->prevOpenDir->nextOpenDir = dir->nextOpenDir;
|
||||||
|
else
|
||||||
|
dir->vd->firstOpenDir = dir->nextOpenDir;
|
||||||
|
|
||||||
|
// Unlock
|
||||||
|
ntfsUnlock(dir->vd);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
67
source/libntfs/ntfsdir.h
Normal file
67
source/libntfs/ntfsdir.h
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
/**
|
||||||
|
* ntfs_dir.c - devoptab directory routines for NTFS-based devices.
|
||||||
|
*
|
||||||
|
* Copyright (c) 2009 Rhys "Shareese" Koedijk
|
||||||
|
* Copyright (c) 2006 Michael "Chishm" Chisholm
|
||||||
|
*
|
||||||
|
* This program/include file 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/include file 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; if not, write to the Free Software Foundation,
|
||||||
|
* Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _NTFSDIR_H
|
||||||
|
#define _NTFSDIR_H
|
||||||
|
|
||||||
|
#include "ntfsinternal.h"
|
||||||
|
#include <sys/reent.h>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ntfs_dir_entry - Directory entry
|
||||||
|
*/
|
||||||
|
typedef struct _ntfs_dir_entry {
|
||||||
|
char *name;
|
||||||
|
struct _ntfs_dir_entry *next;
|
||||||
|
} ntfs_dir_entry;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ntfs_dir_state - Directory state
|
||||||
|
*/
|
||||||
|
typedef struct _ntfs_dir_state {
|
||||||
|
ntfs_vd *vd; /* Volume this directory belongs to */
|
||||||
|
ntfs_inode *ni; /* Directory descriptor */
|
||||||
|
ntfs_dir_entry *first; /* The first entry in the directory */
|
||||||
|
ntfs_dir_entry *current; /* The current entry in the directory */
|
||||||
|
struct _ntfs_dir_state *prevOpenDir; /* The previous entry in a double-linked FILO list of open directories */
|
||||||
|
struct _ntfs_dir_state *nextOpenDir; /* The next entry in a double-linked FILO list of open directories */
|
||||||
|
} ntfs_dir_state;
|
||||||
|
|
||||||
|
/* Directory state routines */
|
||||||
|
void ntfsCloseDir (ntfs_dir_state *file);
|
||||||
|
|
||||||
|
/* Gekko devoptab directory routines for NTFS-based devices */
|
||||||
|
extern int ntfs_stat_r (struct _reent *r, const char *path, struct stat *st);
|
||||||
|
extern int ntfs_link_r (struct _reent *r, const char *existing, const char *newLink);
|
||||||
|
extern int ntfs_unlink_r (struct _reent *r, const char *name);
|
||||||
|
extern int ntfs_chdir_r (struct _reent *r, const char *name);
|
||||||
|
extern int ntfs_rename_r (struct _reent *r, const char *oldName, const char *newName);
|
||||||
|
extern int ntfs_mkdir_r (struct _reent *r, const char *path, int mode);
|
||||||
|
extern int ntfs_statvfs_r (struct _reent *r, const char *path, struct statvfs *buf);
|
||||||
|
|
||||||
|
/* Gekko devoptab directory walking routines for NTFS-based devices */
|
||||||
|
extern DIR_ITER *ntfs_diropen_r (struct _reent *r, DIR_ITER *dirState, const char *path);
|
||||||
|
extern int ntfs_dirreset_r (struct _reent *r, DIR_ITER *dirState);
|
||||||
|
extern int ntfs_dirnext_r (struct _reent *r, DIR_ITER *dirState, char *filename, struct stat *filestat);
|
||||||
|
extern int ntfs_dirclose_r (struct _reent *r, DIR_ITER *dirState);
|
||||||
|
|
||||||
|
#endif /* _NTFSDIR_H */
|
||||||
|
|
528
source/libntfs/ntfsfile.c
Normal file
528
source/libntfs/ntfsfile.c
Normal file
@ -0,0 +1,528 @@
|
|||||||
|
/**
|
||||||
|
* ntfsfile.c - devoptab file routines for NTFS-based devices.
|
||||||
|
*
|
||||||
|
* Copyright (c) 2009 Rhys "Shareese" Koedijk
|
||||||
|
* Copyright (c) 2006 Michael "Chishm" Chisholm
|
||||||
|
*
|
||||||
|
* This program/include file 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/include file 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; if not, write to the Free Software Foundation,
|
||||||
|
* Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifdef HAVE_CONFIG_H
|
||||||
|
#include "config.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef HAVE_STDLIB_H
|
||||||
|
#include <stdlib.h>
|
||||||
|
#endif
|
||||||
|
#ifdef HAVE_SYS_STAT_H
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#endif
|
||||||
|
#ifdef HAVE_FCNTL_H
|
||||||
|
#include <fcntl.h>
|
||||||
|
#endif
|
||||||
|
#ifdef HAVE_ERRNO_H
|
||||||
|
#include <errno.h>
|
||||||
|
#endif
|
||||||
|
#ifdef HAVE_STRING_H
|
||||||
|
#include <string.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "ntfsinternal.h"
|
||||||
|
#include "ntfsfile.h"
|
||||||
|
#include "ntfs.h"
|
||||||
|
|
||||||
|
#define STATE(x) ((ntfs_file_state*)x)
|
||||||
|
|
||||||
|
void ntfsCloseFile (ntfs_file_state *file)
|
||||||
|
{
|
||||||
|
// Sanity check
|
||||||
|
if (!file || !file->vd)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Special case fix ups for compressed and/or encrypted files
|
||||||
|
if (file->compressed)
|
||||||
|
ntfs_attr_pclose(file->data_na);
|
||||||
|
if (file->encrypted)
|
||||||
|
ntfs_efs_fixup_attribute(NULL, file->data_na);
|
||||||
|
|
||||||
|
// Close the file data attribute (if open)
|
||||||
|
if (file->data_na)
|
||||||
|
ntfs_attr_close(file->data_na);
|
||||||
|
|
||||||
|
// Sync the file (and its attributes) to disc
|
||||||
|
if(file->write)
|
||||||
|
ntfsSync(file->vd, file->ni);
|
||||||
|
|
||||||
|
// Close the file (if open)
|
||||||
|
if (file->ni)
|
||||||
|
ntfsCloseEntry(file->vd, file->ni);
|
||||||
|
|
||||||
|
// Reset the file state
|
||||||
|
file->ni = NULL;
|
||||||
|
file->data_na = NULL;
|
||||||
|
file->flags = 0;
|
||||||
|
file->read = false;
|
||||||
|
file->write = false;
|
||||||
|
file->append = false;
|
||||||
|
file->pos = 0;
|
||||||
|
file->len = 0;
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int ntfs_open_r (struct _reent *r, void *fileStruct, const char *path, int flags, int mode)
|
||||||
|
{
|
||||||
|
ntfs_log_trace("fileStruct %p, path %s, flags %i, mode %i\n", fileStruct, path, flags, mode);
|
||||||
|
|
||||||
|
ntfs_file_state* file = STATE(fileStruct);
|
||||||
|
|
||||||
|
// Get the volume descriptor for this path
|
||||||
|
file->vd = ntfsGetVolume(path);
|
||||||
|
if (!file->vd) {
|
||||||
|
r->_errno = ENODEV;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Lock
|
||||||
|
ntfsLock(file->vd);
|
||||||
|
|
||||||
|
// Determine which mode the file is opened for
|
||||||
|
file->flags = flags;
|
||||||
|
if ((flags & 0x03) == O_RDONLY) {
|
||||||
|
file->read = true;
|
||||||
|
file->write = false;
|
||||||
|
file->append = false;
|
||||||
|
} else if ((flags & 0x03) == O_WRONLY) {
|
||||||
|
file->read = false;
|
||||||
|
file->write = true;
|
||||||
|
file->append = (flags & O_APPEND);
|
||||||
|
} else if ((flags & 0x03) == O_RDWR) {
|
||||||
|
file->read = true;
|
||||||
|
file->write = true;
|
||||||
|
file->append = (flags & O_APPEND);
|
||||||
|
} else {
|
||||||
|
r->_errno = EACCES;
|
||||||
|
ntfsUnlock(file->vd);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Try and find the file and (if found) ensure that it is not a directory
|
||||||
|
file->ni = ntfsOpenEntry(file->vd, path);
|
||||||
|
if (file->ni && (file->ni->mrec->flags & MFT_RECORD_IS_DIRECTORY)) {
|
||||||
|
ntfsCloseEntry(file->vd, file->ni);
|
||||||
|
ntfsUnlock(file->vd);
|
||||||
|
r->_errno = EISDIR;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Are we creating this file?
|
||||||
|
if (flags & O_CREAT) {
|
||||||
|
|
||||||
|
// The file SHOULD NOT already exist
|
||||||
|
if (file->ni) {
|
||||||
|
ntfsCloseEntry(file->vd, file->ni);
|
||||||
|
ntfsUnlock(file->vd);
|
||||||
|
r->_errno = EEXIST;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create the file
|
||||||
|
file->ni = ntfsCreate(file->vd, path, S_IFREG, NULL);
|
||||||
|
if (!file->ni) {
|
||||||
|
ntfsUnlock(file->vd);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sanity check, the file should be open by now
|
||||||
|
if (!file->ni) {
|
||||||
|
ntfsUnlock(file->vd);
|
||||||
|
r->_errno = ENOENT;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Open the files data attribute
|
||||||
|
file->data_na = ntfs_attr_open(file->ni, AT_DATA, AT_UNNAMED, 0);
|
||||||
|
if(!file->data_na) {
|
||||||
|
ntfsCloseEntry(file->vd, file->ni);
|
||||||
|
ntfsUnlock(file->vd);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Determine if this files data is compressed and/or encrypted
|
||||||
|
file->compressed = NAttrCompressed(file->data_na) || (file->ni->flags & FILE_ATTR_COMPRESSED);
|
||||||
|
file->encrypted = NAttrEncrypted(file->data_na) || (file->ni->flags & FILE_ATTR_ENCRYPTED);
|
||||||
|
|
||||||
|
// We cannot read/write encrypted files
|
||||||
|
if (file->encrypted) {
|
||||||
|
ntfs_attr_close(file->data_na);
|
||||||
|
ntfsCloseEntry(file->vd, file->ni);
|
||||||
|
ntfsUnlock(file->vd);
|
||||||
|
r->_errno = EACCES;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Make sure we aren't trying to write to a read-only file
|
||||||
|
if ((file->ni->flags & FILE_ATTR_READONLY) && file->write) {
|
||||||
|
ntfs_attr_close(file->data_na);
|
||||||
|
ntfsCloseEntry(file->vd, file->ni);
|
||||||
|
ntfsUnlock(file->vd);
|
||||||
|
r->_errno = EROFS;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Truncate the file if requested
|
||||||
|
if ((flags & O_TRUNC) && file->write) {
|
||||||
|
if (ntfs_attr_truncate(file->data_na, 0)) {
|
||||||
|
ntfs_attr_close(file->data_na);
|
||||||
|
ntfsCloseEntry(file->vd, file->ni);
|
||||||
|
ntfsUnlock(file->vd);
|
||||||
|
r->_errno = errno;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set the files current position and length
|
||||||
|
file->pos = 0;
|
||||||
|
file->len = file->data_na->data_size;
|
||||||
|
|
||||||
|
// Update file times
|
||||||
|
ntfsUpdateTimes(file->vd, file->ni, NTFS_UPDATE_ATIME);
|
||||||
|
|
||||||
|
// Insert the file into the double-linked FILO list of open files
|
||||||
|
if (file->vd->firstOpenFile) {
|
||||||
|
file->nextOpenFile = file->vd->firstOpenFile;
|
||||||
|
file->vd->firstOpenFile->prevOpenFile = file;
|
||||||
|
} else {
|
||||||
|
file->nextOpenFile = NULL;
|
||||||
|
}
|
||||||
|
file->prevOpenFile = NULL;
|
||||||
|
file->vd->firstOpenFile = file;
|
||||||
|
file->vd->openFileCount++;
|
||||||
|
|
||||||
|
// Unlock
|
||||||
|
ntfsUnlock(file->vd);
|
||||||
|
|
||||||
|
return (int)fileStruct;
|
||||||
|
}
|
||||||
|
|
||||||
|
int ntfs_close_r (struct _reent *r, int fd)
|
||||||
|
{
|
||||||
|
ntfs_log_trace("fd %p\n", fd);
|
||||||
|
|
||||||
|
ntfs_file_state* file = STATE(fd);
|
||||||
|
|
||||||
|
// Sanity check
|
||||||
|
if (!file || !file->vd) {
|
||||||
|
r->_errno = EBADF;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Lock
|
||||||
|
ntfsLock(file->vd);
|
||||||
|
|
||||||
|
// Close the file
|
||||||
|
ntfsCloseFile(file);
|
||||||
|
|
||||||
|
// Remove the file from the double-linked FILO list of open files
|
||||||
|
file->vd->openFileCount--;
|
||||||
|
if (file->nextOpenFile)
|
||||||
|
file->nextOpenFile->prevOpenFile = file->prevOpenFile;
|
||||||
|
if (file->prevOpenFile)
|
||||||
|
file->prevOpenFile->nextOpenFile = file->nextOpenFile;
|
||||||
|
else
|
||||||
|
file->vd->firstOpenFile = file->nextOpenFile;
|
||||||
|
|
||||||
|
// Unlock
|
||||||
|
ntfsUnlock(file->vd);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
ssize_t ntfs_write_r (struct _reent *r, int fd, const char *ptr, size_t len)
|
||||||
|
{
|
||||||
|
ntfs_log_trace("fd %p, ptr %p, len %Li\n", fd, ptr, len);
|
||||||
|
|
||||||
|
ntfs_file_state* file = STATE(fd);
|
||||||
|
ssize_t written = 0;
|
||||||
|
off_t old_pos = 0;
|
||||||
|
|
||||||
|
// Sanity check
|
||||||
|
if (!file || !file->vd || !file->ni || !file->data_na) {
|
||||||
|
r->_errno = EINVAL;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Short circuit cases where we don't actually have to do anything
|
||||||
|
if (!ptr || len <= 0) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Lock
|
||||||
|
ntfsLock(file->vd);
|
||||||
|
|
||||||
|
// Check that we are allowed to write to this file
|
||||||
|
if (!file->write) {
|
||||||
|
ntfsUnlock(file->vd);
|
||||||
|
r->_errno = EACCES;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we are in append mode, backup the current position and move to the end of the file
|
||||||
|
if (file->append) {
|
||||||
|
old_pos = file->pos;
|
||||||
|
file->pos = file->len;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write to the files data atrribute
|
||||||
|
while (len) {
|
||||||
|
ssize_t ret = ntfs_attr_pwrite(file->data_na, file->pos, len, ptr);
|
||||||
|
if (ret <= 0) {
|
||||||
|
ntfsUnlock(file->vd);
|
||||||
|
r->_errno = errno;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
len -= ret;
|
||||||
|
file->pos += ret;
|
||||||
|
written += ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we are in append mode, restore the current position to were it was prior to this write
|
||||||
|
if (file->append) {
|
||||||
|
file->pos = old_pos;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mark the file for archiving (if we actually wrote something)
|
||||||
|
if (written)
|
||||||
|
file->ni->flags |= FILE_ATTR_ARCHIVE;
|
||||||
|
|
||||||
|
// Update file times (if we actually wrote something)
|
||||||
|
if (written)
|
||||||
|
ntfsUpdateTimes(file->vd, file->ni, NTFS_UPDATE_MCTIME);
|
||||||
|
|
||||||
|
// Update the files data length
|
||||||
|
file->len = file->data_na->data_size;
|
||||||
|
|
||||||
|
// Unlock
|
||||||
|
ntfsUnlock(file->vd);
|
||||||
|
|
||||||
|
return written;
|
||||||
|
}
|
||||||
|
|
||||||
|
ssize_t ntfs_read_r (struct _reent *r, int fd, char *ptr, size_t len)
|
||||||
|
{
|
||||||
|
ntfs_log_trace("fd %p, ptr %p, len %Li\n", fd, ptr, len);
|
||||||
|
|
||||||
|
ntfs_file_state* file = STATE(fd);
|
||||||
|
ssize_t read = 0;
|
||||||
|
|
||||||
|
// Sanity check
|
||||||
|
if (!file || !file->vd || !file->ni || !file->data_na) {
|
||||||
|
r->_errno = EINVAL;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Short circuit cases where we don't actually have to do anything
|
||||||
|
if (!ptr || len <= 0) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Lock
|
||||||
|
ntfsLock(file->vd);
|
||||||
|
|
||||||
|
// Check that we are allowed to read from this file
|
||||||
|
if (!file->read) {
|
||||||
|
ntfsUnlock(file->vd);
|
||||||
|
r->_errno = EACCES;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Don't read past the end of file
|
||||||
|
if (file->pos + len > file->len) {
|
||||||
|
r->_errno = EOVERFLOW;
|
||||||
|
len = file->len - file->pos;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read from the files data attribute
|
||||||
|
while (len) {
|
||||||
|
ssize_t ret = ntfs_attr_pread(file->data_na, file->pos, len, ptr);
|
||||||
|
if (ret <= 0 || ret > len) {
|
||||||
|
ntfsUnlock(file->vd);
|
||||||
|
r->_errno = errno;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
ptr += ret;
|
||||||
|
len -= ret;
|
||||||
|
file->pos += ret;
|
||||||
|
read += ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update file times (if we actually read something)
|
||||||
|
if (read)
|
||||||
|
ntfsUpdateTimes(file->vd, file->ni, NTFS_UPDATE_ATIME);
|
||||||
|
|
||||||
|
// Unlock
|
||||||
|
ntfsUnlock(file->vd);
|
||||||
|
|
||||||
|
return read;
|
||||||
|
}
|
||||||
|
|
||||||
|
off_t ntfs_seek_r (struct _reent *r, int fd, off_t pos, int dir)
|
||||||
|
{
|
||||||
|
ntfs_log_trace("fd %p, pos %Li, dir %i\n", fd, pos, dir);
|
||||||
|
|
||||||
|
ntfs_file_state* file = STATE(fd);
|
||||||
|
off_t position = 0;
|
||||||
|
|
||||||
|
// Sanity check
|
||||||
|
if (!file || !file->vd || !file->ni || !file->data_na) {
|
||||||
|
r->_errno = EINVAL;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Lock
|
||||||
|
ntfsLock(file->vd);
|
||||||
|
|
||||||
|
// Set the files current position
|
||||||
|
switch(dir) {
|
||||||
|
case SEEK_SET: position = file->pos = MIN(MAX(pos, 0), file->len); break;
|
||||||
|
case SEEK_CUR: position = file->pos = MIN(MAX(file->pos + pos, 0), file->len); break;
|
||||||
|
case SEEK_END: position = file->pos = MIN(MAX(file->len + pos, 0), file->len); break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unlock
|
||||||
|
ntfsUnlock(file->vd);
|
||||||
|
|
||||||
|
return position;
|
||||||
|
}
|
||||||
|
int ntfs_fstat_r (struct _reent *r, int fd, struct stat *st)
|
||||||
|
{
|
||||||
|
ntfs_log_trace("fd %p\n", fd);
|
||||||
|
|
||||||
|
ntfs_file_state* file = STATE(fd);
|
||||||
|
int ret = 0;
|
||||||
|
|
||||||
|
// Sanity check
|
||||||
|
if (!file || !file->vd || !file->ni || !file->data_na) {
|
||||||
|
r->_errno = EINVAL;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Short circuit cases were we don't actually have to do anything
|
||||||
|
if (!st)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
// Get the file stats
|
||||||
|
ret = ntfsStat(file->vd, file->ni, st);
|
||||||
|
if (ret)
|
||||||
|
r->_errno = errno;
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
int ntfs_ftruncate_r (struct _reent *r, int fd, off_t len)
|
||||||
|
{
|
||||||
|
ntfs_log_trace("fd %p, len %Li\n", fd, len);
|
||||||
|
|
||||||
|
ntfs_file_state* file = STATE(fd);
|
||||||
|
|
||||||
|
// Sanity check
|
||||||
|
if (!file || !file->vd || !file->ni || !file->data_na) {
|
||||||
|
r->_errno = EINVAL;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Lock
|
||||||
|
ntfsLock(file->vd);
|
||||||
|
|
||||||
|
// Check that we are allowed to write to this file
|
||||||
|
if (!file->write) {
|
||||||
|
ntfsUnlock(file->vd);
|
||||||
|
r->_errno = EACCES;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// For compressed files, only deleting and expanding contents are implemented
|
||||||
|
if (file->compressed &&
|
||||||
|
len > 0 &&
|
||||||
|
len < file->data_na->initialized_size) {
|
||||||
|
ntfsUnlock(file->vd);
|
||||||
|
r->_errno = EOPNOTSUPP;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Resize the files data attribute, either by expanding or truncating
|
||||||
|
if (file->compressed && len > file->data_na->initialized_size) {
|
||||||
|
char zero = 0;
|
||||||
|
if (ntfs_attr_pwrite(file->data_na, len - 1, 1, &zero) <= 0) {
|
||||||
|
ntfsUnlock(file->vd);
|
||||||
|
r->_errno = errno;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (ntfs_attr_truncate(file->data_na, len)) {
|
||||||
|
ntfsUnlock(file->vd);
|
||||||
|
r->_errno = errno;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mark the file for archiving (if we actually changed something)
|
||||||
|
if (file->len != file->data_na->data_size)
|
||||||
|
file->ni->flags |= FILE_ATTR_ARCHIVE;
|
||||||
|
|
||||||
|
// Update file times (if we actually changed something)
|
||||||
|
if (file->len != file->data_na->data_size)
|
||||||
|
ntfsUpdateTimes(file->vd, file->ni, NTFS_UPDATE_MCTIME);
|
||||||
|
|
||||||
|
// Update the files data length
|
||||||
|
file->len = file->data_na->data_size;
|
||||||
|
|
||||||
|
// Sync the file (and its attributes) to disc
|
||||||
|
ntfsSync(file->vd, file->ni);
|
||||||
|
|
||||||
|
// Unlock
|
||||||
|
ntfsUnlock(file->vd);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int ntfs_fsync_r (struct _reent *r, int fd)
|
||||||
|
{
|
||||||
|
ntfs_log_trace("fd %p\n", fd);
|
||||||
|
|
||||||
|
ntfs_file_state* file = STATE(fd);
|
||||||
|
int ret = 0;
|
||||||
|
|
||||||
|
// Sanity check
|
||||||
|
if (!file || !file->vd || !file->ni || !file->data_na) {
|
||||||
|
r->_errno = EINVAL;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Lock
|
||||||
|
ntfsLock(file->vd);
|
||||||
|
|
||||||
|
// Sync the file (and its attributes) to disc
|
||||||
|
ret = ntfsSync(file->vd, file->ni);
|
||||||
|
if (ret)
|
||||||
|
r->_errno = errno;
|
||||||
|
|
||||||
|
// Unlock
|
||||||
|
ntfsUnlock(file->vd);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
67
source/libntfs/ntfsfile.h
Normal file
67
source/libntfs/ntfsfile.h
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
/**
|
||||||
|
* ntfsfile.c - devoptab file routines for NTFS-based devices.
|
||||||
|
*
|
||||||
|
* Copyright (c) 2009 Rhys "Shareese" Koedijk
|
||||||
|
* Copyright (c) 2006 Michael "Chishm" Chisholm
|
||||||
|
*
|
||||||
|
* This program/include file 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/include file 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; if not, write to the Free Software Foundation,
|
||||||
|
* Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _NTFSFILE_H
|
||||||
|
#define _NTFSFILE_H
|
||||||
|
|
||||||
|
#ifdef HAVE_CONFIG_H
|
||||||
|
#include "config.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "ntfsinternal.h"
|
||||||
|
#include <sys/reent.h>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ntfs_file_state - File state
|
||||||
|
*/
|
||||||
|
typedef struct _ntfs_file_state {
|
||||||
|
ntfs_vd *vd; /* Volume this file belongs to */
|
||||||
|
ntfs_inode *ni; /* File descriptor */
|
||||||
|
ntfs_attr *data_na; /* File data descriptor */
|
||||||
|
int flags; /* Opening flags */
|
||||||
|
bool read; /* True if allowed to read from file */
|
||||||
|
bool write; /* True if allowed to write to file */
|
||||||
|
bool append; /* True if allowed to append to file */
|
||||||
|
bool compressed; /* True if file data is compressed */
|
||||||
|
bool encrypted; /* True if file data is encryted */
|
||||||
|
off_t pos; /* Current position within the file (in bytes) */
|
||||||
|
//size_t len; /* Total length of the file (in bytes) */
|
||||||
|
//size_t is 32 bit, off_t is signed, so use u64 for len!
|
||||||
|
u64 len; /* Total length of the file (in bytes) */
|
||||||
|
struct _ntfs_file_state *prevOpenFile; /* The previous entry in a double-linked FILO list of open files */
|
||||||
|
struct _ntfs_file_state *nextOpenFile; /* The next entry in a double-linked FILO list of open files */
|
||||||
|
} ntfs_file_state;
|
||||||
|
|
||||||
|
/* File state routines */
|
||||||
|
void ntfsCloseFile (ntfs_file_state *file);
|
||||||
|
|
||||||
|
/* Gekko devoptab file routines for NTFS-based devices */
|
||||||
|
extern int ntfs_open_r (struct _reent *r, void *fileStruct, const char *path, int flags, int mode);
|
||||||
|
extern int ntfs_close_r (struct _reent *r, int fd);
|
||||||
|
extern ssize_t ntfs_write_r (struct _reent *r, int fd, const char *ptr, size_t len);
|
||||||
|
extern ssize_t ntfs_read_r (struct _reent *r, int fd, char *ptr, size_t len);
|
||||||
|
extern off_t ntfs_seek_r (struct _reent *r, int fd, off_t pos, int dir);
|
||||||
|
extern int ntfs_fstat_r (struct _reent *r, int fd, struct stat *st);
|
||||||
|
extern int ntfs_ftruncate_r (struct _reent *r, int fd, off_t len);
|
||||||
|
extern int ntfs_fsync_r (struct _reent *r, int fd);
|
||||||
|
|
||||||
|
#endif /* _NTFSFILE_H */
|
||||||
|
|
568
source/libntfs/ntfsfile_frag.c
Normal file
568
source/libntfs/ntfsfile_frag.c
Normal file
@ -0,0 +1,568 @@
|
|||||||
|
/**
|
||||||
|
* ntfsfile.c - devoptab file routines for NTFS-based devices.
|
||||||
|
*
|
||||||
|
* Copyright (c) 2009 Rhys "Shareese" Koedijk
|
||||||
|
* Copyright (c) 2006 Michael "Chishm" Chisholm
|
||||||
|
*
|
||||||
|
* This program/include file 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/include file 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; if not, write to the Free Software Foundation,
|
||||||
|
* Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifdef HAVE_CONFIG_H
|
||||||
|
#include "config.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef HAVE_STDLIB_H
|
||||||
|
#include <stdlib.h>
|
||||||
|
#endif
|
||||||
|
#ifdef HAVE_SYS_STAT_H
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#endif
|
||||||
|
#ifdef HAVE_FCNTL_H
|
||||||
|
#include <fcntl.h>
|
||||||
|
#endif
|
||||||
|
#ifdef HAVE_ERRNO_H
|
||||||
|
#include <errno.h>
|
||||||
|
#endif
|
||||||
|
#ifdef HAVE_STRING_H
|
||||||
|
#include <string.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "ntfsinternal.h"
|
||||||
|
#include "ntfsfile.h"
|
||||||
|
#include "ntfs.h"
|
||||||
|
|
||||||
|
#define STATE(x) ((ntfs_file_state*)x)
|
||||||
|
|
||||||
|
// for easier comparision of ntfsfile.c against ntfsfile_frag.c
|
||||||
|
// everything is included but ifdef-ed out
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
|
||||||
|
void ntfsCloseFile (ntfs_file_state *file)
|
||||||
|
{
|
||||||
|
// Sanity check
|
||||||
|
if (!file || !file->vd)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Special case fix ups for compressed and/or encrypted files
|
||||||
|
if (file->compressed)
|
||||||
|
ntfs_attr_pclose(file->data_na);
|
||||||
|
if (file->encrypted)
|
||||||
|
ntfs_efs_fixup_attribute(NULL, file->data_na);
|
||||||
|
|
||||||
|
// Close the file data attribute (if open)
|
||||||
|
if (file->data_na)
|
||||||
|
ntfs_attr_close(file->data_na);
|
||||||
|
|
||||||
|
// Sync the file (and its attributes) to disc
|
||||||
|
if(file->write)
|
||||||
|
ntfsSync(file->vd, file->ni);
|
||||||
|
|
||||||
|
// Close the file (if open)
|
||||||
|
if (file->ni)
|
||||||
|
ntfsCloseEntry(file->vd, file->ni);
|
||||||
|
|
||||||
|
// Reset the file state
|
||||||
|
file->ni = NULL;
|
||||||
|
file->data_na = NULL;
|
||||||
|
file->flags = 0;
|
||||||
|
file->read = false;
|
||||||
|
file->write = false;
|
||||||
|
file->append = false;
|
||||||
|
file->pos = 0;
|
||||||
|
file->len = 0;
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int ntfs_open_r (struct _reent *r, void *fileStruct, const char *path, int flags, int mode)
|
||||||
|
{
|
||||||
|
ntfs_log_trace("fileStruct %p, path %s, flags %i, mode %i\n", fileStruct, path, flags, mode);
|
||||||
|
|
||||||
|
ntfs_file_state* file = STATE(fileStruct);
|
||||||
|
|
||||||
|
// Get the volume descriptor for this path
|
||||||
|
file->vd = ntfsGetVolume(path);
|
||||||
|
if (!file->vd) {
|
||||||
|
r->_errno = ENODEV;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Lock
|
||||||
|
ntfsLock(file->vd);
|
||||||
|
|
||||||
|
// Determine which mode the file is opened for
|
||||||
|
file->flags = flags;
|
||||||
|
if ((flags & 0x03) == O_RDONLY) {
|
||||||
|
file->read = true;
|
||||||
|
file->write = false;
|
||||||
|
file->append = false;
|
||||||
|
} else if ((flags & 0x03) == O_WRONLY) {
|
||||||
|
file->read = false;
|
||||||
|
file->write = true;
|
||||||
|
file->append = (flags & O_APPEND);
|
||||||
|
} else if ((flags & 0x03) == O_RDWR) {
|
||||||
|
file->read = true;
|
||||||
|
file->write = true;
|
||||||
|
file->append = (flags & O_APPEND);
|
||||||
|
} else {
|
||||||
|
r->_errno = EACCES;
|
||||||
|
ntfsUnlock(file->vd);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Try and find the file and (if found) ensure that it is not a directory
|
||||||
|
file->ni = ntfsOpenEntry(file->vd, path);
|
||||||
|
if (file->ni && (file->ni->mrec->flags & MFT_RECORD_IS_DIRECTORY)) {
|
||||||
|
ntfsCloseEntry(file->vd, file->ni);
|
||||||
|
ntfsUnlock(file->vd);
|
||||||
|
r->_errno = EISDIR;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Are we creating this file?
|
||||||
|
if (flags & O_CREAT) {
|
||||||
|
|
||||||
|
// The file SHOULD NOT already exist
|
||||||
|
if (file->ni) {
|
||||||
|
ntfsCloseEntry(file->vd, file->ni);
|
||||||
|
ntfsUnlock(file->vd);
|
||||||
|
r->_errno = EEXIST;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create the file
|
||||||
|
file->ni = ntfsCreate(file->vd, path, S_IFREG, NULL);
|
||||||
|
if (!file->ni) {
|
||||||
|
ntfsUnlock(file->vd);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sanity check, the file should be open by now
|
||||||
|
if (!file->ni) {
|
||||||
|
ntfsUnlock(file->vd);
|
||||||
|
r->_errno = ENOENT;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Open the files data attribute
|
||||||
|
file->data_na = ntfs_attr_open(file->ni, AT_DATA, AT_UNNAMED, 0);
|
||||||
|
if(!file->data_na) {
|
||||||
|
ntfsCloseEntry(file->vd, file->ni);
|
||||||
|
ntfsUnlock(file->vd);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Determine if this files data is compressed and/or encrypted
|
||||||
|
file->compressed = NAttrCompressed(file->data_na) || (file->ni->flags & FILE_ATTR_COMPRESSED);
|
||||||
|
file->encrypted = NAttrEncrypted(file->data_na) || (file->ni->flags & FILE_ATTR_ENCRYPTED);
|
||||||
|
|
||||||
|
// We cannot read/write encrypted files
|
||||||
|
if (file->encrypted) {
|
||||||
|
ntfs_attr_close(file->data_na);
|
||||||
|
ntfsCloseEntry(file->vd, file->ni);
|
||||||
|
ntfsUnlock(file->vd);
|
||||||
|
r->_errno = EACCES;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Make sure we aren't trying to write to a read-only file
|
||||||
|
if ((file->ni->flags & FILE_ATTR_READONLY) && file->write) {
|
||||||
|
ntfs_attr_close(file->data_na);
|
||||||
|
ntfsCloseEntry(file->vd, file->ni);
|
||||||
|
ntfsUnlock(file->vd);
|
||||||
|
r->_errno = EROFS;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Truncate the file if requested
|
||||||
|
if ((flags & O_TRUNC) && file->write) {
|
||||||
|
if (ntfs_attr_truncate(file->data_na, 0)) {
|
||||||
|
ntfs_attr_close(file->data_na);
|
||||||
|
ntfsCloseEntry(file->vd, file->ni);
|
||||||
|
ntfsUnlock(file->vd);
|
||||||
|
r->_errno = errno;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set the files current position and length
|
||||||
|
file->pos = 0;
|
||||||
|
file->len = file->data_na->data_size;
|
||||||
|
|
||||||
|
// Update file times
|
||||||
|
ntfsUpdateTimes(file->vd, file->ni, NTFS_UPDATE_ATIME);
|
||||||
|
|
||||||
|
// Insert the file into the double-linked FILO list of open files
|
||||||
|
if (file->vd->firstOpenFile) {
|
||||||
|
file->nextOpenFile = file->vd->firstOpenFile;
|
||||||
|
file->vd->firstOpenFile->prevOpenFile = file;
|
||||||
|
} else {
|
||||||
|
file->nextOpenFile = NULL;
|
||||||
|
}
|
||||||
|
file->prevOpenFile = NULL;
|
||||||
|
file->vd->firstOpenFile = file;
|
||||||
|
file->vd->openFileCount++;
|
||||||
|
|
||||||
|
// Unlock
|
||||||
|
ntfsUnlock(file->vd);
|
||||||
|
|
||||||
|
return (int)fileStruct;
|
||||||
|
}
|
||||||
|
|
||||||
|
int ntfs_close_r (struct _reent *r, int fd)
|
||||||
|
{
|
||||||
|
ntfs_log_trace("fd %p\n", fd);
|
||||||
|
|
||||||
|
ntfs_file_state* file = STATE(fd);
|
||||||
|
|
||||||
|
// Sanity check
|
||||||
|
if (!file || !file->vd) {
|
||||||
|
r->_errno = EBADF;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Lock
|
||||||
|
ntfsLock(file->vd);
|
||||||
|
|
||||||
|
// Close the file
|
||||||
|
ntfsCloseFile(file);
|
||||||
|
|
||||||
|
// Remove the file from the double-linked FILO list of open files
|
||||||
|
file->vd->openFileCount--;
|
||||||
|
if (file->nextOpenFile)
|
||||||
|
file->nextOpenFile->prevOpenFile = file->prevOpenFile;
|
||||||
|
if (file->prevOpenFile)
|
||||||
|
file->prevOpenFile->nextOpenFile = file->nextOpenFile;
|
||||||
|
else
|
||||||
|
file->vd->firstOpenFile = file->nextOpenFile;
|
||||||
|
|
||||||
|
// Unlock
|
||||||
|
ntfsUnlock(file->vd);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
ssize_t ntfs_write_r (struct _reent *r, int fd, const char *ptr, size_t len)
|
||||||
|
{
|
||||||
|
ntfs_log_trace("fd %p, ptr %p, len %Li\n", fd, ptr, len);
|
||||||
|
|
||||||
|
ntfs_file_state* file = STATE(fd);
|
||||||
|
ssize_t written = 0;
|
||||||
|
off_t old_pos = 0;
|
||||||
|
|
||||||
|
// Sanity check
|
||||||
|
if (!file || !file->vd || !file->ni || !file->data_na) {
|
||||||
|
r->_errno = EINVAL;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Short circuit cases where we don't actually have to do anything
|
||||||
|
if (!ptr || len <= 0) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Lock
|
||||||
|
ntfsLock(file->vd);
|
||||||
|
|
||||||
|
// Check that we are allowed to write to this file
|
||||||
|
if (!file->write) {
|
||||||
|
ntfsUnlock(file->vd);
|
||||||
|
r->_errno = EACCES;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we are in append mode, backup the current position and move to the end of the file
|
||||||
|
if (file->append) {
|
||||||
|
old_pos = file->pos;
|
||||||
|
file->pos = file->len;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write to the files data atrribute
|
||||||
|
while (len) {
|
||||||
|
ssize_t ret = ntfs_attr_pwrite(file->data_na, file->pos, len, ptr);
|
||||||
|
if (ret <= 0) {
|
||||||
|
ntfsUnlock(file->vd);
|
||||||
|
r->_errno = errno;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
len -= ret;
|
||||||
|
file->pos += ret;
|
||||||
|
written += ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we are in append mode, restore the current position to were it was prior to this write
|
||||||
|
if (file->append) {
|
||||||
|
file->pos = old_pos;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mark the file for archiving (if we actually wrote something)
|
||||||
|
if (written)
|
||||||
|
file->ni->flags |= FILE_ATTR_ARCHIVE;
|
||||||
|
|
||||||
|
// Update file times (if we actually wrote something)
|
||||||
|
if (written)
|
||||||
|
ntfsUpdateTimes(file->vd, file->ni, NTFS_UPDATE_MCTIME);
|
||||||
|
|
||||||
|
// Update the files data length
|
||||||
|
file->len = file->data_na->data_size;
|
||||||
|
|
||||||
|
// Unlock
|
||||||
|
ntfsUnlock(file->vd);
|
||||||
|
|
||||||
|
return written;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
s64 ntfs_attr_getfragments(ntfs_attr *na, const s64 pos, s64 count, u64 offset,
|
||||||
|
_ntfs_frag_append_t append_fragment, void *callback_data);
|
||||||
|
|
||||||
|
int _NTFS_get_fragments (const char *path,
|
||||||
|
_ntfs_frag_append_t append_fragment, void *callback_data)
|
||||||
|
{
|
||||||
|
struct _reent r;
|
||||||
|
ntfs_file_state file_st, *file = &file_st;
|
||||||
|
ssize_t read = 0;
|
||||||
|
int ret_val = -11;
|
||||||
|
|
||||||
|
// Open File
|
||||||
|
r._errno = 0;
|
||||||
|
int fd = ntfs_open_r(&r, file, path, O_RDONLY, 0);
|
||||||
|
if (fd != (int)file) return -12;
|
||||||
|
|
||||||
|
|
||||||
|
// Sanity check
|
||||||
|
if (!file || !file->vd || !file->ni || !file->data_na) {
|
||||||
|
//r->_errno = EINVAL;
|
||||||
|
return -13;
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
// Short circuit cases where we don't actually have to do anything
|
||||||
|
if (!ptr || len <= 0) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Lock
|
||||||
|
ntfsLock(file->vd);
|
||||||
|
|
||||||
|
/*
|
||||||
|
// Check that we are allowed to read from this file
|
||||||
|
if (!file->read) {
|
||||||
|
ntfsUnlock(file->vd);
|
||||||
|
r->_errno = EACCES;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Don't read past the end of file
|
||||||
|
if (file->pos + len > file->len) {
|
||||||
|
r->_errno = EOVERFLOW;
|
||||||
|
len = file->len - file->pos;
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
u64 offset = 0;
|
||||||
|
u64 len = file->len;
|
||||||
|
|
||||||
|
// Read from the files data attribute
|
||||||
|
while (len) {
|
||||||
|
s64 ret = ntfs_attr_getfragments(file->data_na, file->pos,
|
||||||
|
len, offset, append_fragment, callback_data);
|
||||||
|
if (ret <= 0 || ret > len) {
|
||||||
|
//r->_errno = errno;
|
||||||
|
ret_val = -14;
|
||||||
|
if (ret < 0) ret_val = ret;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
offset += ret;
|
||||||
|
len -= ret;
|
||||||
|
file->pos += ret;
|
||||||
|
read += ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
// set file size
|
||||||
|
append_fragment(callback_data, file->len >> 9, 0, 0);
|
||||||
|
// success
|
||||||
|
ret_val = 0;
|
||||||
|
|
||||||
|
/*
|
||||||
|
// Update file times (if we actually read something)
|
||||||
|
if (read)
|
||||||
|
ntfsUpdateTimes(file->vd, file->ni, NTFS_UPDATE_ATIME);
|
||||||
|
*/
|
||||||
|
|
||||||
|
out:
|
||||||
|
// Unlock
|
||||||
|
ntfsUnlock(file->vd);
|
||||||
|
// Close the file
|
||||||
|
ntfs_close_r (&r, fd);
|
||||||
|
|
||||||
|
return ret_val;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
|
||||||
|
off_t ntfs_seek_r (struct _reent *r, int fd, off_t pos, int dir)
|
||||||
|
{
|
||||||
|
ntfs_log_trace("fd %p, pos %Li, dir %i\n", fd, pos, dir);
|
||||||
|
|
||||||
|
ntfs_file_state* file = STATE(fd);
|
||||||
|
off_t position = 0;
|
||||||
|
|
||||||
|
// Sanity check
|
||||||
|
if (!file || !file->vd || !file->ni || !file->data_na) {
|
||||||
|
r->_errno = EINVAL;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Lock
|
||||||
|
ntfsLock(file->vd);
|
||||||
|
|
||||||
|
// Set the files current position
|
||||||
|
switch(dir) {
|
||||||
|
case SEEK_SET: position = file->pos = MIN(MAX(pos, 0), file->len); break;
|
||||||
|
case SEEK_CUR: position = file->pos = MIN(MAX(file->pos + pos, 0), file->len); break;
|
||||||
|
case SEEK_END: position = file->pos = MIN(MAX(file->len + pos, 0), file->len); break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unlock
|
||||||
|
ntfsUnlock(file->vd);
|
||||||
|
|
||||||
|
return position;
|
||||||
|
}
|
||||||
|
int ntfs_fstat_r (struct _reent *r, int fd, struct stat *st)
|
||||||
|
{
|
||||||
|
ntfs_log_trace("fd %p\n", fd);
|
||||||
|
|
||||||
|
ntfs_file_state* file = STATE(fd);
|
||||||
|
int ret = 0;
|
||||||
|
|
||||||
|
// Sanity check
|
||||||
|
if (!file || !file->vd || !file->ni || !file->data_na) {
|
||||||
|
r->_errno = EINVAL;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Short circuit cases were we don't actually have to do anything
|
||||||
|
if (!st)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
// Get the file stats
|
||||||
|
ret = ntfsStat(file->vd, file->ni, st);
|
||||||
|
if (ret)
|
||||||
|
r->_errno = errno;
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
int ntfs_ftruncate_r (struct _reent *r, int fd, off_t len)
|
||||||
|
{
|
||||||
|
ntfs_log_trace("fd %p, len %Li\n", fd, len);
|
||||||
|
|
||||||
|
ntfs_file_state* file = STATE(fd);
|
||||||
|
|
||||||
|
// Sanity check
|
||||||
|
if (!file || !file->vd || !file->ni || !file->data_na) {
|
||||||
|
r->_errno = EINVAL;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Lock
|
||||||
|
ntfsLock(file->vd);
|
||||||
|
|
||||||
|
// Check that we are allowed to write to this file
|
||||||
|
if (!file->write) {
|
||||||
|
ntfsUnlock(file->vd);
|
||||||
|
r->_errno = EACCES;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// For compressed files, only deleting and expanding contents are implemented
|
||||||
|
if (file->compressed &&
|
||||||
|
len > 0 &&
|
||||||
|
len < file->data_na->initialized_size) {
|
||||||
|
ntfsUnlock(file->vd);
|
||||||
|
r->_errno = EOPNOTSUPP;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Resize the files data attribute, either by expanding or truncating
|
||||||
|
if (file->compressed && len > file->data_na->initialized_size) {
|
||||||
|
char zero = 0;
|
||||||
|
if (ntfs_attr_pwrite(file->data_na, len - 1, 1, &zero) <= 0) {
|
||||||
|
ntfsUnlock(file->vd);
|
||||||
|
r->_errno = errno;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (ntfs_attr_truncate(file->data_na, len)) {
|
||||||
|
ntfsUnlock(file->vd);
|
||||||
|
r->_errno = errno;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mark the file for archiving (if we actually changed something)
|
||||||
|
if (file->len != file->data_na->data_size)
|
||||||
|
file->ni->flags |= FILE_ATTR_ARCHIVE;
|
||||||
|
|
||||||
|
// Update file times (if we actually changed something)
|
||||||
|
if (file->len != file->data_na->data_size)
|
||||||
|
ntfsUpdateTimes(file->vd, file->ni, NTFS_UPDATE_MCTIME);
|
||||||
|
|
||||||
|
// Update the files data length
|
||||||
|
file->len = file->data_na->data_size;
|
||||||
|
|
||||||
|
// Sync the file (and its attributes) to disc
|
||||||
|
ntfsSync(file->vd, file->ni);
|
||||||
|
|
||||||
|
// Unlock
|
||||||
|
ntfsUnlock(file->vd);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int ntfs_fsync_r (struct _reent *r, int fd)
|
||||||
|
{
|
||||||
|
ntfs_log_trace("fd %p\n", fd);
|
||||||
|
|
||||||
|
ntfs_file_state* file = STATE(fd);
|
||||||
|
int ret = 0;
|
||||||
|
|
||||||
|
// Sanity check
|
||||||
|
if (!file || !file->vd || !file->ni || !file->data_na) {
|
||||||
|
r->_errno = EINVAL;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Lock
|
||||||
|
ntfsLock(file->vd);
|
||||||
|
|
||||||
|
// Sync the file (and its attributes) to disc
|
||||||
|
ret = ntfsSync(file->vd, file->ni);
|
||||||
|
if (ret)
|
||||||
|
r->_errno = errno;
|
||||||
|
|
||||||
|
// Unlock
|
||||||
|
ntfsUnlock(file->vd);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
829
source/libntfs/ntfsinternal.c
Normal file
829
source/libntfs/ntfsinternal.c
Normal file
@ -0,0 +1,829 @@
|
|||||||
|
/**
|
||||||
|
* ntfsinternal.h - Internal support routines for NTFS-based devices.
|
||||||
|
*
|
||||||
|
* Copyright (c) 2009 Rhys "Shareese" Koedijk
|
||||||
|
* Copyright (c) 2006 Michael "Chishm" Chisholm
|
||||||
|
*
|
||||||
|
* This program/include file 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/include file 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; if not, write to the Free Software Foundation,
|
||||||
|
* Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifdef HAVE_CONFIG_H
|
||||||
|
#include "config.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef HAVE_STDLIB_H
|
||||||
|
#include <stdlib.h>
|
||||||
|
#endif
|
||||||
|
#ifdef HAVE_ERRNO_H
|
||||||
|
#include <errno.h>
|
||||||
|
#endif
|
||||||
|
#ifdef HAVE_STRING_H
|
||||||
|
#include <string.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "ntfsinternal.h"
|
||||||
|
#include "ntfsdir.h"
|
||||||
|
#include "ntfsfile.h"
|
||||||
|
|
||||||
|
#if defined(__wii__)
|
||||||
|
#include <sdcard/wiisd_io.h>
|
||||||
|
#include <sdcard/gcsd.h>
|
||||||
|
#include <ogc/usbstorage.h>
|
||||||
|
|
||||||
|
const INTERFACE_ID ntfs_disc_interfaces[] = {
|
||||||
|
{ "sd", &__io_wiisd },
|
||||||
|
{ "usb", &__io_usbstorage },
|
||||||
|
{ "carda", &__io_gcsda },
|
||||||
|
{ "cardb", &__io_gcsdb },
|
||||||
|
{ NULL, NULL }
|
||||||
|
};
|
||||||
|
|
||||||
|
#elif defined(__gamecube__)
|
||||||
|
#include <sdcard/gcsd.h>
|
||||||
|
|
||||||
|
const INTERFACE_ID ntfs_disc_interfaces[] = {
|
||||||
|
{ "carda", &__io_gcsda },
|
||||||
|
{ "cardb", &__io_gcsdb },
|
||||||
|
{ NULL, NULL }
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
int ntfsAddDevice (const char *name, void *deviceData)
|
||||||
|
{
|
||||||
|
const devoptab_t *devoptab_ntfs = ntfsGetDevOpTab();
|
||||||
|
devoptab_t *dev = NULL;
|
||||||
|
char *devname = NULL;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
// Sanity check
|
||||||
|
if (!name || !deviceData || !devoptab_ntfs) {
|
||||||
|
errno = EINVAL;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Allocate a devoptab for this device
|
||||||
|
dev = ntfs_alloc(sizeof(devoptab_t) + strlen(name) + 1);
|
||||||
|
if (!dev) {
|
||||||
|
errno = ENOMEM;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Use the space allocated at the end of the devoptab for storing the device name
|
||||||
|
devname = (char*)(dev + 1);
|
||||||
|
strcpy(devname, name);
|
||||||
|
|
||||||
|
// Setup the devoptab
|
||||||
|
memcpy(dev, devoptab_ntfs, sizeof(devoptab_t));
|
||||||
|
dev->name = devname;
|
||||||
|
dev->deviceData = deviceData;
|
||||||
|
|
||||||
|
// Add the device to the devoptab table (if there is a free slot)
|
||||||
|
for (i = 0; i < STD_MAX; i++) {
|
||||||
|
if (devoptab_list[i] == devoptab_list[0] && i != 0) {
|
||||||
|
devoptab_list[i] = dev;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we reach here then there are no free slots in the devoptab table for this device
|
||||||
|
errno = EADDRNOTAVAIL;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ntfsRemoveDevice (const char *path)
|
||||||
|
{
|
||||||
|
const devoptab_t *devoptab = NULL;
|
||||||
|
char name[128] = {0};
|
||||||
|
int i;
|
||||||
|
|
||||||
|
// Get the device name from the path
|
||||||
|
strncpy(name, path, 127);
|
||||||
|
strtok(name, ":/");
|
||||||
|
|
||||||
|
// Find and remove the specified device from the devoptab table
|
||||||
|
// NOTE: We do this manually due to a 'bug' in RemoveDevice
|
||||||
|
// which ignores names with suffixes and causes names
|
||||||
|
// like "ntfs" and "ntfs1" to be seen as equals
|
||||||
|
for (i = 0; i < STD_MAX; i++) {
|
||||||
|
devoptab = devoptab_list[i];
|
||||||
|
if (devoptab && devoptab->name) {
|
||||||
|
if (strcmp(name, devoptab->name) == 0) {
|
||||||
|
devoptab_list[i] = devoptab_list[0];
|
||||||
|
ntfs_free((devoptab_t*)devoptab);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const devoptab_t *ntfsGetDevice (const char *path, bool useDefaultDevice)
|
||||||
|
{
|
||||||
|
const devoptab_t *devoptab = NULL;
|
||||||
|
char name[128] = {0};
|
||||||
|
int i;
|
||||||
|
|
||||||
|
// Get the device name from the path
|
||||||
|
strncpy(name, path, 127);
|
||||||
|
strtok(name, ":/");
|
||||||
|
|
||||||
|
// Search the devoptab table for the specified device name
|
||||||
|
// NOTE: We do this manually due to a 'bug' in GetDeviceOpTab
|
||||||
|
// which ignores names with suffixes and causes names
|
||||||
|
// like "ntfs" and "ntfs1" to be seen as equals
|
||||||
|
for (i = 0; i < STD_MAX; i++) {
|
||||||
|
devoptab = devoptab_list[i];
|
||||||
|
if (devoptab && devoptab->name) {
|
||||||
|
if (strcmp(name, devoptab->name) == 0) {
|
||||||
|
return devoptab;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we reach here then we couldn't find the device name,
|
||||||
|
// chances are that this path has no device name in it.
|
||||||
|
// Call GetDeviceOpTab to get our default device (chdir).
|
||||||
|
if (useDefaultDevice)
|
||||||
|
return GetDeviceOpTab("");
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
const INTERFACE_ID *ntfsGetDiscInterfaces (void)
|
||||||
|
{
|
||||||
|
// Get all know disc interfaces on the host system
|
||||||
|
return ntfs_disc_interfaces;
|
||||||
|
}
|
||||||
|
|
||||||
|
ntfs_vd *ntfsGetVolume (const char *path)
|
||||||
|
{
|
||||||
|
// Get the volume descriptor from the paths associated devoptab (if found)
|
||||||
|
const devoptab_t *devoptab_ntfs = ntfsGetDevOpTab();
|
||||||
|
const devoptab_t *devoptab = ntfsGetDevice(path, true);
|
||||||
|
if (devoptab && devoptab_ntfs && (devoptab->open_r == devoptab_ntfs->open_r))
|
||||||
|
return (ntfs_vd*)devoptab->deviceData;
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
int ntfsInitVolume (ntfs_vd *vd)
|
||||||
|
{
|
||||||
|
// Sanity check
|
||||||
|
if (!vd) {
|
||||||
|
errno = ENODEV;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initialise the volume lock
|
||||||
|
LWP_MutexInit(&vd->lock, false);
|
||||||
|
|
||||||
|
// Reset the volumes current directory
|
||||||
|
vd->cwd_ni = NULL;
|
||||||
|
|
||||||
|
// Reset open directory and file stats
|
||||||
|
vd->openDirCount = 0;
|
||||||
|
vd->openFileCount = 0;
|
||||||
|
vd->firstOpenDir = NULL;
|
||||||
|
vd->firstOpenFile = NULL;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ntfsDeinitVolume (ntfs_vd *vd)
|
||||||
|
{
|
||||||
|
// Sanity check
|
||||||
|
if (!vd) {
|
||||||
|
errno = ENODEV;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Lock
|
||||||
|
ntfsLock(vd);
|
||||||
|
|
||||||
|
// Close any directories which are still open (lazy programmers!)
|
||||||
|
ntfs_dir_state *nextDir = vd->firstOpenDir;
|
||||||
|
while (nextDir) {
|
||||||
|
ntfs_log_warning("Cleaning up orphaned directory @ %p\n", nextDir);
|
||||||
|
ntfsCloseDir(nextDir);
|
||||||
|
nextDir = nextDir->nextOpenDir;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close any files which are still open (lazy programmers!)
|
||||||
|
ntfs_file_state *nextFile = vd->firstOpenFile;
|
||||||
|
while (nextFile) {
|
||||||
|
ntfs_log_warning("Cleaning up orphaned file @ %p\n", nextFile);
|
||||||
|
ntfsCloseFile(nextFile);
|
||||||
|
nextFile = nextFile->nextOpenFile;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reset open directory and file stats
|
||||||
|
vd->openDirCount = 0;
|
||||||
|
vd->openFileCount = 0;
|
||||||
|
vd->firstOpenDir = NULL;
|
||||||
|
vd->firstOpenFile = NULL;
|
||||||
|
|
||||||
|
// Close the volumes current directory (if any)
|
||||||
|
if (vd->cwd_ni) {
|
||||||
|
ntfsCloseEntry(vd, vd->cwd_ni);
|
||||||
|
vd->cwd_ni = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Force the underlying device to sync
|
||||||
|
vd->dev->d_ops->sync(vd->dev);
|
||||||
|
|
||||||
|
// Unlock
|
||||||
|
ntfsUnlock(vd);
|
||||||
|
|
||||||
|
// Deinitialise the volume lock
|
||||||
|
LWP_MutexDestroy(vd->lock);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ntfs_inode *ntfsOpenEntry (ntfs_vd *vd, const char *path)
|
||||||
|
{
|
||||||
|
return ntfsParseEntry(vd, path, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
ntfs_inode *ntfsParseEntry (ntfs_vd *vd, const char *path, int reparseLevel)
|
||||||
|
{
|
||||||
|
ntfs_inode *ni = NULL;
|
||||||
|
char *target = NULL;
|
||||||
|
int attr_size;
|
||||||
|
|
||||||
|
// Sanity check
|
||||||
|
if (!vd) {
|
||||||
|
errno = ENODEV;
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the actual path of the entry
|
||||||
|
path = ntfsRealPath(path);
|
||||||
|
if (!path) {
|
||||||
|
errno = EINVAL;
|
||||||
|
return NULL;
|
||||||
|
} else if (path[0] == '\0') {
|
||||||
|
path = ".";
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find the entry, taking into account our current directory (if any)
|
||||||
|
if (path[0] != PATH_SEP)
|
||||||
|
ni = ntfs_pathname_to_inode(vd->vol, vd->cwd_ni, path++);
|
||||||
|
else
|
||||||
|
ni = ntfs_pathname_to_inode(vd->vol, NULL, path);
|
||||||
|
|
||||||
|
// If the entry was found and it has reparse data then parse its true path;
|
||||||
|
// this resolves the true location of symbolic links and directory junctions
|
||||||
|
if (ni && (ni->flags & FILE_ATTR_REPARSE_POINT)) {
|
||||||
|
if (ntfs_possible_symlink(ni)) {
|
||||||
|
|
||||||
|
// Sanity check, give up if we are parsing to deep
|
||||||
|
if (reparseLevel > NTFS_MAX_SYMLINK_DEPTH) {
|
||||||
|
ntfsCloseEntry(vd, ni);
|
||||||
|
errno = ELOOP;
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the target path of this entry
|
||||||
|
target = ntfs_make_symlink(path, ni, &attr_size);
|
||||||
|
if (!target) {
|
||||||
|
ntfsCloseEntry(vd, ni);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close the entry (we are no longer interested in it)
|
||||||
|
ntfsCloseEntry(vd, ni);
|
||||||
|
|
||||||
|
// Parse the entries target
|
||||||
|
ni = ntfsParseEntry(vd, target, reparseLevel++);
|
||||||
|
|
||||||
|
// Clean up
|
||||||
|
ntfs_free(target);
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ni;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ntfsCloseEntry (ntfs_vd *vd, ntfs_inode *ni)
|
||||||
|
{
|
||||||
|
// Sanity check
|
||||||
|
if (!vd) {
|
||||||
|
errno = ENODEV;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Lock
|
||||||
|
ntfsLock(vd);
|
||||||
|
|
||||||
|
// Sync the entry (if it is dirty)
|
||||||
|
if (NInoDirty(ni))
|
||||||
|
ntfsSync(vd, ni);
|
||||||
|
|
||||||
|
// Close the entry
|
||||||
|
ntfs_inode_close(ni);
|
||||||
|
|
||||||
|
// Unlock
|
||||||
|
ntfsUnlock(vd);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ntfs_inode *ntfsCreate (ntfs_vd *vd, const char *path, mode_t type, const char *target)
|
||||||
|
{
|
||||||
|
ntfs_inode *dir_ni = NULL, *ni = NULL;
|
||||||
|
char *dir = NULL;
|
||||||
|
char *name = NULL;
|
||||||
|
ntfschar *uname = NULL, *utarget = NULL;
|
||||||
|
int uname_len, utarget_len;
|
||||||
|
|
||||||
|
// Sanity check
|
||||||
|
if (!vd) {
|
||||||
|
errno = ENODEV;
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
// You cannot link between devices
|
||||||
|
if(target) {
|
||||||
|
if(vd != ntfsGetVolume(target)) {
|
||||||
|
errno = EXDEV;
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the actual paths of the entry
|
||||||
|
path = ntfsRealPath(path);
|
||||||
|
target = ntfsRealPath(target);
|
||||||
|
if (!path || !target) {
|
||||||
|
errno = EINVAL;
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Lock
|
||||||
|
ntfsLock(vd);
|
||||||
|
|
||||||
|
// Get the unicode name for the entry and find its parent directory
|
||||||
|
// TODO: This looks horrible, clean it up
|
||||||
|
dir = strdup(path);
|
||||||
|
if (!dir) {
|
||||||
|
errno = EINVAL;
|
||||||
|
goto cleanup;
|
||||||
|
}
|
||||||
|
name = strrchr(dir, '/');
|
||||||
|
if (name)
|
||||||
|
name++;
|
||||||
|
else
|
||||||
|
name = dir;
|
||||||
|
uname_len = ntfsLocalToUnicode(name, &uname);
|
||||||
|
if (uname_len < 0) {
|
||||||
|
errno = EINVAL;
|
||||||
|
goto cleanup;
|
||||||
|
}
|
||||||
|
*name = 0;
|
||||||
|
|
||||||
|
// Open the entries parent directory
|
||||||
|
dir_ni = ntfsOpenEntry(vd, dir);
|
||||||
|
if (!dir_ni) {
|
||||||
|
goto cleanup;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create the entry
|
||||||
|
switch (type) {
|
||||||
|
|
||||||
|
// Symbolic link
|
||||||
|
case S_IFLNK:
|
||||||
|
utarget_len = ntfsLocalToUnicode(target, &utarget);
|
||||||
|
if (utarget_len < 0) {
|
||||||
|
errno = EINVAL;
|
||||||
|
goto cleanup;
|
||||||
|
}
|
||||||
|
ni = ntfs_create_symlink(dir_ni, 0, uname, uname_len, utarget, utarget_len);
|
||||||
|
break;
|
||||||
|
|
||||||
|
// Directory or file
|
||||||
|
case S_IFDIR:
|
||||||
|
case S_IFREG:
|
||||||
|
ni = ntfs_create(dir_ni, 0, uname, uname_len, type);
|
||||||
|
break;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the entry was created
|
||||||
|
if (ni) {
|
||||||
|
|
||||||
|
// Mark the entry for archiving
|
||||||
|
ni->flags |= FILE_ATTR_ARCHIVE;
|
||||||
|
|
||||||
|
// Mark the entry as dirty
|
||||||
|
NInoSetDirty(ni);
|
||||||
|
|
||||||
|
// Sync the entry to disc
|
||||||
|
ntfsSync(vd, ni);
|
||||||
|
|
||||||
|
// Update parent directories times
|
||||||
|
ntfsUpdateTimes(vd, dir_ni, NTFS_UPDATE_MCTIME);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
cleanup:
|
||||||
|
|
||||||
|
if(dir_ni)
|
||||||
|
ntfsCloseEntry(vd, dir_ni);
|
||||||
|
|
||||||
|
if(utarget)
|
||||||
|
ntfs_free(utarget);
|
||||||
|
|
||||||
|
if(uname)
|
||||||
|
ntfs_free(uname);
|
||||||
|
|
||||||
|
if(dir)
|
||||||
|
ntfs_free(dir);
|
||||||
|
|
||||||
|
// Unlock
|
||||||
|
ntfsUnlock(vd);
|
||||||
|
|
||||||
|
return ni;
|
||||||
|
}
|
||||||
|
|
||||||
|
int ntfsLink (ntfs_vd *vd, const char *old_path, const char *new_path)
|
||||||
|
{
|
||||||
|
ntfs_inode *dir_ni = NULL, *ni = NULL;
|
||||||
|
char *dir = NULL;
|
||||||
|
char *name = NULL;
|
||||||
|
ntfschar *uname = NULL;
|
||||||
|
int uname_len;
|
||||||
|
int res = 0;
|
||||||
|
|
||||||
|
// Sanity check
|
||||||
|
if (!vd) {
|
||||||
|
errno = ENODEV;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// You cannot link between devices
|
||||||
|
if(vd != ntfsGetVolume(new_path)) {
|
||||||
|
errno = EXDEV;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the actual paths of the entry
|
||||||
|
old_path = ntfsRealPath(old_path);
|
||||||
|
new_path = ntfsRealPath(new_path);
|
||||||
|
if (!old_path || !new_path) {
|
||||||
|
errno = EINVAL;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Lock
|
||||||
|
ntfsLock(vd);
|
||||||
|
|
||||||
|
// Get the unicode name for the entry and find its parent directory
|
||||||
|
// TODO: This looks horrible, clean it up
|
||||||
|
dir = strdup(new_path);
|
||||||
|
if (!dir) {
|
||||||
|
errno = EINVAL;
|
||||||
|
goto cleanup;
|
||||||
|
}
|
||||||
|
name = strrchr(dir, '/');
|
||||||
|
if (name)
|
||||||
|
name++;
|
||||||
|
else
|
||||||
|
name = dir;
|
||||||
|
uname_len = ntfsLocalToUnicode(name, &uname);
|
||||||
|
if (uname_len < 0) {
|
||||||
|
errno = EINVAL;
|
||||||
|
goto cleanup;
|
||||||
|
}
|
||||||
|
*name = 0;
|
||||||
|
|
||||||
|
// Find the entry
|
||||||
|
ni = ntfsOpenEntry(vd, old_path);
|
||||||
|
if (!ni) {
|
||||||
|
errno = ENOENT;
|
||||||
|
res = -1;
|
||||||
|
goto cleanup;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Open the entries new parent directory
|
||||||
|
dir_ni = ntfsOpenEntry(vd, dir);
|
||||||
|
if (!dir_ni) {
|
||||||
|
errno = ENOENT;
|
||||||
|
res = -1;
|
||||||
|
goto cleanup;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Link the entry to its new parent
|
||||||
|
if (ntfs_link(ni, dir_ni, uname, uname_len)) {
|
||||||
|
res = -1;
|
||||||
|
goto cleanup;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update entry times
|
||||||
|
ntfsUpdateTimes(vd, ni, NTFS_UPDATE_CTIME);
|
||||||
|
ntfsUpdateTimes(vd, dir_ni, NTFS_UPDATE_MCTIME);
|
||||||
|
|
||||||
|
// Sync the entry to disc
|
||||||
|
ntfsSync(vd, ni);
|
||||||
|
|
||||||
|
cleanup:
|
||||||
|
|
||||||
|
if(dir_ni)
|
||||||
|
ntfsCloseEntry(vd, dir_ni);
|
||||||
|
|
||||||
|
if(ni)
|
||||||
|
ntfsCloseEntry(vd, ni);
|
||||||
|
|
||||||
|
if(uname)
|
||||||
|
ntfs_free(uname);
|
||||||
|
|
||||||
|
if(dir)
|
||||||
|
ntfs_free(dir);
|
||||||
|
|
||||||
|
// Unlock
|
||||||
|
ntfsUnlock(vd);
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
int ntfsUnlink (ntfs_vd *vd, const char *path)
|
||||||
|
{
|
||||||
|
ntfs_inode *dir_ni = NULL, *ni = NULL;
|
||||||
|
char *dir = NULL;
|
||||||
|
char *name = NULL;
|
||||||
|
ntfschar *uname = NULL;
|
||||||
|
int uname_len;
|
||||||
|
int res = 0;
|
||||||
|
|
||||||
|
// Sanity check
|
||||||
|
if (!vd) {
|
||||||
|
errno = ENODEV;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the actual path of the entry
|
||||||
|
path = ntfsRealPath(path);
|
||||||
|
if (!path) {
|
||||||
|
errno = EINVAL;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Lock
|
||||||
|
ntfsLock(vd);
|
||||||
|
|
||||||
|
// Get the unicode name for the entry and find its parent directory
|
||||||
|
// TODO: This looks horrible
|
||||||
|
dir = strdup(path);
|
||||||
|
if (!dir) {
|
||||||
|
errno = EINVAL;
|
||||||
|
goto cleanup;
|
||||||
|
}
|
||||||
|
name = strrchr(dir, '/');
|
||||||
|
if (name)
|
||||||
|
name++;
|
||||||
|
else
|
||||||
|
name = dir;
|
||||||
|
uname_len = ntfsLocalToUnicode(name, &uname);
|
||||||
|
if (uname_len < 0) {
|
||||||
|
errno = EINVAL;
|
||||||
|
goto cleanup;
|
||||||
|
}
|
||||||
|
*name = 0;
|
||||||
|
|
||||||
|
// Find the entry
|
||||||
|
ni = ntfsOpenEntry(vd, path);
|
||||||
|
if (!ni) {
|
||||||
|
errno = ENOENT;
|
||||||
|
res = -1;
|
||||||
|
goto cleanup;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Open the entries parent directory
|
||||||
|
dir_ni = ntfsOpenEntry(vd, dir);
|
||||||
|
if (!dir_ni) {
|
||||||
|
errno = ENOENT;
|
||||||
|
res = -1;
|
||||||
|
goto cleanup;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unlink the entry from its parent
|
||||||
|
if (ntfs_delete(vd->vol, path, ni, dir_ni, uname, uname_len)) {
|
||||||
|
res = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Force the underlying device to sync
|
||||||
|
vd->dev->d_ops->sync(vd->dev);
|
||||||
|
|
||||||
|
// ntfs_delete() ALWAYS closes ni and dir_ni; so no need for us to anymore
|
||||||
|
dir_ni = ni = NULL;
|
||||||
|
|
||||||
|
cleanup:
|
||||||
|
|
||||||
|
if(dir_ni)
|
||||||
|
ntfsCloseEntry(vd, dir_ni);
|
||||||
|
|
||||||
|
if(ni)
|
||||||
|
ntfsCloseEntry(vd, ni);
|
||||||
|
|
||||||
|
if(uname)
|
||||||
|
ntfs_free(uname);
|
||||||
|
|
||||||
|
if(dir)
|
||||||
|
ntfs_free(dir);
|
||||||
|
|
||||||
|
// Unlock
|
||||||
|
ntfsUnlock(vd);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int ntfsSync (ntfs_vd *vd, ntfs_inode *ni)
|
||||||
|
{
|
||||||
|
int res = 0;
|
||||||
|
|
||||||
|
// Sanity check
|
||||||
|
if (!vd) {
|
||||||
|
errno = ENODEV;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sanity check
|
||||||
|
if (!ni) {
|
||||||
|
errno = ENOENT;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Lock
|
||||||
|
ntfsLock(vd);
|
||||||
|
|
||||||
|
// Sync the entry
|
||||||
|
res = ntfs_inode_sync(ni);
|
||||||
|
|
||||||
|
// Force the underlying device to sync
|
||||||
|
vd->dev->d_ops->sync(vd->dev);
|
||||||
|
|
||||||
|
// Unlock
|
||||||
|
ntfsUnlock(vd);
|
||||||
|
|
||||||
|
return res;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
int ntfsStat (ntfs_vd *vd, ntfs_inode *ni, struct stat *st)
|
||||||
|
{
|
||||||
|
//printf("ntfsStat %p %p %p\n", vd, ni, st);
|
||||||
|
ntfs_attr *na = NULL;
|
||||||
|
int res = 0;
|
||||||
|
|
||||||
|
// Sanity check
|
||||||
|
if (!vd) {
|
||||||
|
errno = ENODEV;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sanity check
|
||||||
|
if (!ni) {
|
||||||
|
errno = ENOENT;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Short circuit cases were we don't actually have to do anything
|
||||||
|
if (!st)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
// Lock
|
||||||
|
ntfsLock(vd);
|
||||||
|
|
||||||
|
// Zero out the stat buffer
|
||||||
|
memset(st, 0, sizeof(struct stat));
|
||||||
|
|
||||||
|
// Is this entry a directory
|
||||||
|
if (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY) {
|
||||||
|
st->st_mode = S_IFDIR | (0777 & ~vd->dmask);
|
||||||
|
st->st_nlink = 1;
|
||||||
|
|
||||||
|
// Open the directories index allocation table attribute
|
||||||
|
na = ntfs_attr_open(ni, AT_INDEX_ALLOCATION, NTFS_INDEX_I30, 4);
|
||||||
|
if (na) {
|
||||||
|
st->st_size = na->data_size;
|
||||||
|
st->st_blocks = na->allocated_size >> 9;
|
||||||
|
ntfs_attr_close(na);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Else it must be a file
|
||||||
|
} else {
|
||||||
|
st->st_mode = S_IFREG | (0777 & ~vd->fmask);
|
||||||
|
st->st_size = ni->data_size;
|
||||||
|
st->st_blocks = (ni->allocated_size + 511) >> 9;
|
||||||
|
st->st_nlink = le16_to_cpu(ni->mrec->link_count);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fill in the generic entry stats
|
||||||
|
st->st_dev = vd->id;
|
||||||
|
st->st_uid = vd->uid;
|
||||||
|
st->st_gid = vd->gid;
|
||||||
|
st->st_ino = ni->mft_no;
|
||||||
|
st->st_atime = ni->last_access_time;
|
||||||
|
st->st_ctime = ni->last_mft_change_time;
|
||||||
|
st->st_mtime = ni->last_data_change_time;
|
||||||
|
|
||||||
|
// Update entry times
|
||||||
|
ntfsUpdateTimes(vd, ni, NTFS_UPDATE_ATIME);
|
||||||
|
|
||||||
|
// Unlock
|
||||||
|
ntfsUnlock(vd);
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ntfsUpdateTimes (ntfs_vd *vd, ntfs_inode *ni, ntfs_time_update_flags mask)
|
||||||
|
{
|
||||||
|
// Run the access time update strategy against the device driver settings first
|
||||||
|
if (vd && vd->atime == ATIME_DISABLED)
|
||||||
|
mask &= ~NTFS_UPDATE_ATIME;
|
||||||
|
|
||||||
|
// Update entry times
|
||||||
|
if (ni && mask)
|
||||||
|
ntfs_inode_update_times(ni, mask);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *ntfsRealPath (const char *path)
|
||||||
|
{
|
||||||
|
// Sanity check
|
||||||
|
if (!path)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
// Move the path pointer to the start of the actual path
|
||||||
|
if (strchr(path, ':') != NULL) {
|
||||||
|
path = strchr(path, ':') + 1;
|
||||||
|
}
|
||||||
|
if (strchr(path, ':') != NULL) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
|
||||||
|
int ntfsUnicodeToLocal (const ntfschar *ins, const int ins_len, char **outs, int outs_len)
|
||||||
|
{
|
||||||
|
int len = 0;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
// Sanity check
|
||||||
|
if (!ins || !ins_len || !outs)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
// Convert the unicode string to our current local
|
||||||
|
len = ntfs_ucstombs(ins, ins_len, outs, outs_len);
|
||||||
|
if (len == -1 && errno == EILSEQ) {
|
||||||
|
|
||||||
|
// The string could not be converted to the current local,
|
||||||
|
// do it manually by replacing non-ASCII characters with underscores
|
||||||
|
if (!*outs || outs_len >= ins_len) {
|
||||||
|
if (!*outs) {
|
||||||
|
*outs = ntfs_alloc(ins_len + 1);
|
||||||
|
if (!*outs) {
|
||||||
|
errno = ENOMEM;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (i = 0; i < ins_len; i++) {
|
||||||
|
ntfschar uc = le16_to_cpu(ins[i]);
|
||||||
|
if (uc > 0xff)
|
||||||
|
uc = (ntfschar)'_';
|
||||||
|
*outs[i] = (char)uc;
|
||||||
|
}
|
||||||
|
*outs[ins_len] = (ntfschar)'\0';
|
||||||
|
len = ins_len;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return len;
|
||||||
|
}
|
||||||
|
|
||||||
|
int ntfsLocalToUnicode (const char *ins, ntfschar **outs)
|
||||||
|
{
|
||||||
|
// Sanity check
|
||||||
|
if (!ins || !outs)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
// Convert the local string to unicode
|
||||||
|
return ntfs_mbstoucs(ins, outs);
|
||||||
|
}
|
177
source/libntfs/ntfsinternal.h
Normal file
177
source/libntfs/ntfsinternal.h
Normal file
@ -0,0 +1,177 @@
|
|||||||
|
/**
|
||||||
|
* ntfsinternal.h - Internal support routines for NTFS-based devices.
|
||||||
|
*
|
||||||
|
* Copyright (c) 2009 Rhys "Shareese" Koedijk
|
||||||
|
* Copyright (c) 2006 Michael "Chishm" Chisholm
|
||||||
|
*
|
||||||
|
* This program/include file 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/include file 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; if not, write to the Free Software Foundation,
|
||||||
|
* Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _NTFSINTERNAL_H
|
||||||
|
#define _NTFSINTERNAL_H
|
||||||
|
|
||||||
|
#ifdef HAVE_CONFIG_H
|
||||||
|
#include "config.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "types.h"
|
||||||
|
#include "compat.h"
|
||||||
|
#include "logging.h"
|
||||||
|
#include "layout.h"
|
||||||
|
#include "device.h"
|
||||||
|
#include "volume.h"
|
||||||
|
#include "dir.h"
|
||||||
|
#include "inode.h"
|
||||||
|
#include "attrib.h"
|
||||||
|
#include "reparse.h"
|
||||||
|
#include "security.h"
|
||||||
|
#include "efs.h"
|
||||||
|
#include "unistr.h"
|
||||||
|
|
||||||
|
#include <gccore.h>
|
||||||
|
#include <ogc/disc_io.h>
|
||||||
|
#include <sys/iosupport.h>
|
||||||
|
|
||||||
|
#define NTFS_MOUNT_PREFIX "ntfs" /* Device name prefix to use when auto-mounting */
|
||||||
|
#define NTFS_MAX_PARTITIONS 32 /* Maximum number of partitions that can be found */
|
||||||
|
#define NTFS_MAX_MOUNTS 10 /* Maximum number of mounts available at one time */
|
||||||
|
#define NTFS_MAX_SYMLINK_DEPTH 10 /* Maximum search depth when resolving symbolic links */
|
||||||
|
|
||||||
|
#define NTFS_OEM_ID cpu_to_le64(0x202020205346544eULL) /* "NTFS " */
|
||||||
|
|
||||||
|
#define MBR_SIGNATURE cpu_to_le16(0xAA55)
|
||||||
|
#define EBR_SIGNATURE cpu_to_le16(0xAA55)
|
||||||
|
|
||||||
|
#define PARTITION_STATUS_NONBOOTABLE 0x00 /* Non-bootable */
|
||||||
|
#define PARTITION_STATUS_BOOTABLE 0x80 /* Bootable (active) */
|
||||||
|
|
||||||
|
#define PARTITION_TYPE_EMPTY 0x00 /* Empty */
|
||||||
|
#define PARTITION_TYPE_DOS33_EXTENDED 0x05 /* DOS 3.3+ extended partition */
|
||||||
|
#define PARTITION_TYPE_NTFS 0x07 /* Windows NT NTFS */
|
||||||
|
#define PARTITION_TYPE_WIN95_EXTENDED 0x0F /* Windows 95 extended partition */
|
||||||
|
|
||||||
|
/* Forward declarations */
|
||||||
|
struct _ntfs_file_state;
|
||||||
|
struct _ntfs_dir_state;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* PRIMARY_PARTITION - Block device partition record
|
||||||
|
*/
|
||||||
|
typedef struct _PARTITION_RECORD {
|
||||||
|
u8 status; /* Partition status; see above */
|
||||||
|
u8 chs_start[3]; /* Cylinder-head-sector address to first block of partition */
|
||||||
|
u8 type; /* Partition type; see above */
|
||||||
|
u8 chs_end[3]; /* Cylinder-head-sector address to last block of partition */
|
||||||
|
u32 lba_start; /* Local block address to first sector of partition */
|
||||||
|
u32 block_count; /* Number of blocks in partition */
|
||||||
|
} __attribute__((__packed__)) PARTITION_RECORD;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* MASTER_BOOT_RECORD - Block device master boot record
|
||||||
|
*/
|
||||||
|
typedef struct _MASTER_BOOT_RECORD {
|
||||||
|
u8 code_area[446]; /* Code area; normally empty */
|
||||||
|
PARTITION_RECORD partitions[4]; /* 4 primary partitions */
|
||||||
|
u16 signature; /* MBR signature; 0xAA55 */
|
||||||
|
} __attribute__((__packed__)) MASTER_BOOT_RECORD;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* EXTENDED_PARTITION - Block device extended boot record
|
||||||
|
*/
|
||||||
|
typedef struct _EXTENDED_BOOT_RECORD {
|
||||||
|
u8 code_area[446]; /* Code area; normally empty */
|
||||||
|
PARTITION_RECORD partition; /* Primary partition */
|
||||||
|
PARTITION_RECORD next_ebr; /* Next extended boot record in the chain */
|
||||||
|
u8 reserved[32]; /* Normally empty */
|
||||||
|
u16 signature; /* EBR signature; 0xAA55 */
|
||||||
|
} __attribute__((__packed__)) EXTENDED_BOOT_RECORD;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* INTERFACE_ID - Disc interface identifier
|
||||||
|
*/
|
||||||
|
typedef struct _INTERFACE_ID {
|
||||||
|
const char *name; /* Interface name */
|
||||||
|
const DISC_INTERFACE *interface; /* Disc interface */
|
||||||
|
} INTERFACE_ID;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ntfs_atime_t - File access time update strategies
|
||||||
|
*/
|
||||||
|
typedef enum {
|
||||||
|
ATIME_ENABLED, /* Update access times */
|
||||||
|
ATIME_DISABLED /* Don't update access times */
|
||||||
|
} ntfs_atime_t;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ntfs_vd - NTFS volume descriptor
|
||||||
|
*/
|
||||||
|
typedef struct _ntfs_vd {
|
||||||
|
struct ntfs_device *dev; /* NTFS device handle */
|
||||||
|
ntfs_volume *vol; /* NTFS volume handle */
|
||||||
|
mutex_t lock; /* Volume lock mutex */
|
||||||
|
s64 id; /* Filesystem id */
|
||||||
|
u32 flags; /* Mount flags */
|
||||||
|
u16 uid; /* User id for entry creation */
|
||||||
|
u16 gid; /* Group id for entry creation */
|
||||||
|
u16 fmask; /* Unix style permission mask for file creation */
|
||||||
|
u16 dmask; /* Unix style permission mask for directory creation */
|
||||||
|
ntfs_atime_t atime; /* Entry access time update strategy */
|
||||||
|
bool showHiddenFiles; /* If true, show hidden files when enumerating directories */
|
||||||
|
bool showSystemFiles; /* If true, show system files when enumerating directories */
|
||||||
|
ntfs_inode *cwd_ni; /* Current directory */
|
||||||
|
struct _ntfs_dir_state *firstOpenDir; /* The start of a FILO linked list of currently opened directories */
|
||||||
|
struct _ntfs_file_state *firstOpenFile; /* The start of a FILO linked list of currently opened files */
|
||||||
|
u16 openDirCount; /* The total number of directories currently open in this volume */
|
||||||
|
u16 openFileCount; /* The total number of files currently open in this volume */
|
||||||
|
} ntfs_vd;
|
||||||
|
|
||||||
|
/* Lock volume */
|
||||||
|
static inline void ntfsLock (ntfs_vd *vd)
|
||||||
|
{
|
||||||
|
LWP_MutexLock(vd->lock);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Unlock volume */
|
||||||
|
static inline void ntfsUnlock (ntfs_vd *vd)
|
||||||
|
{
|
||||||
|
LWP_MutexUnlock(vd->lock);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Gekko device related routines */
|
||||||
|
int ntfsAddDevice (const char *name, void *deviceData);
|
||||||
|
void ntfsRemoveDevice (const char *path);
|
||||||
|
const devoptab_t *ntfsGetDevice (const char *path, bool useDefaultDevice);
|
||||||
|
const devoptab_t *ntfsGetDevOpTab (void);
|
||||||
|
const INTERFACE_ID* ntfsGetDiscInterfaces (void);
|
||||||
|
|
||||||
|
/* Miscellaneous helper/support routines */
|
||||||
|
int ntfsInitVolume (ntfs_vd *vd);
|
||||||
|
void ntfsDeinitVolume (ntfs_vd *vd);
|
||||||
|
ntfs_vd *ntfsGetVolume (const char *path);
|
||||||
|
ntfs_inode *ntfsOpenEntry (ntfs_vd *vd, const char *path);
|
||||||
|
ntfs_inode *ntfsParseEntry (ntfs_vd *vd, const char *path, int reparseLevel);
|
||||||
|
void ntfsCloseEntry (ntfs_vd *vd, ntfs_inode *ni);
|
||||||
|
ntfs_inode *ntfsCreate (ntfs_vd *vd, const char *path, mode_t type, const char *target);
|
||||||
|
int ntfsLink (ntfs_vd *vd, const char *old_path, const char *new_path);
|
||||||
|
int ntfsUnlink (ntfs_vd *vd, const char *path);
|
||||||
|
int ntfsSync (ntfs_vd *vd, ntfs_inode *ni);
|
||||||
|
int ntfsStat (ntfs_vd *vd, ntfs_inode *ni, struct stat *st);
|
||||||
|
void ntfsUpdateTimes (ntfs_vd *vd, ntfs_inode *ni, ntfs_time_update_flags mask);
|
||||||
|
|
||||||
|
const char *ntfsRealPath (const char *path);
|
||||||
|
int ntfsUnicodeToLocal (const ntfschar *ins, const int ins_len, char **outs, int outs_len);
|
||||||
|
int ntfsLocalToUnicode (const char *ins, ntfschar **outs);
|
||||||
|
|
||||||
|
#endif /* _NTFSINTERNAL_H */
|
69
source/libntfs/ntfstime.h
Normal file
69
source/libntfs/ntfstime.h
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
/*
|
||||||
|
* ntfstime.h - NTFS time related functions. Originated from the Linux-NTFS project.
|
||||||
|
*
|
||||||
|
* Copyright (c) 2005 Anton Altaparmakov
|
||||||
|
* Copyright (c) 2005 Yura Pakhuchiy
|
||||||
|
*
|
||||||
|
* This program/include file 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/include file 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
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _NTFS_NTFSTIME_H
|
||||||
|
#define _NTFS_NTFSTIME_H
|
||||||
|
|
||||||
|
#ifdef HAVE_TIME_H
|
||||||
|
#include <time.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "types.h"
|
||||||
|
|
||||||
|
#define NTFS_TIME_OFFSET ((s64)(369 * 365 + 89) * 24 * 3600 * 10000000)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ntfs2utc - Convert an NTFS time to Unix time
|
||||||
|
* @ntfs_time: An NTFS time in 100ns units since 1601
|
||||||
|
*
|
||||||
|
* NTFS stores times as the number of 100ns intervals since January 1st 1601 at
|
||||||
|
* 00:00 UTC. This system will not suffer from Y2K problems until ~57000AD.
|
||||||
|
*
|
||||||
|
* Return: n A Unix time (number of seconds since 1970)
|
||||||
|
*/
|
||||||
|
static __inline__ time_t ntfs2utc(s64 ntfs_time)
|
||||||
|
{
|
||||||
|
return (sle64_to_cpu(ntfs_time) - (NTFS_TIME_OFFSET)) / 10000000;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* utc2ntfs - Convert Linux time to NTFS time
|
||||||
|
* @utc_time: Linux time to convert to NTFS
|
||||||
|
*
|
||||||
|
* Convert the Linux time @utc_time to its corresponding NTFS time.
|
||||||
|
*
|
||||||
|
* Linux stores time in a long at present and measures it as the number of
|
||||||
|
* 1-second intervals since 1st January 1970, 00:00:00 UTC.
|
||||||
|
*
|
||||||
|
* NTFS uses Microsoft's standard time format which is stored in a s64 and is
|
||||||
|
* measured as the number of 100 nano-second intervals since 1st January 1601,
|
||||||
|
* 00:00:00 UTC.
|
||||||
|
*
|
||||||
|
* Return: n An NTFS time (100ns units since Jan 1601)
|
||||||
|
*/
|
||||||
|
static __inline__ s64 utc2ntfs(time_t utc_time)
|
||||||
|
{
|
||||||
|
/* Convert to 100ns intervals and then add the NTFS time offset. */
|
||||||
|
return cpu_to_sle64((s64)utc_time * 10000000 + NTFS_TIME_OFFSET);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif /* _NTFS_NTFSTIME_H */
|
1296
source/libntfs/reparse.c
Normal file
1296
source/libntfs/reparse.c
Normal file
File diff suppressed because it is too large
Load Diff
39
source/libntfs/reparse.h
Normal file
39
source/libntfs/reparse.h
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
/*
|
||||||
|
*
|
||||||
|
* Copyright (c) 2008 Jean-Pierre Andre
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 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
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef REPARSE_H
|
||||||
|
#define REPARSE_H
|
||||||
|
|
||||||
|
char *ntfs_make_symlink(const char *org_path,
|
||||||
|
ntfs_inode *ni, int *pattr_size);
|
||||||
|
BOOL ntfs_possible_symlink(ntfs_inode *ni);
|
||||||
|
|
||||||
|
int ntfs_get_ntfs_reparse_data(const char *path,
|
||||||
|
char *value, size_t size, ntfs_inode *ni);
|
||||||
|
int ntfs_set_ntfs_reparse_data(const char *path, const char *value,
|
||||||
|
size_t size, int flags, ntfs_inode *ni);
|
||||||
|
int ntfs_remove_ntfs_reparse_data(const char *path, ntfs_inode *ni);
|
||||||
|
|
||||||
|
int ntfs_delete_reparse_index(ntfs_inode *ni);
|
||||||
|
|
||||||
|
#endif /* REPARSE_H */
|
2162
source/libntfs/runlist.c
Normal file
2162
source/libntfs/runlist.c
Normal file
File diff suppressed because it is too large
Load Diff
89
source/libntfs/runlist.h
Normal file
89
source/libntfs/runlist.h
Normal file
@ -0,0 +1,89 @@
|
|||||||
|
/*
|
||||||
|
* runlist.h - Exports for runlist handling. Originated from the Linux-NTFS project.
|
||||||
|
*
|
||||||
|
* Copyright (c) 2002 Anton Altaparmakov
|
||||||
|
* Copyright (c) 2002 Richard Russon
|
||||||
|
*
|
||||||
|
* This program/include file 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/include file 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
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _NTFS_RUNLIST_H
|
||||||
|
#define _NTFS_RUNLIST_H
|
||||||
|
|
||||||
|
#include "types.h"
|
||||||
|
|
||||||
|
/* Forward declarations */
|
||||||
|
typedef struct _runlist_element runlist_element;
|
||||||
|
typedef runlist_element runlist;
|
||||||
|
|
||||||
|
#include "attrib.h"
|
||||||
|
#include "volume.h"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* struct _runlist_element - in memory vcn to lcn mapping array element.
|
||||||
|
* @vcn: starting vcn of the current array element
|
||||||
|
* @lcn: starting lcn of the current array element
|
||||||
|
* @length: length in clusters of the current array element
|
||||||
|
*
|
||||||
|
* The last vcn (in fact the last vcn + 1) is reached when length == 0.
|
||||||
|
*
|
||||||
|
* When lcn == -1 this means that the count vcns starting at vcn are not
|
||||||
|
* physically allocated (i.e. this is a hole / data is sparse).
|
||||||
|
*/
|
||||||
|
struct _runlist_element {/* In memory vcn to lcn mapping structure element. */
|
||||||
|
VCN vcn; /* vcn = Starting virtual cluster number. */
|
||||||
|
LCN lcn; /* lcn = Starting logical cluster number. */
|
||||||
|
s64 length; /* Run length in clusters. */
|
||||||
|
};
|
||||||
|
|
||||||
|
extern runlist_element *ntfs_rl_extend(runlist_element *rl, int more_entries);
|
||||||
|
|
||||||
|
extern LCN ntfs_rl_vcn_to_lcn(const runlist_element *rl, const VCN vcn);
|
||||||
|
|
||||||
|
extern s64 ntfs_rl_pread(const ntfs_volume *vol, const runlist_element *rl,
|
||||||
|
const s64 pos, s64 count, void *b);
|
||||||
|
extern s64 ntfs_rl_pwrite(const ntfs_volume *vol, const runlist_element *rl,
|
||||||
|
s64 ofs, const s64 pos, s64 count, void *b);
|
||||||
|
|
||||||
|
extern runlist_element *ntfs_runlists_merge(runlist_element *drl,
|
||||||
|
runlist_element *srl);
|
||||||
|
|
||||||
|
extern runlist_element *ntfs_mapping_pairs_decompress(const ntfs_volume *vol,
|
||||||
|
const ATTR_RECORD *attr, runlist_element *old_rl);
|
||||||
|
|
||||||
|
extern int ntfs_get_nr_significant_bytes(const s64 n);
|
||||||
|
|
||||||
|
extern int ntfs_get_size_for_mapping_pairs(const ntfs_volume *vol,
|
||||||
|
const runlist_element *rl, const VCN start_vcn, int max_size);
|
||||||
|
|
||||||
|
extern int ntfs_write_significant_bytes(u8 *dst, const u8 *dst_max,
|
||||||
|
const s64 n);
|
||||||
|
|
||||||
|
extern int ntfs_mapping_pairs_build(const ntfs_volume *vol, u8 *dst,
|
||||||
|
const int dst_len, const runlist_element *rl,
|
||||||
|
const VCN start_vcn, runlist_element const **stop_rl);
|
||||||
|
|
||||||
|
extern int ntfs_rl_truncate(runlist **arl, const VCN start_vcn);
|
||||||
|
|
||||||
|
extern int ntfs_rl_sparse(runlist *rl);
|
||||||
|
extern s64 ntfs_rl_get_compressed_size(ntfs_volume *vol, runlist *rl);
|
||||||
|
|
||||||
|
#ifdef NTFS_TEST
|
||||||
|
int test_rl_main(int argc, char *argv[]);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* defined _NTFS_RUNLIST_H */
|
||||||
|
|
4982
source/libntfs/security.c
Normal file
4982
source/libntfs/security.c
Normal file
File diff suppressed because it is too large
Load Diff
354
source/libntfs/security.h
Normal file
354
source/libntfs/security.h
Normal file
@ -0,0 +1,354 @@
|
|||||||
|
/*
|
||||||
|
* security.h - Exports for handling security/ACLs in NTFS.
|
||||||
|
* Originated from the Linux-NTFS project.
|
||||||
|
*
|
||||||
|
* Copyright (c) 2004 Anton Altaparmakov
|
||||||
|
* Copyright (c) 2005-2006 Szabolcs Szakacsits
|
||||||
|
* Copyright (c) 2007-2008 Jean-Pierre Andre
|
||||||
|
*
|
||||||
|
* This program/include file 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/include file 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
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _NTFS_SECURITY_H
|
||||||
|
#define _NTFS_SECURITY_H
|
||||||
|
|
||||||
|
#include "types.h"
|
||||||
|
#include "layout.h"
|
||||||
|
#include "inode.h"
|
||||||
|
#include "dir.h"
|
||||||
|
|
||||||
|
#ifndef POSIXACLS
|
||||||
|
#define POSIXACLS 0
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if __BYTE_ORDER == __LITTLE_ENDIAN
|
||||||
|
#define const_cpu_to_be16(x) ((((x) & 255L) << 8) + (((x) >> 8) & 255L))
|
||||||
|
#define const_cpu_to_be32(x) ((((x) & 255L) << 24) + (((x) & 0xff00L) << 8) \
|
||||||
|
+ (((x) >> 8) & 0xff00L) + (((x) >> 24) & 255L))
|
||||||
|
#else
|
||||||
|
#define const_cpu_to_be16(x) (x)
|
||||||
|
#define const_cpu_to_be32(x) (x)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/*
|
||||||
|
* item in the mapping list
|
||||||
|
*/
|
||||||
|
|
||||||
|
struct MAPPING {
|
||||||
|
struct MAPPING *next;
|
||||||
|
int xid; /* linux id : uid or gid */
|
||||||
|
SID *sid; /* Windows id : usid or gsid */
|
||||||
|
int grcnt; /* group count (for users only) */
|
||||||
|
gid_t *groups; /* groups which the user is member of */
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Entry in the permissions cache
|
||||||
|
* Note : this cache is not organized as a generic cache
|
||||||
|
*/
|
||||||
|
|
||||||
|
struct CACHED_PERMISSIONS {
|
||||||
|
uid_t uid;
|
||||||
|
gid_t gid;
|
||||||
|
le32 inh_fileid;
|
||||||
|
le32 inh_dirid;
|
||||||
|
#if POSIXACLS
|
||||||
|
struct POSIX_SECURITY *pxdesc;
|
||||||
|
unsigned int pxdescsize:16;
|
||||||
|
#endif
|
||||||
|
unsigned int mode:12;
|
||||||
|
unsigned int valid:1;
|
||||||
|
} ;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Entry in the permissions cache for directories with no security_id
|
||||||
|
*/
|
||||||
|
|
||||||
|
struct CACHED_PERMISSIONS_LEGACY {
|
||||||
|
struct CACHED_PERMISSIONS_LEGACY *next;
|
||||||
|
void *variable;
|
||||||
|
size_t varsize;
|
||||||
|
/* above fields must match "struct CACHED_GENERIC" */
|
||||||
|
u64 mft_no;
|
||||||
|
struct CACHED_PERMISSIONS perm;
|
||||||
|
} ;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Entry in the securid cache
|
||||||
|
*/
|
||||||
|
|
||||||
|
struct CACHED_SECURID {
|
||||||
|
struct CACHED_SECURID *next;
|
||||||
|
void *variable;
|
||||||
|
size_t varsize;
|
||||||
|
/* above fields must match "struct CACHED_GENERIC" */
|
||||||
|
uid_t uid;
|
||||||
|
gid_t gid;
|
||||||
|
unsigned int dmode;
|
||||||
|
le32 securid;
|
||||||
|
} ;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Header of the security cache
|
||||||
|
* (has no cache structure by itself)
|
||||||
|
*/
|
||||||
|
|
||||||
|
struct CACHED_PERMISSIONS_HEADER {
|
||||||
|
unsigned int last;
|
||||||
|
/* statistics for permissions */
|
||||||
|
unsigned long p_writes;
|
||||||
|
unsigned long p_reads;
|
||||||
|
unsigned long p_hits;
|
||||||
|
} ;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The whole permissions cache
|
||||||
|
*/
|
||||||
|
|
||||||
|
struct PERMISSIONS_CACHE {
|
||||||
|
struct CACHED_PERMISSIONS_HEADER head;
|
||||||
|
struct CACHED_PERMISSIONS *cachetable[1]; /* array of variable size */
|
||||||
|
} ;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Security flags values
|
||||||
|
*/
|
||||||
|
|
||||||
|
enum {
|
||||||
|
SECURITY_DEFAULT, /* rely on fuse for permissions checking */
|
||||||
|
SECURITY_RAW, /* force same ownership/permissions on files */
|
||||||
|
SECURITY_ADDSECURIDS, /* upgrade old security descriptors */
|
||||||
|
SECURITY_STATICGRPS, /* use static groups for access control */
|
||||||
|
SECURITY_WANTED /* a security related option was present */
|
||||||
|
} ;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Security context, needed by most security functions
|
||||||
|
*/
|
||||||
|
|
||||||
|
enum { MAPUSERS, MAPGROUPS, MAPCOUNT } ;
|
||||||
|
|
||||||
|
struct SECURITY_CONTEXT {
|
||||||
|
ntfs_volume *vol;
|
||||||
|
struct MAPPING *mapping[MAPCOUNT];
|
||||||
|
struct PERMISSIONS_CACHE **pseccache;
|
||||||
|
uid_t uid; /* uid of user requesting (not the mounter) */
|
||||||
|
gid_t gid; /* gid of user requesting (not the mounter) */
|
||||||
|
pid_t tid; /* thread id of thread requesting */
|
||||||
|
mode_t umask; /* umask of requesting thread */
|
||||||
|
} ;
|
||||||
|
|
||||||
|
#if POSIXACLS
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Posix ACL structures
|
||||||
|
*/
|
||||||
|
|
||||||
|
struct POSIX_ACE {
|
||||||
|
u16 tag;
|
||||||
|
u16 perms;
|
||||||
|
s32 id;
|
||||||
|
} ;
|
||||||
|
|
||||||
|
struct POSIX_ACL {
|
||||||
|
u8 version;
|
||||||
|
u8 flags;
|
||||||
|
u16 filler;
|
||||||
|
struct POSIX_ACE ace[0];
|
||||||
|
} ;
|
||||||
|
|
||||||
|
struct POSIX_SECURITY {
|
||||||
|
mode_t mode;
|
||||||
|
int acccnt;
|
||||||
|
int defcnt;
|
||||||
|
int firstdef;
|
||||||
|
u16 tagsset;
|
||||||
|
struct POSIX_ACL acl;
|
||||||
|
} ;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Posix tags, cpu-endian 16 bits
|
||||||
|
*/
|
||||||
|
|
||||||
|
enum {
|
||||||
|
POSIX_ACL_USER_OBJ = 1,
|
||||||
|
POSIX_ACL_USER = 2,
|
||||||
|
POSIX_ACL_GROUP_OBJ = 4,
|
||||||
|
POSIX_ACL_GROUP = 8,
|
||||||
|
POSIX_ACL_MASK = 16,
|
||||||
|
POSIX_ACL_OTHER = 32,
|
||||||
|
POSIX_ACL_SPECIAL = 64 /* internal use only */
|
||||||
|
} ;
|
||||||
|
|
||||||
|
#define POSIX_ACL_EXTENSIONS (POSIX_ACL_USER | POSIX_ACL_GROUP | POSIX_ACL_MASK)
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Posix permissions, cpu-endian 16 bits
|
||||||
|
*/
|
||||||
|
|
||||||
|
enum {
|
||||||
|
POSIX_PERM_X = 1,
|
||||||
|
POSIX_PERM_W = 2,
|
||||||
|
POSIX_PERM_R = 4,
|
||||||
|
POSIX_PERM_DENIAL = 64 /* internal use only */
|
||||||
|
} ;
|
||||||
|
|
||||||
|
#define POSIX_VERSION 2
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
extern BOOL ntfs_guid_is_zero(const GUID *guid);
|
||||||
|
extern char *ntfs_guid_to_mbs(const GUID *guid, char *guid_str);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ntfs_sid_is_valid - determine if a SID is valid
|
||||||
|
* @sid: SID for which to determine if it is valid
|
||||||
|
*
|
||||||
|
* Determine if the SID pointed to by @sid is valid.
|
||||||
|
*
|
||||||
|
* Return TRUE if it is valid and FALSE otherwise.
|
||||||
|
*/
|
||||||
|
static __inline__ BOOL ntfs_sid_is_valid(const SID *sid)
|
||||||
|
{
|
||||||
|
if (!sid || sid->revision != SID_REVISION ||
|
||||||
|
sid->sub_authority_count > SID_MAX_SUB_AUTHORITIES)
|
||||||
|
return FALSE;
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
extern int ntfs_sid_to_mbs_size(const SID *sid);
|
||||||
|
extern char *ntfs_sid_to_mbs(const SID *sid, char *sid_str,
|
||||||
|
size_t sid_str_size);
|
||||||
|
extern void ntfs_generate_guid(GUID *guid);
|
||||||
|
extern int ntfs_sd_add_everyone(ntfs_inode *ni);
|
||||||
|
|
||||||
|
extern le32 ntfs_security_hash(const SECURITY_DESCRIPTOR_RELATIVE *sd,
|
||||||
|
const u32 len);
|
||||||
|
|
||||||
|
int ntfs_build_mapping(struct SECURITY_CONTEXT *scx, const char *usermap_path);
|
||||||
|
int ntfs_get_owner_mode(struct SECURITY_CONTEXT *scx,
|
||||||
|
const char *path, ntfs_inode *ni, struct stat*);
|
||||||
|
int ntfs_set_mode(struct SECURITY_CONTEXT *scx,
|
||||||
|
const char *path, ntfs_inode *ni, mode_t mode);
|
||||||
|
BOOL ntfs_allowed_as_owner(struct SECURITY_CONTEXT *scx,
|
||||||
|
const char *path, ntfs_inode *ni);
|
||||||
|
int ntfs_allowed_access(struct SECURITY_CONTEXT *scx, const char *path,
|
||||||
|
ntfs_inode *ni, int accesstype);
|
||||||
|
BOOL ntfs_allowed_dir_access(struct SECURITY_CONTEXT *scx,
|
||||||
|
const char *path, int accesstype);
|
||||||
|
|
||||||
|
#if POSIXACLS
|
||||||
|
le32 ntfs_alloc_securid(struct SECURITY_CONTEXT *scx,
|
||||||
|
uid_t uid, gid_t gid, const char *dir_path,
|
||||||
|
ntfs_inode *dir_ni, mode_t mode, BOOL isdir);
|
||||||
|
#else
|
||||||
|
le32 ntfs_alloc_securid(struct SECURITY_CONTEXT *scx,
|
||||||
|
uid_t uid, gid_t gid, mode_t mode, BOOL isdir);
|
||||||
|
#endif
|
||||||
|
int ntfs_set_owner(struct SECURITY_CONTEXT *scx,
|
||||||
|
const char *path, ntfs_inode *ni, uid_t uid, gid_t gid);
|
||||||
|
#if POSIXACLS
|
||||||
|
int ntfs_set_owner_mode(struct SECURITY_CONTEXT *scx,
|
||||||
|
ntfs_inode *ni, uid_t uid, gid_t gid,
|
||||||
|
mode_t mode, struct POSIX_SECURITY *pxdesc);
|
||||||
|
#else
|
||||||
|
int ntfs_set_owner_mode(struct SECURITY_CONTEXT *scx,
|
||||||
|
ntfs_inode *ni, uid_t uid, gid_t gid, mode_t mode);
|
||||||
|
#endif
|
||||||
|
le32 ntfs_inherited_id(struct SECURITY_CONTEXT *scx,
|
||||||
|
const char *dir_path, ntfs_inode *dir_ni, BOOL fordir);
|
||||||
|
int ntfs_open_secure(ntfs_volume *vol);
|
||||||
|
void ntfs_close_secure(struct SECURITY_CONTEXT *scx);
|
||||||
|
|
||||||
|
#if POSIXACLS
|
||||||
|
|
||||||
|
int ntfs_set_inherited_posix(struct SECURITY_CONTEXT *scx,
|
||||||
|
ntfs_inode *ni, uid_t uid, gid_t gid,
|
||||||
|
const char *dir_path, ntfs_inode *dir_ni, mode_t mode);
|
||||||
|
int ntfs_get_posix_acl(struct SECURITY_CONTEXT *scx, const char *path,
|
||||||
|
const char *name, char *value, size_t size,
|
||||||
|
ntfs_inode *ni);
|
||||||
|
int ntfs_set_posix_acl(struct SECURITY_CONTEXT *scx, const char *path,
|
||||||
|
const char *name, const char *value, size_t size,
|
||||||
|
int flags, ntfs_inode *ni);
|
||||||
|
int ntfs_remove_posix_acl(struct SECURITY_CONTEXT *scx, const char *path,
|
||||||
|
const char *name, ntfs_inode *ni);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
int ntfs_get_ntfs_acl(struct SECURITY_CONTEXT *scx, const char *path,
|
||||||
|
const char *name, char *value, size_t size,
|
||||||
|
ntfs_inode *ni);
|
||||||
|
int ntfs_set_ntfs_acl(struct SECURITY_CONTEXT *scx, const char *path,
|
||||||
|
const char *name, const char *value, size_t size,
|
||||||
|
int flags, ntfs_inode *ni);
|
||||||
|
int ntfs_get_ntfs_attrib(const char *path,
|
||||||
|
char *value, size_t size, ntfs_inode *ni);
|
||||||
|
int ntfs_set_ntfs_attrib(const char *path,
|
||||||
|
const char *value, size_t size, int flags,
|
||||||
|
ntfs_inode *ni);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Security API for direct access to security descriptors
|
||||||
|
* based on Win32 API
|
||||||
|
*/
|
||||||
|
|
||||||
|
#define MAGIC_API 0x09042009
|
||||||
|
|
||||||
|
struct SECURITY_API {
|
||||||
|
u32 magic;
|
||||||
|
struct SECURITY_CONTEXT security;
|
||||||
|
struct PERMISSIONS_CACHE *seccache;
|
||||||
|
} ;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The following constants are used in interfacing external programs.
|
||||||
|
* They are not to be stored on disk and must be defined in their
|
||||||
|
* native cpu representation.
|
||||||
|
* When disk representation (le) is needed, use SE_DACL_PRESENT, etc.
|
||||||
|
*/
|
||||||
|
enum { OWNER_SECURITY_INFORMATION = 1,
|
||||||
|
GROUP_SECURITY_INFORMATION = 2,
|
||||||
|
DACL_SECURITY_INFORMATION = 4,
|
||||||
|
SACL_SECURITY_INFORMATION = 8
|
||||||
|
} ;
|
||||||
|
|
||||||
|
int ntfs_get_file_security(struct SECURITY_API *scapi,
|
||||||
|
const char *path, u32 selection,
|
||||||
|
char *buf, u32 buflen, u32 *psize);
|
||||||
|
int ntfs_set_file_security(struct SECURITY_API *scapi,
|
||||||
|
const char *path, u32 selection, const char *attr);
|
||||||
|
int ntfs_get_file_attributes(struct SECURITY_API *scapi,
|
||||||
|
const char *path);
|
||||||
|
BOOL ntfs_set_file_attributes(struct SECURITY_API *scapi,
|
||||||
|
const char *path, s32 attrib);
|
||||||
|
BOOL ntfs_read_directory(struct SECURITY_API *scapi,
|
||||||
|
const char *path, ntfs_filldir_t callback, void *context);
|
||||||
|
int ntfs_read_sds(struct SECURITY_API *scapi,
|
||||||
|
char *buf, u32 size, u32 offset);
|
||||||
|
INDEX_ENTRY *ntfs_read_sii(struct SECURITY_API *scapi,
|
||||||
|
INDEX_ENTRY *entry);
|
||||||
|
INDEX_ENTRY *ntfs_read_sdh(struct SECURITY_API *scapi,
|
||||||
|
INDEX_ENTRY *entry);
|
||||||
|
struct SECURITY_API *ntfs_initialize_file_security(const char *device,
|
||||||
|
int flags);
|
||||||
|
BOOL ntfs_leave_file_security(struct SECURITY_API *scx);
|
||||||
|
|
||||||
|
int ntfs_get_usid(struct SECURITY_API *scapi, uid_t uid, char *buf);
|
||||||
|
int ntfs_get_gsid(struct SECURITY_API *scapi, gid_t gid, char *buf);
|
||||||
|
int ntfs_get_user(struct SECURITY_API *scapi, const SID *usid);
|
||||||
|
int ntfs_get_group(struct SECURITY_API *scapi, const SID *gsid);
|
||||||
|
|
||||||
|
#endif /* defined _NTFS_SECURITY_H */
|
85
source/libntfs/support.h
Normal file
85
source/libntfs/support.h
Normal file
@ -0,0 +1,85 @@
|
|||||||
|
/*
|
||||||
|
* support.h - Useful definitions and macros. Originated from the Linux-NTFS project.
|
||||||
|
*
|
||||||
|
* Copyright (c) 2000-2004 Anton Altaparmakov
|
||||||
|
*
|
||||||
|
* This program/include file 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/include file 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
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _NTFS_SUPPORT_H
|
||||||
|
#define _NTFS_SUPPORT_H
|
||||||
|
|
||||||
|
#ifdef HAVE_CONFIG_H
|
||||||
|
#include "config.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef HAVE_STDDEF_H
|
||||||
|
#include <stddef.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Our mailing list. Use this define to prevent typos in email address.
|
||||||
|
*/
|
||||||
|
#define NTFS_DEV_LIST "ntfs-3g-devel@lists.sf.net"
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Generic macro to convert pointers to values for comparison purposes.
|
||||||
|
*/
|
||||||
|
#ifndef p2n
|
||||||
|
#define p2n(p) ((ptrdiff_t)((ptrdiff_t*)(p)))
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The classic min and max macros.
|
||||||
|
*/
|
||||||
|
#ifndef min
|
||||||
|
#define min(a,b) ((a) <= (b) ? (a) : (b))
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef max
|
||||||
|
#define max(a,b) ((a) >= (b) ? (a) : (b))
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Useful macro for determining the offset of a struct member.
|
||||||
|
*/
|
||||||
|
#ifndef offsetof
|
||||||
|
#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Simple bit operation macros. NOTE: These are NOT atomic.
|
||||||
|
*/
|
||||||
|
#define test_bit(bit, var) ((var) & (1 << (bit)))
|
||||||
|
#define set_bit(bit, var) (var) |= 1 << (bit)
|
||||||
|
#define clear_bit(bit, var) (var) &= ~(1 << (bit))
|
||||||
|
|
||||||
|
#define test_and_set_bit(bit, var) \
|
||||||
|
({ \
|
||||||
|
const BOOL old_state = test_bit(bit, var); \
|
||||||
|
set_bit(bit, var); \
|
||||||
|
old_state; \
|
||||||
|
})
|
||||||
|
|
||||||
|
#define test_and_clear_bit(bit, var) \
|
||||||
|
({ \
|
||||||
|
const BOOL old_state = test_bit(bit, var); \
|
||||||
|
clear_bit(bit, var); \
|
||||||
|
old_state; \
|
||||||
|
})
|
||||||
|
|
||||||
|
#endif /* defined _NTFS_SUPPORT_H */
|
||||||
|
|
133
source/libntfs/types.h
Normal file
133
source/libntfs/types.h
Normal file
@ -0,0 +1,133 @@
|
|||||||
|
/*
|
||||||
|
* types.h - Misc type definitions not related to on-disk structure.
|
||||||
|
* Originated from the Linux-NTFS project.
|
||||||
|
*
|
||||||
|
* Copyright (c) 2000-2004 Anton Altaparmakov
|
||||||
|
*
|
||||||
|
* This program/include file 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/include file 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
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _NTFS_TYPES_H
|
||||||
|
#define _NTFS_TYPES_H
|
||||||
|
|
||||||
|
#ifdef HAVE_CONFIG_H
|
||||||
|
#include "config.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if HAVE_STDINT_H || !HAVE_CONFIG_H
|
||||||
|
#include <stdint.h>
|
||||||
|
#endif
|
||||||
|
#ifdef HAVE_SYS_TYPES_H
|
||||||
|
#include <sys/types.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef GEKKO
|
||||||
|
#include <gctypes.h>
|
||||||
|
#include "compat.h"
|
||||||
|
#else /* GEKKO */
|
||||||
|
|
||||||
|
typedef uint8_t u8; /* Unsigned types of an exact size */
|
||||||
|
typedef uint16_t u16;
|
||||||
|
typedef uint32_t u32;
|
||||||
|
typedef uint64_t u64;
|
||||||
|
|
||||||
|
typedef int8_t s8; /* Signed types of an exact size */
|
||||||
|
typedef int16_t s16;
|
||||||
|
typedef int32_t s32;
|
||||||
|
typedef int64_t s64;
|
||||||
|
|
||||||
|
#endif /* GEKKO */
|
||||||
|
|
||||||
|
typedef u16 le16;
|
||||||
|
typedef u32 le32;
|
||||||
|
typedef u64 le64;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Declare sle{16,32,64} to be unsigned because we do not want sign extension
|
||||||
|
* on BE architectures.
|
||||||
|
*/
|
||||||
|
typedef u16 sle16;
|
||||||
|
typedef u32 sle32;
|
||||||
|
typedef u64 sle64;
|
||||||
|
|
||||||
|
typedef u16 ntfschar; /* 2-byte Unicode character type. */
|
||||||
|
#define UCHAR_T_SIZE_BITS 1
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Clusters are signed 64-bit values on NTFS volumes. We define two types, LCN
|
||||||
|
* and VCN, to allow for type checking and better code readability.
|
||||||
|
*/
|
||||||
|
typedef s64 VCN;
|
||||||
|
typedef sle64 leVCN;
|
||||||
|
typedef s64 LCN;
|
||||||
|
typedef sle64 leLCN;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The NTFS journal $LogFile uses log sequence numbers which are signed 64-bit
|
||||||
|
* values. We define our own type LSN, to allow for type checking and better
|
||||||
|
* code readability.
|
||||||
|
*/
|
||||||
|
typedef s64 LSN;
|
||||||
|
typedef sle64 leLSN;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Cygwin has a collision between our BOOL and <windef.h>'s
|
||||||
|
* As long as this file will be included after <windows.h> were fine.
|
||||||
|
*/
|
||||||
|
#ifndef _WINDEF_H
|
||||||
|
#ifndef GEKKO
|
||||||
|
/**
|
||||||
|
* enum BOOL - These are just to make the code more readable...
|
||||||
|
*/
|
||||||
|
typedef enum {
|
||||||
|
#ifndef FALSE
|
||||||
|
FALSE = 0,
|
||||||
|
#endif
|
||||||
|
#ifndef NO
|
||||||
|
NO = 0,
|
||||||
|
#endif
|
||||||
|
#ifndef ZERO
|
||||||
|
ZERO = 0,
|
||||||
|
#endif
|
||||||
|
#ifndef TRUE
|
||||||
|
TRUE = 1,
|
||||||
|
#endif
|
||||||
|
#ifndef YES
|
||||||
|
YES = 1,
|
||||||
|
#endif
|
||||||
|
#ifndef ONE
|
||||||
|
ONE = 1,
|
||||||
|
#endif
|
||||||
|
} BOOL;
|
||||||
|
#endif /* GEKKO */
|
||||||
|
#endif /* defined _WINDEF_H */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* enum IGNORE_CASE_BOOL -
|
||||||
|
*/
|
||||||
|
typedef enum {
|
||||||
|
CASE_SENSITIVE = 0,
|
||||||
|
IGNORE_CASE = 1,
|
||||||
|
} IGNORE_CASE_BOOL;
|
||||||
|
|
||||||
|
#define STATUS_OK (0)
|
||||||
|
#define STATUS_ERROR (-1)
|
||||||
|
#define STATUS_RESIDENT_ATTRIBUTE_FILLED_MFT (-2)
|
||||||
|
#define STATUS_KEEP_SEARCHING (-3)
|
||||||
|
#define STATUS_NOT_FOUND (-4)
|
||||||
|
|
||||||
|
#endif /* defined _NTFS_TYPES_H */
|
||||||
|
|
1314
source/libntfs/unistr.c
Normal file
1314
source/libntfs/unistr.c
Normal file
File diff suppressed because it is too large
Load Diff
116
source/libntfs/unistr.h
Normal file
116
source/libntfs/unistr.h
Normal file
@ -0,0 +1,116 @@
|
|||||||
|
/*
|
||||||
|
* unistr.h - Exports for Unicode string handling. Originated from the Linux-NTFS
|
||||||
|
* project.
|
||||||
|
*
|
||||||
|
* Copyright (c) 2000-2004 Anton Altaparmakov
|
||||||
|
*
|
||||||
|
* This program/include file 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/include file 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
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _NTFS_UNISTR_H
|
||||||
|
#define _NTFS_UNISTR_H
|
||||||
|
|
||||||
|
#include "types.h"
|
||||||
|
#include "layout.h"
|
||||||
|
|
||||||
|
extern BOOL ntfs_names_are_equal(const ntfschar *s1, size_t s1_len,
|
||||||
|
const ntfschar *s2, size_t s2_len, const IGNORE_CASE_BOOL ic,
|
||||||
|
const ntfschar *upcase, const u32 upcase_size);
|
||||||
|
|
||||||
|
extern int ntfs_names_collate(const ntfschar *name1, const u32 name1_len,
|
||||||
|
const ntfschar *name2, const u32 name2_len,
|
||||||
|
const int err_val, const IGNORE_CASE_BOOL ic,
|
||||||
|
const ntfschar *upcase, const u32 upcase_len);
|
||||||
|
|
||||||
|
extern int ntfs_ucsncmp(const ntfschar *s1, const ntfschar *s2, size_t n);
|
||||||
|
|
||||||
|
extern int ntfs_ucsncasecmp(const ntfschar *s1, const ntfschar *s2, size_t n,
|
||||||
|
const ntfschar *upcase, const u32 upcase_size);
|
||||||
|
|
||||||
|
extern u32 ntfs_ucsnlen(const ntfschar *s, u32 maxlen);
|
||||||
|
|
||||||
|
extern ntfschar *ntfs_ucsndup(const ntfschar *s, u32 maxlen);
|
||||||
|
|
||||||
|
extern void ntfs_name_upcase(ntfschar *name, u32 name_len,
|
||||||
|
const ntfschar *upcase, const u32 upcase_len);
|
||||||
|
|
||||||
|
extern void ntfs_file_value_upcase(FILE_NAME_ATTR *file_name_attr,
|
||||||
|
const ntfschar *upcase, const u32 upcase_len);
|
||||||
|
|
||||||
|
extern int ntfs_file_values_compare(const FILE_NAME_ATTR *file_name_attr1,
|
||||||
|
const FILE_NAME_ATTR *file_name_attr2,
|
||||||
|
const int err_val, const IGNORE_CASE_BOOL ic,
|
||||||
|
const ntfschar *upcase, const u32 upcase_len);
|
||||||
|
|
||||||
|
extern int ntfs_ucstombs(const ntfschar *ins, const int ins_len, char **outs,
|
||||||
|
int outs_len);
|
||||||
|
extern int ntfs_mbstoucs(const char *ins, ntfschar **outs);
|
||||||
|
|
||||||
|
extern void ntfs_upcase_table_build(ntfschar *uc, u32 uc_len);
|
||||||
|
|
||||||
|
extern ntfschar *ntfs_str2ucs(const char *s, int *len);
|
||||||
|
|
||||||
|
extern void ntfs_ucsfree(ntfschar *ucs);
|
||||||
|
|
||||||
|
extern BOOL ntfs_forbidden_chars(const ntfschar *name, int len);
|
||||||
|
extern BOOL ntfs_collapsible_chars(ntfs_volume *vol,
|
||||||
|
const ntfschar *shortname, int shortlen,
|
||||||
|
const ntfschar *longname, int longlen);
|
||||||
|
|
||||||
|
extern int ntfs_set_char_encoding(const char *locale);
|
||||||
|
|
||||||
|
#if defined(__APPLE__) || defined(__DARWIN__)
|
||||||
|
/**
|
||||||
|
* Mac OS X only.
|
||||||
|
*
|
||||||
|
* Sets file name Unicode normalization form conversion on or off.
|
||||||
|
* normalize=0 : Off
|
||||||
|
* normalize=1 : On
|
||||||
|
* If set to on, all filenames returned by ntfs-3g will be converted to the NFD
|
||||||
|
* normalization form, while all filenames recieved by ntfs-3g will be converted to the NFC
|
||||||
|
* normalization form. Since Windows and most other OS:es use the NFC form while Mac OS X
|
||||||
|
* mostly uses NFD, this conversion increases compatibility between Mac applications and
|
||||||
|
* NTFS-3G.
|
||||||
|
*
|
||||||
|
* @param normalize decides whether or not the string functions will do automatic filename
|
||||||
|
* normalization when converting to and from UTF-8. 0 means normalization is disabled,
|
||||||
|
* 1 means it is enabled.
|
||||||
|
* @return -1 if the argument was invalid or an error occurred, 0 if all went well.
|
||||||
|
*/
|
||||||
|
extern int ntfs_macosx_normalize_filenames(int normalize);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Mac OS X only.
|
||||||
|
*
|
||||||
|
* Normalizes the input string "utf8_string" to one of the normalization forms NFD or NFC.
|
||||||
|
* The parameter "composed" decides whether output should be in composed, NFC, form
|
||||||
|
* (composed == 1) or decomposed, NFD, form (composed == 0).
|
||||||
|
* Input is assumed to be properly UTF-8 encoded and null-terminated. Output will be a newly
|
||||||
|
* ntfs_calloc'ed string encoded in UTF-8. It is the callers responsibility to free(...) the
|
||||||
|
* allocated string when it's no longer needed.
|
||||||
|
*
|
||||||
|
* @param utf8_string the input string, which may be in any normalization form.
|
||||||
|
* @param target a pointer where the resulting string will be stored.
|
||||||
|
* @param composed decides which composition form to normalize the input string to. 0 means
|
||||||
|
* composed form (NFC), 1 means decomposed form (NFD).
|
||||||
|
* @return -1 if the normalization failed for some reason, otherwise the length of the
|
||||||
|
* normalized string stored in target.
|
||||||
|
*/
|
||||||
|
extern int ntfs_macosx_normalize_utf8(const char *utf8_string, char **target, int composed);
|
||||||
|
#endif /* defined(__APPLE__) || defined(__DARWIN__) */
|
||||||
|
|
||||||
|
#endif /* defined _NTFS_UNISTR_H */
|
||||||
|
|
1594
source/libntfs/volume.c
Normal file
1594
source/libntfs/volume.c
Normal file
File diff suppressed because it is too large
Load Diff
274
source/libntfs/volume.h
Normal file
274
source/libntfs/volume.h
Normal file
@ -0,0 +1,274 @@
|
|||||||
|
/*
|
||||||
|
* volume.h - Exports for NTFS volume handling. Originated from the Linux-NTFS project.
|
||||||
|
*
|
||||||
|
* Copyright (c) 2000-2004 Anton Altaparmakov
|
||||||
|
* Copyright (c) 2004-2005 Richard Russon
|
||||||
|
* Copyright (c) 2005-2006 Yura Pakhuchiy
|
||||||
|
* Copyright (c) 2005-2009 Szabolcs Szakacsits
|
||||||
|
*
|
||||||
|
* This program/include file 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/include file 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
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _NTFS_VOLUME_H
|
||||||
|
#define _NTFS_VOLUME_H
|
||||||
|
|
||||||
|
#ifdef HAVE_CONFIG_H
|
||||||
|
#include "config.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef HAVE_STDIO_H
|
||||||
|
#include <stdio.h>
|
||||||
|
#endif
|
||||||
|
#ifdef HAVE_SYS_PARAM_H
|
||||||
|
#include <sys/param.h>
|
||||||
|
#endif
|
||||||
|
#ifdef HAVE_SYS_MOUNT_H
|
||||||
|
#include <sys/mount.h>
|
||||||
|
#endif
|
||||||
|
#ifdef HAVE_MNTENT_H
|
||||||
|
#include <mntent.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define CACHE_INODE_SIZE 32 /* inode cache, zero or >= 3 and not too big */
|
||||||
|
#define CACHE_SECURID_SIZE 16 /* securid cache, zero or >= 3 and not too big */
|
||||||
|
#define CACHE_LEGACY_SIZE 8 /* legacy cache size, zero or >= 3 and not too big */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Under Cygwin, DJGPP and FreeBSD we do not have MS_RDONLY,
|
||||||
|
* so we define them ourselves.
|
||||||
|
*/
|
||||||
|
#ifndef MS_RDONLY
|
||||||
|
#define MS_RDONLY 1
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define MS_EXCLUSIVE 0x08000000
|
||||||
|
|
||||||
|
#ifndef MS_RECOVER
|
||||||
|
#define MS_RECOVER 0x10000000
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define MS_IGNORE_HIBERFILE 0x20000000
|
||||||
|
|
||||||
|
/* Forward declaration */
|
||||||
|
typedef struct _ntfs_volume ntfs_volume;
|
||||||
|
|
||||||
|
#include "types.h"
|
||||||
|
#include "support.h"
|
||||||
|
#include "device.h"
|
||||||
|
#include "inode.h"
|
||||||
|
#include "attrib.h"
|
||||||
|
#include "index.h"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* enum ntfs_mount_flags -
|
||||||
|
*
|
||||||
|
* Flags returned by the ntfs_check_if_mounted() function.
|
||||||
|
*/
|
||||||
|
typedef enum {
|
||||||
|
NTFS_MF_MOUNTED = 1, /* Device is mounted. */
|
||||||
|
NTFS_MF_ISROOT = 2, /* Device is mounted as system root. */
|
||||||
|
NTFS_MF_READONLY = 4, /* Device is mounted read-only. */
|
||||||
|
} ntfs_mount_flags;
|
||||||
|
|
||||||
|
extern int ntfs_check_if_mounted(const char *file, unsigned long *mnt_flags);
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
NTFS_VOLUME_OK = 0,
|
||||||
|
NTFS_VOLUME_SYNTAX_ERROR = 11,
|
||||||
|
NTFS_VOLUME_NOT_NTFS = 12,
|
||||||
|
NTFS_VOLUME_CORRUPT = 13,
|
||||||
|
NTFS_VOLUME_HIBERNATED = 14,
|
||||||
|
NTFS_VOLUME_UNCLEAN_UNMOUNT = 15,
|
||||||
|
NTFS_VOLUME_LOCKED = 16,
|
||||||
|
NTFS_VOLUME_RAID = 17,
|
||||||
|
NTFS_VOLUME_UNKNOWN_REASON = 18,
|
||||||
|
NTFS_VOLUME_NO_PRIVILEGE = 19,
|
||||||
|
NTFS_VOLUME_OUT_OF_MEMORY = 20,
|
||||||
|
NTFS_VOLUME_FUSE_ERROR = 21,
|
||||||
|
NTFS_VOLUME_INSECURE = 22
|
||||||
|
} ntfs_volume_status;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* enum ntfs_volume_state_bits -
|
||||||
|
*
|
||||||
|
* Defined bits for the state field in the ntfs_volume structure.
|
||||||
|
*/
|
||||||
|
typedef enum {
|
||||||
|
NV_ReadOnly, /* 1: Volume is read-only. */
|
||||||
|
NV_CaseSensitive, /* 1: Volume is mounted case-sensitive. */
|
||||||
|
NV_LogFileEmpty, /* 1: $logFile journal is empty. */
|
||||||
|
} ntfs_volume_state_bits;
|
||||||
|
|
||||||
|
#define test_nvol_flag(nv, flag) test_bit(NV_##flag, (nv)->state)
|
||||||
|
#define set_nvol_flag(nv, flag) set_bit(NV_##flag, (nv)->state)
|
||||||
|
#define clear_nvol_flag(nv, flag) clear_bit(NV_##flag, (nv)->state)
|
||||||
|
|
||||||
|
#define NVolReadOnly(nv) test_nvol_flag(nv, ReadOnly)
|
||||||
|
#define NVolSetReadOnly(nv) set_nvol_flag(nv, ReadOnly)
|
||||||
|
#define NVolClearReadOnly(nv) clear_nvol_flag(nv, ReadOnly)
|
||||||
|
|
||||||
|
#define NVolCaseSensitive(nv) test_nvol_flag(nv, CaseSensitive)
|
||||||
|
#define NVolSetCaseSensitive(nv) set_nvol_flag(nv, CaseSensitive)
|
||||||
|
#define NVolClearCaseSensitive(nv) clear_nvol_flag(nv, CaseSensitive)
|
||||||
|
|
||||||
|
#define NVolLogFileEmpty(nv) test_nvol_flag(nv, LogFileEmpty)
|
||||||
|
#define NVolSetLogFileEmpty(nv) set_nvol_flag(nv, LogFileEmpty)
|
||||||
|
#define NVolClearLogFileEmpty(nv) clear_nvol_flag(nv, LogFileEmpty)
|
||||||
|
|
||||||
|
/*
|
||||||
|
* NTFS version 1.1 and 1.2 are used by Windows NT4.
|
||||||
|
* NTFS version 2.x is used by Windows 2000 Beta
|
||||||
|
* NTFS version 3.0 is used by Windows 2000.
|
||||||
|
* NTFS version 3.1 is used by Windows XP, 2003 and Vista.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#define NTFS_V1_1(major, minor) ((major) == 1 && (minor) == 1)
|
||||||
|
#define NTFS_V1_2(major, minor) ((major) == 1 && (minor) == 2)
|
||||||
|
#define NTFS_V2_X(major, minor) ((major) == 2)
|
||||||
|
#define NTFS_V3_0(major, minor) ((major) == 3 && (minor) == 0)
|
||||||
|
#define NTFS_V3_1(major, minor) ((major) == 3 && (minor) == 1)
|
||||||
|
|
||||||
|
#define NTFS_BUF_SIZE 8192
|
||||||
|
|
||||||
|
/**
|
||||||
|
* struct _ntfs_volume - structure describing an open volume in memory.
|
||||||
|
*/
|
||||||
|
struct _ntfs_volume {
|
||||||
|
union {
|
||||||
|
struct ntfs_device *dev; /* NTFS device associated with
|
||||||
|
the volume. */
|
||||||
|
void *sb; /* For kernel porting compatibility. */
|
||||||
|
};
|
||||||
|
char *vol_name; /* Name of the volume. */
|
||||||
|
unsigned long state; /* NTFS specific flags describing this volume.
|
||||||
|
See ntfs_volume_state_bits above. */
|
||||||
|
|
||||||
|
ntfs_inode *vol_ni; /* ntfs_inode structure for FILE_Volume. */
|
||||||
|
u8 major_ver; /* Ntfs major version of volume. */
|
||||||
|
u8 minor_ver; /* Ntfs minor version of volume. */
|
||||||
|
u16 flags; /* Bit array of VOLUME_* flags. */
|
||||||
|
|
||||||
|
u16 sector_size; /* Byte size of a sector. */
|
||||||
|
u8 sector_size_bits; /* Log(2) of the byte size of a sector. */
|
||||||
|
u32 cluster_size; /* Byte size of a cluster. */
|
||||||
|
u32 mft_record_size; /* Byte size of a mft record. */
|
||||||
|
u32 indx_record_size; /* Byte size of a INDX record. */
|
||||||
|
u8 cluster_size_bits; /* Log(2) of the byte size of a cluster. */
|
||||||
|
u8 mft_record_size_bits;/* Log(2) of the byte size of a mft record. */
|
||||||
|
u8 indx_record_size_bits;/* Log(2) of the byte size of a INDX record. */
|
||||||
|
|
||||||
|
/* Variables used by the cluster and mft allocators. */
|
||||||
|
u8 mft_zone_multiplier; /* Initial mft zone multiplier. */
|
||||||
|
u8 full_zones; /* cluster zones which are full */
|
||||||
|
s64 mft_data_pos; /* Mft record number at which to allocate the
|
||||||
|
next mft record. */
|
||||||
|
LCN mft_zone_start; /* First cluster of the mft zone. */
|
||||||
|
LCN mft_zone_end; /* First cluster beyond the mft zone. */
|
||||||
|
LCN mft_zone_pos; /* Current position in the mft zone. */
|
||||||
|
LCN data1_zone_pos; /* Current position in the first data zone. */
|
||||||
|
LCN data2_zone_pos; /* Current position in the second data zone. */
|
||||||
|
|
||||||
|
s64 nr_clusters; /* Volume size in clusters, hence also the
|
||||||
|
number of bits in lcn_bitmap. */
|
||||||
|
ntfs_inode *lcnbmp_ni; /* ntfs_inode structure for FILE_Bitmap. */
|
||||||
|
ntfs_attr *lcnbmp_na; /* ntfs_attr structure for the data attribute
|
||||||
|
of FILE_Bitmap. Each bit represents a
|
||||||
|
cluster on the volume, bit 0 representing
|
||||||
|
lcn 0 and so on. A set bit means that the
|
||||||
|
cluster and vice versa. */
|
||||||
|
|
||||||
|
LCN mft_lcn; /* Logical cluster number of the data attribute
|
||||||
|
for FILE_MFT. */
|
||||||
|
ntfs_inode *mft_ni; /* ntfs_inode structure for FILE_MFT. */
|
||||||
|
ntfs_attr *mft_na; /* ntfs_attr structure for the data attribute
|
||||||
|
of FILE_MFT. */
|
||||||
|
ntfs_attr *mftbmp_na; /* ntfs_attr structure for the bitmap attribute
|
||||||
|
of FILE_MFT. Each bit represents an mft
|
||||||
|
record in the $DATA attribute, bit 0
|
||||||
|
representing mft record 0 and so on. A set
|
||||||
|
bit means that the mft record is in use and
|
||||||
|
vice versa. */
|
||||||
|
|
||||||
|
ntfs_inode *secure_ni; /* ntfs_inode structure for FILE $Secure */
|
||||||
|
ntfs_index_context *secure_xsii; /* index for using $Secure:$SII */
|
||||||
|
ntfs_index_context *secure_xsdh; /* index for using $Secure:$SDH */
|
||||||
|
int secure_reentry; /* check for non-rentries */
|
||||||
|
unsigned int secure_flags; /* flags, see security.h for values */
|
||||||
|
|
||||||
|
int mftmirr_size; /* Size of the FILE_MFTMirr in mft records. */
|
||||||
|
LCN mftmirr_lcn; /* Logical cluster number of the data attribute
|
||||||
|
for FILE_MFTMirr. */
|
||||||
|
ntfs_inode *mftmirr_ni; /* ntfs_inode structure for FILE_MFTMirr. */
|
||||||
|
ntfs_attr *mftmirr_na; /* ntfs_attr structure for the data attribute
|
||||||
|
of FILE_MFTMirr. */
|
||||||
|
|
||||||
|
ntfschar *upcase; /* Upper case equivalents of all 65536 2-byte
|
||||||
|
Unicode characters. Obtained from
|
||||||
|
FILE_UpCase. */
|
||||||
|
u32 upcase_len; /* Length in Unicode characters of the upcase
|
||||||
|
table. */
|
||||||
|
|
||||||
|
ATTR_DEF *attrdef; /* Attribute definitions. Obtained from
|
||||||
|
FILE_AttrDef. */
|
||||||
|
s32 attrdef_len; /* Size of the attribute definition table in
|
||||||
|
bytes. */
|
||||||
|
|
||||||
|
s64 free_clusters; /* Track the number of free clusters which
|
||||||
|
greatly improves statfs() performance */
|
||||||
|
s64 free_mft_records; /* Same for free mft records (see above) */
|
||||||
|
BOOL efs_raw; /* volume is mounted for raw access to
|
||||||
|
efs-encrypted files */
|
||||||
|
|
||||||
|
#if CACHE_INODE_SIZE
|
||||||
|
struct CACHE_HEADER *xinode_cache;
|
||||||
|
#endif
|
||||||
|
#if CACHE_SECURID_SIZE
|
||||||
|
struct CACHE_HEADER *securid_cache;
|
||||||
|
#endif
|
||||||
|
#if CACHE_LEGACY_SIZE
|
||||||
|
struct CACHE_HEADER *legacy_cache;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
extern const char *ntfs_home;
|
||||||
|
|
||||||
|
extern ntfs_volume *ntfs_volume_alloc(void);
|
||||||
|
|
||||||
|
extern ntfs_volume *ntfs_volume_startup(struct ntfs_device *dev,
|
||||||
|
unsigned long flags);
|
||||||
|
|
||||||
|
extern ntfs_volume *ntfs_device_mount(struct ntfs_device *dev,
|
||||||
|
unsigned long flags);
|
||||||
|
|
||||||
|
extern ntfs_volume *ntfs_mount(const char *name, unsigned long flags);
|
||||||
|
extern int ntfs_umount(ntfs_volume *vol, const BOOL force);
|
||||||
|
|
||||||
|
extern int ntfs_version_is_supported(ntfs_volume *vol);
|
||||||
|
extern int ntfs_volume_check_hiberfile(ntfs_volume *vol, int verbose);
|
||||||
|
extern int ntfs_logfile_reset(ntfs_volume *vol);
|
||||||
|
|
||||||
|
extern int ntfs_volume_write_flags(ntfs_volume *vol, const u16 flags);
|
||||||
|
|
||||||
|
extern int ntfs_volume_error(int err);
|
||||||
|
extern void ntfs_mount_error(const char *vol, const char *mntpoint, int err);
|
||||||
|
|
||||||
|
extern int ntfs_volume_get_free_space(ntfs_volume *vol);
|
||||||
|
|
||||||
|
extern int ntfs_set_locale(void);
|
||||||
|
|
||||||
|
#endif /* defined _NTFS_VOLUME_H */
|
||||||
|
|
Loading…
Reference in New Issue
Block a user