Update libntfs to ntfs-3g-2010.10.2. Preparing for future updates through the vendor branch. New api(libntfs) fixes.
git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@39699 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
parent
8ce987787a
commit
0d2c294fa1
@ -922,7 +922,8 @@ fs_create(fs_volume *_vol, fs_vnode *_dir, const char *name, int omode,
|
||||
result = errno;
|
||||
}
|
||||
} else {
|
||||
ni = ntfs_create(bi, uname, unameLength, S_IFREG);
|
||||
le32 securid = 0;
|
||||
ni = ntfs_create(bi, securid, uname, unameLength, S_IFREG);
|
||||
if (ni) {
|
||||
*_vnid = MREF(ni->mft_no);
|
||||
|
||||
@ -1296,6 +1297,7 @@ fs_create_symlink(fs_volume *_vol, fs_vnode *_dir, const char *name,
|
||||
int utargetLength;
|
||||
status_t result = B_NO_ERROR;
|
||||
int fmode;
|
||||
le32 securid = 0;
|
||||
|
||||
LOCK_VOL(ns);
|
||||
|
||||
@ -1324,7 +1326,7 @@ fs_create_symlink(fs_volume *_vol, fs_vnode *_dir, const char *name,
|
||||
goto exit;
|
||||
}
|
||||
|
||||
sym = ntfs_create_symlink(bi, uname, unameLength, utarget, utargetLength);
|
||||
sym = ntfs_create_symlink(bi, securid, uname, unameLength, utarget, utargetLength);
|
||||
if (sym == NULL) {
|
||||
result = EINVAL;
|
||||
goto exit;
|
||||
@ -1385,6 +1387,7 @@ fs_mkdir(fs_volume *_vol, fs_vnode *_dir, const char *name, int perms)
|
||||
ntfs_inode *ni = NULL;
|
||||
ntfs_inode *bi = NULL;
|
||||
status_t result = B_NO_ERROR;
|
||||
le32 securid = 0;
|
||||
|
||||
if (ns->flags & B_FS_IS_READONLY) {
|
||||
ERRPRINT("ntfs is read-only\n");
|
||||
@ -1417,7 +1420,7 @@ fs_mkdir(fs_volume *_vol, fs_vnode *_dir, const char *name, int perms)
|
||||
goto exit;
|
||||
}
|
||||
|
||||
ni = ntfs_create(bi, uname, unameLength, S_IFDIR);
|
||||
ni = ntfs_create(bi, securid, uname, unameLength, S_IFDIR);
|
||||
if (ni) {
|
||||
ino_t vnid = MREF(ni->mft_no);
|
||||
|
||||
@ -1482,6 +1485,8 @@ fs_rename(fs_volume *_vol, fs_vnode *_odir, const char *oldname,
|
||||
|
||||
status_t result = B_NO_ERROR;
|
||||
|
||||
char path[MAX_PATH];
|
||||
|
||||
if (ns->flags & B_FS_IS_READONLY) {
|
||||
ERRPRINT("ntfs is read-only\n");
|
||||
return EROFS;
|
||||
@ -1568,7 +1573,12 @@ fs_rename(fs_volume *_vol, fs_vnode *_odir, const char *oldname,
|
||||
notify_entry_moved(ns->id, MREF(odi->mft_no), oldname, MREF(ndi->mft_no),
|
||||
newname, onode->vnid);
|
||||
|
||||
ntfs_delete(oi, odi, uoldname, uoldnameLength);
|
||||
if (utils_inode_get_name(oi, path, MAX_PATH) == 0) {
|
||||
result = EINVAL;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
ntfs_delete(ns->ntvol, path, oi, odi, uoldname, uoldnameLength);
|
||||
oi = odi = NULL;
|
||||
/* ntfs_delete() always closes ni and dir_ni */
|
||||
|
||||
@ -1615,7 +1625,12 @@ fs_rename(fs_volume *_vol, fs_vnode *_odir, const char *oldname,
|
||||
newname, onode->vnid);
|
||||
put_vnode(_vol, onode->vnid);
|
||||
|
||||
ntfs_delete(oi, odi, uoldname, uoldnameLength);
|
||||
if (utils_inode_get_name(oi, path, MAX_PATH) == 0) {
|
||||
result = EINVAL;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
ntfs_delete(ns->ntvol, path, oi, odi, uoldname, uoldnameLength);
|
||||
oi = odi = NULL;
|
||||
/* ntfs_delete() always closes ni and dir_ni */
|
||||
}
|
||||
@ -1640,13 +1655,15 @@ exit:
|
||||
static status_t
|
||||
do_unlink(fs_volume *_vol, vnode *dir, const char *name, bool isdir)
|
||||
{
|
||||
nspace *vol = (nspace*)_vol->private_volume;
|
||||
nspace *ns = (nspace*)_vol->private_volume;
|
||||
ino_t vnid;
|
||||
vnode *node = NULL;
|
||||
ntfs_inode *ni = NULL;
|
||||
ntfs_inode *bi = NULL;
|
||||
ntfschar *uname = NULL;
|
||||
int unameLength;
|
||||
char path[MAX_PATH];
|
||||
|
||||
status_t result = B_NO_ERROR;
|
||||
|
||||
unameLength = ntfs_mbstoucs(name, &uname);
|
||||
@ -1655,7 +1672,7 @@ do_unlink(fs_volume *_vol, vnode *dir, const char *name, bool isdir)
|
||||
goto exit1;
|
||||
}
|
||||
|
||||
bi = ntfs_inode_open(vol->ntvol, dir->vnid);
|
||||
bi = ntfs_inode_open(ns->ntvol, dir->vnid);
|
||||
if (bi == NULL) {
|
||||
result = ENOENT;
|
||||
goto exit1;
|
||||
@ -1675,7 +1692,7 @@ do_unlink(fs_volume *_vol, vnode *dir, const char *name, bool isdir)
|
||||
goto exit1;
|
||||
}
|
||||
|
||||
ni = ntfs_inode_open(vol->ntvol, node->vnid);
|
||||
ni = ntfs_inode_open(ns->ntvol, node->vnid);
|
||||
if (ni == NULL) {
|
||||
result = ENOENT;
|
||||
goto exit2;
|
||||
@ -1695,15 +1712,20 @@ do_unlink(fs_volume *_vol, vnode *dir, const char *name, bool isdir)
|
||||
goto exit2;
|
||||
}
|
||||
|
||||
if (utils_inode_get_name(ni, path, MAX_PATH) == 0) {
|
||||
result = EINVAL;
|
||||
goto exit2;
|
||||
}
|
||||
|
||||
// TODO: the file must not be deleted here, only unlinked!
|
||||
if (ntfs_delete(ni, bi, uname, unameLength))
|
||||
if (ntfs_delete(ns->ntvol, path, ni, bi, uname, unameLength))
|
||||
result = errno;
|
||||
|
||||
ni = bi = NULL;
|
||||
|
||||
node->parent_vnid = dir->vnid;
|
||||
|
||||
notify_entry_removed(vol->id, dir->vnid, name, vnid);
|
||||
notify_entry_removed(ns->id, dir->vnid, name, vnid);
|
||||
|
||||
result = remove_vnode(_vol, vnid);
|
||||
|
||||
|
@ -3,17 +3,19 @@ SubDir HAIKU_TOP src add-ons kernel file_systems ntfs libntfs ;
|
||||
DEFINES += HAVE_CONFIG_H=1 ;
|
||||
|
||||
KernelStaticLibrary libntfs.a :
|
||||
acls.c
|
||||
attrib.c
|
||||
attrlist.c
|
||||
bitmap.c
|
||||
bootsect.c
|
||||
cache.c
|
||||
collate.c
|
||||
compat.c
|
||||
compress.c
|
||||
debug.c
|
||||
device.c
|
||||
device_io.c
|
||||
dir.c
|
||||
efs.c
|
||||
index.c
|
||||
inode.c
|
||||
lcnalloc.c
|
||||
@ -22,8 +24,11 @@ KernelStaticLibrary libntfs.a :
|
||||
mft.c
|
||||
misc.c
|
||||
mst.c
|
||||
object_id.c
|
||||
reparse.c
|
||||
runlist.c
|
||||
security.c
|
||||
unistr.c
|
||||
unix_io.c
|
||||
volume.c
|
||||
;
|
||||
|
4303
src/add-ons/kernel/file_systems/ntfs/libntfs/acls.c
Normal file
4303
src/add-ons/kernel/file_systems/ntfs/libntfs/acls.c
Normal file
File diff suppressed because it is too large
Load Diff
199
src/add-ons/kernel/file_systems/ntfs/libntfs/acls.h
Normal file
199
src/add-ons/kernel/file_systems/ntfs/libntfs/acls.h
Normal file
@ -0,0 +1,199 @@
|
||||
/*
|
||||
*
|
||||
* 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 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 */
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -37,6 +37,10 @@ typedef struct _ntfs_attr_search_ctx ntfs_attr_search_ctx;
|
||||
#include "logging.h"
|
||||
|
||||
extern ntfschar AT_UNNAMED[];
|
||||
extern ntfschar STREAM_SDS[];
|
||||
|
||||
/* The little endian Unicode string $TXF_DATA as a global constant. */
|
||||
extern ntfschar TXF_DATA[10];
|
||||
|
||||
/**
|
||||
* enum ntfs_lcn_special_values - special return values for ntfs_*_vcn_to_lcn()
|
||||
@ -174,6 +178,7 @@ struct _ntfs_attr {
|
||||
runlist_element *rl;
|
||||
ntfs_inode *ni;
|
||||
ATTR_TYPES type;
|
||||
ATTR_FLAGS data_flags;
|
||||
ntfschar *name;
|
||||
u32 name_len;
|
||||
unsigned long state;
|
||||
@ -184,6 +189,7 @@ struct _ntfs_attr {
|
||||
u32 compression_block_size;
|
||||
u8 compression_block_size_bits;
|
||||
u8 compression_block_clusters;
|
||||
s8 unused_runs; /* pre-reserved entries available */
|
||||
};
|
||||
|
||||
/**
|
||||
@ -193,6 +199,9 @@ struct _ntfs_attr {
|
||||
typedef enum {
|
||||
NA_Initialized, /* 1: structure is initialized. */
|
||||
NA_NonResident, /* 1: Attribute is not resident. */
|
||||
NA_BeingNonResident, /* 1: Attribute is being made not resident. */
|
||||
NA_FullyMapped, /* 1: Attribute has been fully mapped */
|
||||
NA_ComprClosing, /* 1: Compressed attribute is being closed */
|
||||
} ntfs_attr_state_bits;
|
||||
|
||||
#define test_nattr_flag(na, flag) test_bit(NA_##flag, (na)->state)
|
||||
@ -207,6 +216,18 @@ typedef enum {
|
||||
#define NAttrSetNonResident(na) set_nattr_flag(na, NonResident)
|
||||
#define NAttrClearNonResident(na) clear_nattr_flag(na, NonResident)
|
||||
|
||||
#define NAttrBeingNonResident(na) test_nattr_flag(na, BeingNonResident)
|
||||
#define NAttrSetBeingNonResident(na) set_nattr_flag(na, BeingNonResident)
|
||||
#define NAttrClearBeingNonResident(na) clear_nattr_flag(na, BeingNonResident)
|
||||
|
||||
#define NAttrFullyMapped(na) test_nattr_flag(na, FullyMapped)
|
||||
#define NAttrSetFullyMapped(na) set_nattr_flag(na, FullyMapped)
|
||||
#define NAttrClearFullyMapped(na) clear_nattr_flag(na, FullyMapped)
|
||||
|
||||
#define NAttrComprClosing(na) test_nattr_flag(na, ComprClosing)
|
||||
#define NAttrSetComprClosing(na) set_nattr_flag(na, ComprClosing)
|
||||
#define NAttrClearComprClosing(na) clear_nattr_flag(na, ComprClosing)
|
||||
|
||||
#define GenNAttrIno(func_name, flag) \
|
||||
extern int NAttr##func_name(ntfs_attr *na); \
|
||||
extern void NAttrSet##func_name(ntfs_attr *na); \
|
||||
@ -245,11 +266,14 @@ typedef union {
|
||||
} attr_val;
|
||||
|
||||
extern void ntfs_attr_init(ntfs_attr *na, const BOOL non_resident,
|
||||
const BOOL compressed, const BOOL encrypted, const BOOL sparse,
|
||||
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);
|
||||
@ -258,6 +282,7 @@ 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);
|
||||
@ -275,11 +300,11 @@ 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);
|
||||
int ntfs_attr_force_non_resident(ntfs_attr *na);
|
||||
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,
|
||||
@ -292,6 +317,8 @@ 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);
|
||||
|
609
src/add-ons/kernel/file_systems/ntfs/libntfs/cache.c
Normal file
609
src/add-ons/kernel/file_systems/ntfs/libntfs/cache.c
Normal file
@ -0,0 +1,609 @@
|
||||
/**
|
||||
* cache.c : deal with LRU caches
|
||||
*
|
||||
* 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_STRING_H
|
||||
#include <string.h>
|
||||
#endif
|
||||
|
||||
#include "types.h"
|
||||
#include "security.h"
|
||||
#include "cache.h"
|
||||
#include "misc.h"
|
||||
#include "logging.h"
|
||||
|
||||
/*
|
||||
* 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.
|
||||
* When there is a hashing bug, hashing is dropped, and sequential
|
||||
* searches are used.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Enter a new hash index, after a new record has been inserted
|
||||
*
|
||||
* Do not call when a record has been modified (with no key change)
|
||||
*/
|
||||
|
||||
static void inserthashindex(struct CACHE_HEADER *cache,
|
||||
struct CACHED_GENERIC *current)
|
||||
{
|
||||
int h;
|
||||
struct HASH_ENTRY *link;
|
||||
struct HASH_ENTRY *first;
|
||||
|
||||
if (cache->dohash) {
|
||||
h = cache->dohash(current);
|
||||
if ((h >= 0) && (h < cache->max_hash)) {
|
||||
/* get a free link and insert at top of hash list */
|
||||
link = cache->free_hash;
|
||||
if (link) {
|
||||
cache->free_hash = link->next;
|
||||
first = cache->first_hash[h];
|
||||
if (first)
|
||||
link->next = first;
|
||||
else
|
||||
link->next = NULL;
|
||||
link->entry = current;
|
||||
cache->first_hash[h] = link;
|
||||
} else {
|
||||
ntfs_log_error("No more hash entries,"
|
||||
" cache %s hashing dropped\n",
|
||||
cache->name);
|
||||
cache->dohash = (cache_hash)NULL;
|
||||
}
|
||||
} else {
|
||||
ntfs_log_error("Illegal hash value,"
|
||||
" cache %s hashing dropped\n",
|
||||
cache->name);
|
||||
cache->dohash = (cache_hash)NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Drop a hash index when a record is about to be deleted
|
||||
*/
|
||||
|
||||
static void drophashindex(struct CACHE_HEADER *cache,
|
||||
const struct CACHED_GENERIC *current, int hash)
|
||||
{
|
||||
struct HASH_ENTRY *link;
|
||||
struct HASH_ENTRY *previous;
|
||||
|
||||
if (cache->dohash) {
|
||||
if ((hash >= 0) && (hash < cache->max_hash)) {
|
||||
/* find the link and unlink */
|
||||
link = cache->first_hash[hash];
|
||||
previous = (struct HASH_ENTRY*)NULL;
|
||||
while (link && (link->entry != current)) {
|
||||
previous = link;
|
||||
link = link->next;
|
||||
}
|
||||
if (link) {
|
||||
if (previous)
|
||||
previous->next = link->next;
|
||||
else
|
||||
cache->first_hash[hash] = link->next;
|
||||
link->next = cache->free_hash;
|
||||
cache->free_hash = link;
|
||||
} else {
|
||||
ntfs_log_error("Bad hash list,"
|
||||
" cache %s hashing dropped\n",
|
||||
cache->name);
|
||||
cache->dohash = (cache_hash)NULL;
|
||||
}
|
||||
} else {
|
||||
ntfs_log_error("Illegal hash value,"
|
||||
" cache %s hashing dropped\n",
|
||||
cache->name);
|
||||
cache->dohash = (cache_hash)NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Fetch an entry from cache
|
||||
*
|
||||
* returns the cache entry, or NULL if not available
|
||||
* The returned entry may be modified, but not freed
|
||||
*/
|
||||
|
||||
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;
|
||||
struct HASH_ENTRY *link;
|
||||
int h;
|
||||
|
||||
current = (struct CACHED_GENERIC*)NULL;
|
||||
if (cache) {
|
||||
if (cache->dohash) {
|
||||
/*
|
||||
* When possible, use the hash table to
|
||||
* locate the entry if present
|
||||
*/
|
||||
h = cache->dohash(wanted);
|
||||
link = cache->first_hash[h];
|
||||
while (link && compare(link->entry, wanted))
|
||||
link = link->next;
|
||||
if (link)
|
||||
current = link->entry;
|
||||
}
|
||||
if (!cache->dohash) {
|
||||
/*
|
||||
* Search sequentially in LRU list if no hash table
|
||||
* or if hashing has just failed
|
||||
*/
|
||||
current = cache->most_recent_entry;
|
||||
while (current
|
||||
&& compare(current, wanted)) {
|
||||
current = current->next;
|
||||
}
|
||||
}
|
||||
if (current) {
|
||||
previous = current->previous;
|
||||
cache->hits++;
|
||||
if (previous) {
|
||||
/*
|
||||
* found and not at head of list, unlink from current
|
||||
* position and relink as head of list
|
||||
*/
|
||||
previous->next = current->next;
|
||||
if (current->next)
|
||||
current->next->previous
|
||||
= current->previous;
|
||||
else
|
||||
cache->oldest_entry
|
||||
= current->previous;
|
||||
current->next = cache->most_recent_entry;
|
||||
current->previous
|
||||
= (struct CACHED_GENERIC*)NULL;
|
||||
cache->most_recent_entry->previous = current;
|
||||
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 *before;
|
||||
struct HASH_ENTRY *link;
|
||||
int h;
|
||||
|
||||
current = (struct CACHED_GENERIC*)NULL;
|
||||
if (cache) {
|
||||
if (cache->dohash) {
|
||||
/*
|
||||
* When possible, use the hash table to
|
||||
* find out whether the entry if present
|
||||
*/
|
||||
h = cache->dohash(item);
|
||||
link = cache->first_hash[h];
|
||||
while (link && compare(link->entry, item))
|
||||
link = link->next;
|
||||
if (link) {
|
||||
current = link->entry;
|
||||
}
|
||||
}
|
||||
if (!cache->dohash) {
|
||||
/*
|
||||
* 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;
|
||||
while (current
|
||||
&& compare(current, item)) {
|
||||
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;
|
||||
if (!cache->oldest_entry)
|
||||
cache->oldest_entry = current;
|
||||
} else {
|
||||
/* reusing the oldest entry */
|
||||
current = cache->oldest_entry;
|
||||
before = current->previous;
|
||||
before->next = (struct CACHED_GENERIC*)NULL;
|
||||
if (cache->dohash)
|
||||
drophashindex(cache,current,
|
||||
cache->dohash(current));
|
||||
if (cache->dofree)
|
||||
cache->dofree(current);
|
||||
cache->oldest_entry = 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;
|
||||
current->previous = (struct CACHED_GENERIC*)NULL;
|
||||
if (cache->most_recent_entry)
|
||||
cache->most_recent_entry->previous = current;
|
||||
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;
|
||||
}
|
||||
if (cache->dohash && current)
|
||||
inserthashindex(cache,current);
|
||||
}
|
||||
cache->writes++;
|
||||
}
|
||||
return (current);
|
||||
}
|
||||
|
||||
/*
|
||||
* Invalidate a cache entry
|
||||
* The entry is moved to the free entry list
|
||||
* A specific function may be called for entry deletion
|
||||
*/
|
||||
|
||||
static void do_invalidate(struct CACHE_HEADER *cache,
|
||||
struct CACHED_GENERIC *current, int flags)
|
||||
{
|
||||
struct CACHED_GENERIC *previous;
|
||||
|
||||
previous = current->previous;
|
||||
if ((flags & CACHE_FREE) && cache->dofree)
|
||||
cache->dofree(current);
|
||||
/*
|
||||
* Relink into free list
|
||||
*/
|
||||
if (current->next)
|
||||
current->next->previous = current->previous;
|
||||
else
|
||||
cache->oldest_entry = current->previous;
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* 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,
|
||||
int flags)
|
||||
{
|
||||
struct CACHED_GENERIC *current;
|
||||
struct CACHED_GENERIC *previous;
|
||||
struct CACHED_GENERIC *next;
|
||||
struct HASH_ENTRY *link;
|
||||
int count;
|
||||
int h;
|
||||
|
||||
current = (struct CACHED_GENERIC*)NULL;
|
||||
count = 0;
|
||||
if (cache) {
|
||||
if (!(flags & CACHE_NOHASH) && cache->dohash) {
|
||||
/*
|
||||
* When possible, use the hash table to
|
||||
* find out whether the entry if present
|
||||
*/
|
||||
h = cache->dohash(item);
|
||||
link = cache->first_hash[h];
|
||||
while (link) {
|
||||
if (compare(link->entry, item))
|
||||
link = link->next;
|
||||
else {
|
||||
current = link->entry;
|
||||
link = link->next;
|
||||
if (current) {
|
||||
drophashindex(cache,current,h);
|
||||
do_invalidate(cache,
|
||||
current,flags);
|
||||
count++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if ((flags & CACHE_NOHASH) || !cache->dohash) {
|
||||
/*
|
||||
* Search sequentially in LRU list
|
||||
*/
|
||||
current = cache->most_recent_entry;
|
||||
previous = (struct CACHED_GENERIC*)NULL;
|
||||
while (current) {
|
||||
if (!compare(current, item)) {
|
||||
next = current->next;
|
||||
if (cache->dohash)
|
||||
drophashindex(cache,current,
|
||||
cache->dohash(current));
|
||||
do_invalidate(cache,current,flags);
|
||||
current = next;
|
||||
count++;
|
||||
} else {
|
||||
previous = current;
|
||||
current = current->next;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return (count);
|
||||
}
|
||||
|
||||
int ntfs_remove_cache(struct CACHE_HEADER *cache,
|
||||
struct CACHED_GENERIC *item, int flags)
|
||||
{
|
||||
int count;
|
||||
|
||||
count = 0;
|
||||
if (cache) {
|
||||
if (cache->dohash)
|
||||
drophashindex(cache,item,cache->dohash(item));
|
||||
do_invalidate(cache,item,flags);
|
||||
count++;
|
||||
}
|
||||
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 (cache->dofree)
|
||||
cache->dofree(entry);
|
||||
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,
|
||||
cache_free dofree, cache_hash dohash,
|
||||
int full_item_size,
|
||||
int item_count, int max_hash)
|
||||
{
|
||||
struct CACHE_HEADER *cache;
|
||||
struct CACHED_GENERIC *pc;
|
||||
struct CACHED_GENERIC *qc;
|
||||
struct HASH_ENTRY *ph;
|
||||
struct HASH_ENTRY *qh;
|
||||
struct HASH_ENTRY **px;
|
||||
size_t size;
|
||||
int i;
|
||||
|
||||
size = sizeof(struct CACHE_HEADER) + item_count*full_item_size;
|
||||
if (max_hash)
|
||||
size += item_count*sizeof(struct HASH_ENTRY)
|
||||
+ max_hash*sizeof(struct HASH_ENTRY*);
|
||||
cache = (struct CACHE_HEADER*)ntfs_malloc(size);
|
||||
if (cache) {
|
||||
/* header */
|
||||
cache->name = name;
|
||||
cache->dofree = dofree;
|
||||
if (dohash && max_hash) {
|
||||
cache->dohash = dohash;
|
||||
cache->max_hash = max_hash;
|
||||
} else {
|
||||
cache->dohash = (cache_hash)NULL;
|
||||
cache->max_hash = 0;
|
||||
}
|
||||
cache->fixed_size = full_item_size - sizeof(struct CACHED_GENERIC);
|
||||
cache->reads = 0;
|
||||
cache->writes = 0;
|
||||
cache->hits = 0;
|
||||
/* chain the data entries, and mark an invalid entry */
|
||||
cache->most_recent_entry = (struct CACHED_GENERIC*)NULL;
|
||||
cache->oldest_entry = (struct CACHED_GENERIC*)NULL;
|
||||
cache->free_entry = &cache->entry[0];
|
||||
pc = &cache->entry[0];
|
||||
for (i=0; i<(item_count - 1); i++) {
|
||||
qc = (struct CACHED_GENERIC*)((char*)pc
|
||||
+ full_item_size);
|
||||
pc->next = qc;
|
||||
pc->variable = (void*)NULL;
|
||||
pc->varsize = 0;
|
||||
pc = qc;
|
||||
}
|
||||
/* special for the last entry */
|
||||
pc->next = (struct CACHED_GENERIC*)NULL;
|
||||
pc->variable = (void*)NULL;
|
||||
pc->varsize = 0;
|
||||
|
||||
if (max_hash) {
|
||||
/* chain the hash entries */
|
||||
ph = (struct HASH_ENTRY*)(((char*)pc) + full_item_size);
|
||||
cache->free_hash = ph;
|
||||
for (i=0; i<(item_count - 1); i++) {
|
||||
qh = &ph[1];
|
||||
ph->next = qh;
|
||||
ph = qh;
|
||||
}
|
||||
/* special for the last entry */
|
||||
if (item_count) {
|
||||
ph->next = (struct HASH_ENTRY*)NULL;
|
||||
}
|
||||
/* create and initialize the hash indexes */
|
||||
px = (struct HASH_ENTRY**)&ph[1];
|
||||
cache->first_hash = px;
|
||||
for (i=0; i<max_hash; i++)
|
||||
px[i] = (struct HASH_ENTRY*)NULL;
|
||||
} else {
|
||||
cache->free_hash = (struct HASH_ENTRY*)NULL;
|
||||
cache->first_hash = (struct HASH_ENTRY**)NULL;
|
||||
}
|
||||
}
|
||||
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",(cache_free)NULL,
|
||||
ntfs_dir_inode_hash, sizeof(struct CACHED_INODE),
|
||||
CACHE_INODE_SIZE, 2*CACHE_INODE_SIZE);
|
||||
#endif
|
||||
#if CACHE_NIDATA_SIZE
|
||||
/* idata cache */
|
||||
vol->nidata_cache = ntfs_create_cache("nidata",
|
||||
ntfs_inode_nidata_free, ntfs_inode_nidata_hash,
|
||||
sizeof(struct CACHED_NIDATA),
|
||||
CACHE_NIDATA_SIZE, 2*CACHE_NIDATA_SIZE);
|
||||
#endif
|
||||
#if CACHE_LOOKUP_SIZE
|
||||
/* lookup cache */
|
||||
vol->lookup_cache = ntfs_create_cache("lookup",
|
||||
(cache_free)NULL, ntfs_dir_lookup_hash,
|
||||
sizeof(struct CACHED_LOOKUP),
|
||||
CACHE_LOOKUP_SIZE, 2*CACHE_LOOKUP_SIZE);
|
||||
#endif
|
||||
vol->securid_cache = ntfs_create_cache("securid",(cache_free)NULL,
|
||||
(cache_hash)NULL,sizeof(struct CACHED_SECURID), CACHE_SECURID_SIZE, 0);
|
||||
#if CACHE_LEGACY_SIZE
|
||||
vol->legacy_cache = ntfs_create_cache("legacy",(cache_free)NULL,
|
||||
(cache_hash)NULL, sizeof(struct CACHED_PERMISSIONS_LEGACY), CACHE_LEGACY_SIZE, 0);
|
||||
#endif
|
||||
}
|
||||
|
||||
/*
|
||||
* Free all LRU caches
|
||||
*/
|
||||
|
||||
void ntfs_free_lru_caches(ntfs_volume *vol)
|
||||
{
|
||||
#if CACHE_INODE_SIZE
|
||||
ntfs_free_cache(vol->xinode_cache);
|
||||
#endif
|
||||
#if CACHE_NIDATA_SIZE
|
||||
ntfs_free_cache(vol->nidata_cache);
|
||||
#endif
|
||||
#if CACHE_LOOKUP_SIZE
|
||||
ntfs_free_cache(vol->lookup_cache);
|
||||
#endif
|
||||
ntfs_free_cache(vol->securid_cache);
|
||||
#if CACHE_LEGACY_SIZE
|
||||
ntfs_free_cache(vol->legacy_cache);
|
||||
#endif
|
||||
}
|
119
src/add-ons/kernel/file_systems/ntfs/libntfs/cache.h
Normal file
119
src/add-ons/kernel/file_systems/ntfs/libntfs/cache.h
Normal file
@ -0,0 +1,119 @@
|
||||
/*
|
||||
* cache.h : deal with indexed LRU caches
|
||||
*
|
||||
* 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
|
||||
*/
|
||||
|
||||
#ifndef _NTFS_CACHE_H_
|
||||
#define _NTFS_CACHE_H_
|
||||
|
||||
#include "volume.h"
|
||||
|
||||
struct CACHED_GENERIC {
|
||||
struct CACHED_GENERIC *next;
|
||||
struct CACHED_GENERIC *previous;
|
||||
void *variable;
|
||||
size_t varsize;
|
||||
union {
|
||||
/* force alignment for pointers and u64 */
|
||||
u64 u64align;
|
||||
void *ptralign;
|
||||
} fixed[0];
|
||||
} ;
|
||||
|
||||
struct CACHED_INODE {
|
||||
struct CACHED_INODE *next;
|
||||
struct CACHED_INODE *previous;
|
||||
const char *pathname;
|
||||
size_t varsize;
|
||||
/* above fields must match "struct CACHED_GENERIC" */
|
||||
u64 inum;
|
||||
} ;
|
||||
|
||||
struct CACHED_NIDATA {
|
||||
struct CACHED_NIDATA *next;
|
||||
struct CACHED_NIDATA *previous;
|
||||
const char *pathname; /* not used */
|
||||
size_t varsize; /* not used */
|
||||
/* above fields must match "struct CACHED_GENERIC" */
|
||||
u64 inum;
|
||||
ntfs_inode *ni;
|
||||
} ;
|
||||
|
||||
struct CACHED_LOOKUP {
|
||||
struct CACHED_LOOKUP *next;
|
||||
struct CACHED_LOOKUP *previous;
|
||||
const char *name;
|
||||
size_t namesize;
|
||||
/* above fields must match "struct CACHED_GENERIC" */
|
||||
u64 parent;
|
||||
u64 inum;
|
||||
} ;
|
||||
|
||||
enum {
|
||||
CACHE_FREE = 1,
|
||||
CACHE_NOHASH = 2
|
||||
} ;
|
||||
|
||||
typedef int (*cache_compare)(const struct CACHED_GENERIC *cached,
|
||||
const struct CACHED_GENERIC *item);
|
||||
typedef void (*cache_free)(const struct CACHED_GENERIC *cached);
|
||||
typedef int (*cache_hash)(const struct CACHED_GENERIC *cached);
|
||||
|
||||
struct HASH_ENTRY {
|
||||
struct HASH_ENTRY *next;
|
||||
struct CACHED_GENERIC *entry;
|
||||
} ;
|
||||
|
||||
struct CACHE_HEADER {
|
||||
const char *name;
|
||||
struct CACHED_GENERIC *most_recent_entry;
|
||||
struct CACHED_GENERIC *oldest_entry;
|
||||
struct CACHED_GENERIC *free_entry;
|
||||
struct HASH_ENTRY *free_hash;
|
||||
struct HASH_ENTRY **first_hash;
|
||||
cache_free dofree;
|
||||
cache_hash dohash;
|
||||
unsigned long reads;
|
||||
unsigned long writes;
|
||||
unsigned long hits;
|
||||
int fixed_size;
|
||||
int max_hash;
|
||||
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, int flags);
|
||||
int ntfs_remove_cache(struct CACHE_HEADER *cache,
|
||||
struct CACHED_GENERIC *item, int flags);
|
||||
|
||||
void ntfs_create_lru_caches(ntfs_volume *vol);
|
||||
void ntfs_free_lru_caches(ntfs_volume *vol);
|
||||
|
||||
#endif /* _NTFS_CACHE_H_ */
|
||||
|
@ -23,36 +23,23 @@
|
||||
#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 "index.h"
|
||||
#include "collate.h"
|
||||
#include "debug.h"
|
||||
#include "unistr.h"
|
||||
#include "logging.h"
|
||||
|
||||
BOOL ntfs_is_collation_rule_supported(COLLATION_RULES cr)
|
||||
{
|
||||
int i;
|
||||
|
||||
/*
|
||||
* FIXME: At the moment we only support COLLATION_BINARY,
|
||||
* COLLATION_NTOFS_ULONG and COLLATION_FILE_NAME so we return false
|
||||
* for everything else.
|
||||
*/
|
||||
if (cr != COLLATION_BINARY && cr != COLLATION_NTOFS_ULONG &&
|
||||
cr != COLLATION_FILE_NAME)
|
||||
return FALSE;
|
||||
i = le32_to_cpu(cr);
|
||||
if (((i >= 0) && (i <= 0x02)) ||
|
||||
((i >= 0x10) && (i <= 0x13)))
|
||||
return TRUE;
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/**
|
||||
* ntfs_collate_binary - Which of two binary objects should be listed first
|
||||
* @vol: unused
|
||||
@ -121,6 +108,101 @@ static int ntfs_collate_ntofs_ulong(ntfs_volume *vol __attribute__((unused)),
|
||||
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 le32 *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 le32*)data1;
|
||||
p2 = (const le32*)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:
|
||||
@ -137,85 +219,53 @@ 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)))
|
||||
{
|
||||
const FILE_NAME_ATTR *file_name_attr1;
|
||||
const FILE_NAME_ATTR *file_name_attr2;
|
||||
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);
|
||||
file_name_attr1 = (const FILE_NAME_ATTR*)data1;
|
||||
file_name_attr2 = (const FILE_NAME_ATTR*)data2;
|
||||
rc = ntfs_names_full_collate(
|
||||
(ntfschar*)&file_name_attr1->file_name,
|
||||
file_name_attr1->file_name_length,
|
||||
(ntfschar*)&file_name_attr2->file_name,
|
||||
file_name_attr2->file_name_length,
|
||||
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*/,
|
||||
NULL/*ntfs_collate_ntofs_security_hash*/,
|
||||
NULL/*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
|
||||
/*
|
||||
* Get a pointer to appropriate collation function.
|
||||
*
|
||||
* 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.
|
||||
* Returns NULL if the needed function is not implemented
|
||||
*/
|
||||
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;
|
||||
COLLATE ntfs_get_collate_function(COLLATION_RULES cr)
|
||||
{
|
||||
COLLATE collate;
|
||||
|
||||
switch (cr) {
|
||||
case COLLATION_BINARY :
|
||||
collate = ntfs_collate_binary;
|
||||
break;
|
||||
case COLLATION_FILE_NAME :
|
||||
collate = ntfs_collate_file_name;
|
||||
break;
|
||||
case COLLATION_NTOFS_SECURITY_HASH :
|
||||
collate = ntfs_collate_ntofs_security_hash;
|
||||
break;
|
||||
case COLLATION_NTOFS_ULONG :
|
||||
collate = ntfs_collate_ntofs_ulong;
|
||||
break;
|
||||
case COLLATION_NTOFS_ULONGS :
|
||||
collate = ntfs_collate_ntofs_ulongs;
|
||||
break;
|
||||
default :
|
||||
errno = EOPNOTSUPP;
|
||||
collate = (COLLATE)NULL;
|
||||
break;
|
||||
}
|
||||
/*
|
||||
* FIXME: At the moment we only support COLLATION_BINARY,
|
||||
* COLLATION_NTOFS_ULONG and COLLATION_FILE_NAME so we return error
|
||||
* for everything else.
|
||||
*/
|
||||
if (cr != COLLATION_BINARY && cr != COLLATION_NTOFS_ULONG &&
|
||||
cr != COLLATION_FILE_NAME)
|
||||
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;
|
||||
return (collate);
|
||||
}
|
||||
|
@ -29,9 +29,6 @@
|
||||
|
||||
#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);
|
||||
extern COLLATE ntfs_get_collate_function(COLLATION_RULES);
|
||||
|
||||
#endif /* _NTFS_COLLATE_H */
|
||||
|
@ -77,7 +77,7 @@ int ffs(int x)
|
||||
|
||||
#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.2 2008/07/17 15:01:48 szaka Exp $";
|
||||
static const char rcsid[] = "$Id: compat.c,v 1.1.1.1.2.1 2008-08-16 15:17:44 jpandre Exp $";
|
||||
#endif /* LIBC_SCCS and not lint */
|
||||
|
||||
/*
|
||||
@ -164,7 +164,7 @@ int daemon(int nochdir, int noclose) {
|
||||
|
||||
#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.2 2008/07/17 15:01:48 szaka Exp $";
|
||||
static const char rcsid[] = "$Id: compat.c,v 1.1.1.1.2.1 2008-08-16 15:17:44 jpandre Exp $";
|
||||
#endif /* LIBC_SCCS and not lint */
|
||||
|
||||
/*
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -29,5 +29,13 @@
|
||||
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,
|
||||
VCN *update_from);
|
||||
|
||||
extern int ntfs_compressed_close(ntfs_attr *na, runlist_element *brl,
|
||||
s64 offs, VCN *update_from);
|
||||
|
||||
#endif /* defined _NTFS_COMPRESS_H */
|
||||
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -67,18 +67,21 @@ 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 u64 ntfs_inode_lookup_by_mbsname(ntfs_inode *dir_ni, const char *name);
|
||||
extern void ntfs_inode_update_mbsname(ntfs_inode *dir_ni, const char *name,
|
||||
u64 inum);
|
||||
|
||||
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, ntfschar *name, u8 name_len,
|
||||
dev_t type);
|
||||
extern ntfs_inode *ntfs_create_device(ntfs_inode *dir_ni,
|
||||
ntfschar *name, u8 name_len, dev_t type, dev_t dev);
|
||||
extern ntfs_inode *ntfs_create_symlink(ntfs_inode *dir_ni,
|
||||
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_inode *ni, ntfs_inode *dir_ni, ntfschar *name,
|
||||
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,
|
||||
@ -110,5 +113,22 @@ typedef int (*ntfs_filldir_t)(void *dirent, const ntfschar *name,
|
||||
extern int ntfs_readdir(ntfs_inode *dir_ni, s64 *pos,
|
||||
void *dirent, ntfs_filldir_t filldir);
|
||||
|
||||
ntfs_inode *ntfs_dir_parent_inode(ntfs_inode *ni);
|
||||
|
||||
int ntfs_get_ntfs_dos_name(ntfs_inode *ni, ntfs_inode *dir_ni,
|
||||
char *value, size_t size);
|
||||
int ntfs_set_ntfs_dos_name(ntfs_inode *ni, ntfs_inode *dir_ni,
|
||||
const char *value, size_t size, int flags);
|
||||
int ntfs_remove_ntfs_dos_name(ntfs_inode *ni, ntfs_inode *dir_ni);
|
||||
|
||||
#if CACHE_INODE_SIZE
|
||||
|
||||
struct CACHED_GENERIC;
|
||||
|
||||
extern int ntfs_dir_inode_hash(const struct CACHED_GENERIC *cached);
|
||||
extern int ntfs_dir_lookup_hash(const struct CACHED_GENERIC *cached);
|
||||
|
||||
#endif
|
||||
|
||||
#endif /* defined _NTFS_DIR_H */
|
||||
|
||||
|
439
src/add-ons/kernel/file_systems/ntfs/libntfs/efs.c
Normal file
439
src/add-ons/kernel/file_systems/ntfs/libntfs/efs.c
Normal file
@ -0,0 +1,439 @@
|
||||
/**
|
||||
* efs.c - Limited processing of encrypted files
|
||||
*
|
||||
* This module is part of ntfs-3g library
|
||||
*
|
||||
* Copyright (c) 2009 Martin Bene
|
||||
* Copyright (c) 2009-2010 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"
|
||||
|
||||
#ifdef HAVE_SETXATTR /* extended attributes interface required */
|
||||
|
||||
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(ntfs_inode *ni, char *value, size_t size)
|
||||
{
|
||||
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_error("Bad efs_info for inode %lld\n",
|
||||
(long long)ni->mft_no);
|
||||
} else {
|
||||
ntfs_log_error("Could not get efsinfo"
|
||||
" for inode %lld\n",
|
||||
(long long)ni->mft_no);
|
||||
}
|
||||
errno = EIO;
|
||||
attr_size = 0;
|
||||
}
|
||||
} else {
|
||||
errno = ENODATA;
|
||||
ntfs_log_trace("Inode %lld is not encrypted\n",
|
||||
(long long)ni->mft_no);
|
||||
}
|
||||
}
|
||||
return (attr_size ? (int)attr_size : -errno);
|
||||
}
|
||||
|
||||
/*
|
||||
* Fix all encrypted AT_DATA attributes of an inode
|
||||
*
|
||||
* The fix may require making an attribute non resident, which
|
||||
* requires more space in the MFT record, and may cause some
|
||||
* attribute to be expelled and the full record to be reorganized.
|
||||
* When this happens, the search for data attributes has to be
|
||||
* reinitialized.
|
||||
*
|
||||
* Returns zero if successful.
|
||||
* -1 if there is a problem.
|
||||
*/
|
||||
|
||||
static int fixup_loop(ntfs_inode *ni)
|
||||
{
|
||||
ntfs_attr_search_ctx *ctx;
|
||||
ntfs_attr *na;
|
||||
ATTR_RECORD *a;
|
||||
BOOL restart;
|
||||
BOOL first;
|
||||
int cnt;
|
||||
int maxcnt;
|
||||
int res = 0;
|
||||
|
||||
maxcnt = 0;
|
||||
do {
|
||||
restart = FALSE;
|
||||
ctx = ntfs_attr_get_search_ctx(ni, NULL);
|
||||
if (!ctx) {
|
||||
ntfs_log_error("Failed to get ctx for efs\n");
|
||||
res = -1;
|
||||
}
|
||||
cnt = 0;
|
||||
while (!restart && !res
|
||||
&& !ntfs_attr_lookup(AT_DATA, NULL, 0,
|
||||
CASE_SENSITIVE, 0, NULL, 0, ctx)) {
|
||||
cnt++;
|
||||
a = ctx->attr;
|
||||
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");
|
||||
res = -1;
|
||||
}
|
||||
if (na && !(ctx->attr->flags & ATTR_IS_ENCRYPTED)) {
|
||||
if (!NAttrNonResident(na)
|
||||
&& ntfs_attr_make_non_resident(na, ctx)) {
|
||||
/*
|
||||
* ntfs_attr_make_non_resident fails if there
|
||||
* is not enough space in the MFT record.
|
||||
* When this happens, force making non-resident
|
||||
* so that some other attribute is expelled.
|
||||
*/
|
||||
if (ntfs_attr_force_non_resident(na)) {
|
||||
res = -1;
|
||||
} else {
|
||||
/* make sure there is some progress */
|
||||
if (cnt <= maxcnt) {
|
||||
errno = EIO;
|
||||
ntfs_log_error("Multiple failure"
|
||||
" making non resident\n");
|
||||
res = -1;
|
||||
} else {
|
||||
ntfs_attr_put_search_ctx(ctx);
|
||||
ctx = (ntfs_attr_search_ctx*)NULL;
|
||||
restart = TRUE;
|
||||
maxcnt = cnt;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!restart && !res
|
||||
&& ntfs_efs_fixup_attribute(ctx, na)) {
|
||||
ntfs_log_error("Error in efs fixup of AT_DATA Attribute\n");
|
||||
res = -1;
|
||||
}
|
||||
}
|
||||
if (na)
|
||||
ntfs_attr_close(na);
|
||||
}
|
||||
first = FALSE;
|
||||
} while (restart && !res);
|
||||
if (ctx)
|
||||
ntfs_attr_put_search_ctx(ctx);
|
||||
return (res);
|
||||
}
|
||||
|
||||
/*
|
||||
* 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(ntfs_inode *ni, const char *value, size_t size,
|
||||
int flags)
|
||||
|
||||
{
|
||||
int res;
|
||||
int written;
|
||||
ntfs_attr *na;
|
||||
const EFS_ATTR_HEADER *info_header;
|
||||
|
||||
res = 0;
|
||||
if (ni && value && size) {
|
||||
if (ni->flags & (FILE_ATTR_ENCRYPTED | FILE_ATTR_COMPRESSED)) {
|
||||
if (ni->flags & FILE_ATTR_ENCRYPTED) {
|
||||
ntfs_log_trace("Inode %lld already encrypted\n",
|
||||
(long long)ni->mft_no);
|
||||
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("Inode %lld cannot be encrypted and compressed\n",
|
||||
(long long)ni->mft_no);
|
||||
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 */
|
||||
|
||||
if (fixup_loop(ni))
|
||||
return -1;
|
||||
}
|
||||
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;
|
||||
u64 oldsize;
|
||||
le16 appended_bytes;
|
||||
u16 padding_length;
|
||||
ntfs_inode *ni;
|
||||
BOOL close_ctx = FALSE;
|
||||
|
||||
if (!na) {
|
||||
ntfs_log_error("no 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;
|
||||
}
|
||||
} else {
|
||||
if (!NAttrNonResident(na)) {
|
||||
ntfs_log_error("Cannot make non resident"
|
||||
" when a context has been allocated\n");
|
||||
goto err_out;
|
||||
}
|
||||
}
|
||||
|
||||
/* no extra bytes are added to void attributes */
|
||||
oldsize = na->data_size;
|
||||
if (oldsize) {
|
||||
/* make sure size is valid for a raw encrypted stream */
|
||||
if ((oldsize & 511) != 2) {
|
||||
ntfs_log_error("Bad raw encrypted stream\n");
|
||||
goto err_out;
|
||||
}
|
||||
/* read padding length from last two bytes of attribute */
|
||||
if (ntfs_attr_pread(na, oldsize - 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)oldsize);
|
||||
goto err_out;
|
||||
}
|
||||
newsize = oldsize - padding_length - 2;
|
||||
/*
|
||||
* truncate attribute to possibly free clusters allocated
|
||||
* for the last two bytes, but do not truncate to new size
|
||||
* to avoid losing useful data
|
||||
*/
|
||||
if (ntfs_attr_truncate(na, oldsize - 2)) {
|
||||
ntfs_log_error("Error truncating attribute\n");
|
||||
goto err_out;
|
||||
}
|
||||
} else
|
||||
newsize = 0;
|
||||
|
||||
/*
|
||||
* Encrypted AT_DATA Attributes MUST be non-resident
|
||||
* This has to be done after the attribute is resized, as
|
||||
* resizing down to zero may cause the attribute to be made
|
||||
* resident.
|
||||
*/
|
||||
if (!NAttrNonResident(na)
|
||||
&& ntfs_attr_make_non_resident(na, ctx)) {
|
||||
if (!close_ctx
|
||||
|| ntfs_attr_force_non_resident(na)) {
|
||||
ntfs_log_error("Error making DATA attribute non-resident\n");
|
||||
goto err_out;
|
||||
} else {
|
||||
/*
|
||||
* must reinitialize context after forcing
|
||||
* non-resident. We need a context for updating
|
||||
* the state, and at this point, we are sure
|
||||
* the context is not used elsewhere.
|
||||
*/
|
||||
ntfs_attr_reinit_search_ctx(ctx);
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
ni = na->ni;
|
||||
if (!na->name_len) {
|
||||
ni->data_size = newsize;
|
||||
ni->allocated_size = na->allocated_size;
|
||||
}
|
||||
NInoSetDirty(ni);
|
||||
NInoFileNameSetDirty(ni);
|
||||
|
||||
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_ctx && ctx)
|
||||
ntfs_attr_put_search_ctx(ctx);
|
||||
return (-1);
|
||||
}
|
||||
|
||||
#endif /* HAVE_SETXATTR */
|
30
src/add-ons/kernel/file_systems/ntfs/libntfs/efs.h
Normal file
30
src/add-ons/kernel/file_systems/ntfs/libntfs/efs.h
Normal file
@ -0,0 +1,30 @@
|
||||
/*
|
||||
*
|
||||
* 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(ntfs_inode *ni, char *value, size_t size);
|
||||
|
||||
int ntfs_set_efs_info(ntfs_inode *ni,
|
||||
const char *value, size_t size, int flags);
|
||||
int ntfs_efs_fixup_attribute(ntfs_attr_search_ctx *ctx, ntfs_attr *na);
|
||||
|
||||
#endif /* EFS_H */
|
@ -5,6 +5,7 @@
|
||||
* Copyright (c) 2004-2005 Richard Russon
|
||||
* Copyright (c) 2005-2006 Yura Pakhuchiy
|
||||
* Copyright (c) 2005-2008 Szabolcs Szakacsits
|
||||
* Copyright (c) 2007 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
|
||||
@ -37,13 +38,14 @@
|
||||
#endif
|
||||
|
||||
#include "attrib.h"
|
||||
#include "collate.h"
|
||||
#include "debug.h"
|
||||
#include "index.h"
|
||||
#include "collate.h"
|
||||
#include "mst.h"
|
||||
#include "dir.h"
|
||||
#include "logging.h"
|
||||
#include "bitmap.h"
|
||||
#include "reparse.h"
|
||||
#include "misc.h"
|
||||
|
||||
/**
|
||||
@ -515,8 +517,13 @@ static int ntfs_ie_lookup(const void *key, const int key_len,
|
||||
* Not a perfect match, need to do full blown collation so we
|
||||
* know which way in the B+tree we have to go.
|
||||
*/
|
||||
rc = ntfs_collate(icx->ni->vol, icx->cr, key, key_len, &ie->key,
|
||||
le16_to_cpu(ie->key_length));
|
||||
if (!icx->collate) {
|
||||
ntfs_log_error("Collation function not defined\n");
|
||||
errno = EOPNOTSUPP;
|
||||
return STATUS_ERROR;
|
||||
}
|
||||
rc = icx->collate(icx->ni->vol, key, key_len,
|
||||
&ie->key, le16_to_cpu(ie->key_length));
|
||||
if (rc == NTFS_COLLATION_ERROR) {
|
||||
ntfs_log_error("Collation error. Perhaps a filename "
|
||||
"contains invalid characters?\n");
|
||||
@ -556,7 +563,7 @@ static int ntfs_ie_lookup(const void *key, const int key_len,
|
||||
*vcn = ntfs_ie_get_vcn(ie);
|
||||
if (*vcn < 0) {
|
||||
errno = EINVAL;
|
||||
ntfs_log_perror("Negative vcn in inode %llu\n",
|
||||
ntfs_log_perror("Negative vcn in inode %llu",
|
||||
(unsigned long long)icx->ni->mft_no);
|
||||
return STATUS_ERROR;
|
||||
}
|
||||
@ -695,12 +702,12 @@ int ntfs_index_lookup(const void *key, const int key_len, ntfs_index_context *ic
|
||||
icx->vcn_size_bits = ni->vol->cluster_size_bits;
|
||||
else
|
||||
icx->vcn_size_bits = ni->vol->sector_size_bits;
|
||||
|
||||
icx->cr = ir->collation_rule;
|
||||
if (!ntfs_is_collation_rule_supported(icx->cr)) {
|
||||
/* get the appropriate collation function */
|
||||
icx->collate = ntfs_get_collate_function(ir->collation_rule);
|
||||
if (!icx->collate) {
|
||||
err = errno = EOPNOTSUPP;
|
||||
ntfs_log_perror("Unknown collation rule 0x%x",
|
||||
(unsigned)le32_to_cpu(icx->cr));
|
||||
(unsigned)le32_to_cpu(ir->collation_rule));
|
||||
goto err_out;
|
||||
}
|
||||
|
||||
@ -1418,18 +1425,20 @@ static int ntfs_ib_split(ntfs_index_context *icx, INDEX_BLOCK *ib)
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
static int ntfs_ie_add(ntfs_index_context *icx, INDEX_ENTRY *ie)
|
||||
/* JPA static */
|
||||
int ntfs_ie_add(ntfs_index_context *icx, INDEX_ENTRY *ie)
|
||||
{
|
||||
INDEX_HEADER *ih;
|
||||
int allocated_size, new_size;
|
||||
int ret = STATUS_ERROR;
|
||||
|
||||
#ifdef DEBUG
|
||||
/* removed by JPA to make function usable for security indexes
|
||||
char *fn;
|
||||
fn = ntfs_ie_filename_get(ie);
|
||||
ntfs_log_trace("file: '%s'\n", fn);
|
||||
ntfs_attr_name_free(&fn);
|
||||
*/
|
||||
#endif
|
||||
|
||||
while (1) {
|
||||
@ -1767,7 +1776,8 @@ out:
|
||||
*
|
||||
* Return 0 on success or -1 on error with errno set to the error code.
|
||||
*/
|
||||
static int ntfs_index_rm(ntfs_index_context *icx)
|
||||
/*static JPA*/
|
||||
int ntfs_index_rm(ntfs_index_context *icx)
|
||||
{
|
||||
INDEX_HEADER *ih;
|
||||
int err, ret = STATUS_OK;
|
||||
@ -1810,12 +1820,13 @@ err_out:
|
||||
goto out;
|
||||
}
|
||||
|
||||
int ntfs_index_remove(ntfs_inode *ni, const void *key, const int keylen)
|
||||
int ntfs_index_remove(ntfs_inode *dir_ni, ntfs_inode *ni,
|
||||
const void *key, const int keylen)
|
||||
{
|
||||
int ret = STATUS_ERROR;
|
||||
ntfs_index_context *icx;
|
||||
|
||||
icx = ntfs_index_ctx_get(ni, NTFS_INDEX_I30, 4);
|
||||
icx = ntfs_index_ctx_get(dir_ni, NTFS_INDEX_I30, 4);
|
||||
if (!icx)
|
||||
return -1;
|
||||
|
||||
@ -1824,8 +1835,9 @@ int ntfs_index_remove(ntfs_inode *ni, const void *key, const int keylen)
|
||||
if (ntfs_index_lookup(key, keylen, icx))
|
||||
goto err_out;
|
||||
|
||||
if (((FILE_NAME_ATTR *)icx->data)->file_attributes &
|
||||
FILE_ATTR_REPARSE_POINT) {
|
||||
if ((((FILE_NAME_ATTR *)icx->data)->file_attributes &
|
||||
FILE_ATTR_REPARSE_POINT)
|
||||
&& !ntfs_possible_symlink(ni)) {
|
||||
errno = EOPNOTSUPP;
|
||||
goto err_out;
|
||||
}
|
||||
@ -1885,3 +1897,167 @@ out:
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Walk down the index tree (leaf bound)
|
||||
* until there are no subnode in the first index entry
|
||||
* returns the entry at the bottom left in subnode
|
||||
*/
|
||||
|
||||
static INDEX_ENTRY *ntfs_index_walk_down(INDEX_ENTRY *ie,
|
||||
ntfs_index_context *ictx)
|
||||
{
|
||||
INDEX_ENTRY *entry;
|
||||
s64 vcn;
|
||||
|
||||
entry = ie;
|
||||
do {
|
||||
vcn = ntfs_ie_get_vcn(entry);
|
||||
if (ictx->is_in_root) {
|
||||
|
||||
/* down from level zero */
|
||||
|
||||
ictx->ir = (INDEX_ROOT*)NULL;
|
||||
ictx->ib = (INDEX_BLOCK*)ntfs_malloc(ictx->block_size);
|
||||
ictx->pindex = 1;
|
||||
ictx->is_in_root = FALSE;
|
||||
} else {
|
||||
|
||||
/* down from non-zero level */
|
||||
|
||||
ictx->pindex++;
|
||||
}
|
||||
ictx->parent_pos[ictx->pindex] = 0;
|
||||
ictx->parent_vcn[ictx->pindex] = vcn;
|
||||
if (!ntfs_ib_read(ictx,vcn,ictx->ib)) {
|
||||
ictx->entry = ntfs_ie_get_first(&ictx->ib->index);
|
||||
entry = ictx->entry;
|
||||
} else
|
||||
entry = (INDEX_ENTRY*)NULL;
|
||||
} while (entry && (entry->ie_flags & INDEX_ENTRY_NODE));
|
||||
return (entry);
|
||||
}
|
||||
|
||||
/*
|
||||
* Walk up the index tree (root bound)
|
||||
* until there is a valid data entry in parent
|
||||
* returns the parent entry or NULL if no more parent
|
||||
*/
|
||||
|
||||
static INDEX_ENTRY *ntfs_index_walk_up(INDEX_ENTRY *ie,
|
||||
ntfs_index_context *ictx)
|
||||
{
|
||||
INDEX_ENTRY *entry;
|
||||
s64 vcn;
|
||||
|
||||
entry = ie;
|
||||
if (ictx->pindex > 0) {
|
||||
do {
|
||||
ictx->pindex--;
|
||||
if (!ictx->pindex) {
|
||||
|
||||
/* we have reached the root */
|
||||
|
||||
free(ictx->ib);
|
||||
ictx->ib = (INDEX_BLOCK*)NULL;
|
||||
ictx->is_in_root = TRUE;
|
||||
/* a new search context is to be allocated */
|
||||
if (ictx->actx)
|
||||
free(ictx->actx);
|
||||
ictx->ir = ntfs_ir_lookup(ictx->ni,
|
||||
ictx->name, ictx->name_len,
|
||||
&ictx->actx);
|
||||
if (ictx->ir)
|
||||
entry = ntfs_ie_get_by_pos(
|
||||
&ictx->ir->index,
|
||||
ictx->parent_pos[ictx->pindex]);
|
||||
else
|
||||
entry = (INDEX_ENTRY*)NULL;
|
||||
} else {
|
||||
/* up into non-root node */
|
||||
vcn = ictx->parent_vcn[ictx->pindex];
|
||||
if (!ntfs_ib_read(ictx,vcn,ictx->ib)) {
|
||||
entry = ntfs_ie_get_by_pos(
|
||||
&ictx->ib->index,
|
||||
ictx->parent_pos[ictx->pindex]);
|
||||
} else
|
||||
entry = (INDEX_ENTRY*)NULL;
|
||||
}
|
||||
ictx->entry = entry;
|
||||
} while (entry && (ictx->pindex > 0)
|
||||
&& (entry->ie_flags & INDEX_ENTRY_END));
|
||||
} else
|
||||
entry = (INDEX_ENTRY*)NULL;
|
||||
return (entry);
|
||||
}
|
||||
|
||||
/*
|
||||
* Get next entry in an index according to collating sequence.
|
||||
* Must be initialized through a ntfs_index_lookup()
|
||||
*
|
||||
* Returns next entry or NULL if none
|
||||
*
|
||||
* Sample layout :
|
||||
*
|
||||
* +---+---+---+---+---+---+---+---+ n ptrs to subnodes
|
||||
* | | | 10| 25| 33| | | | n-1 keys in between
|
||||
* +---+---+---+---+---+---+---+---+ no key in last entry
|
||||
* | A | A
|
||||
* | | | +-------------------------------+
|
||||
* +--------------------------+ | +-----+ |
|
||||
* | +--+ | |
|
||||
* V | V |
|
||||
* +---+---+---+---+---+---+---+---+ | +---+---+---+---+---+---+---+---+
|
||||
* | 11| 12| 13| 14| 15| 16| 17| | | | 26| 27| 28| 29| 30| 31| 32| |
|
||||
* +---+---+---+---+---+---+---+---+ | +---+---+---+---+---+---+---+---+
|
||||
* | |
|
||||
* +-----------------------+ |
|
||||
* | |
|
||||
* +---+---+---+---+---+---+---+---+
|
||||
* | 18| 19| 20| 21| 22| 23| 24| |
|
||||
* +---+---+---+---+---+---+---+---+
|
||||
*/
|
||||
|
||||
INDEX_ENTRY *ntfs_index_next(INDEX_ENTRY *ie, ntfs_index_context *ictx)
|
||||
{
|
||||
INDEX_ENTRY *next;
|
||||
int flags;
|
||||
|
||||
/*
|
||||
* lookup() may have returned an invalid node
|
||||
* when searching for a partial key
|
||||
* if this happens, walk up
|
||||
*/
|
||||
|
||||
if (ie->ie_flags & INDEX_ENTRY_END)
|
||||
next = ntfs_index_walk_up(ie, ictx);
|
||||
else {
|
||||
/*
|
||||
* get next entry in same node
|
||||
* there is always one after any entry with data
|
||||
*/
|
||||
|
||||
next = (INDEX_ENTRY*)((char*)ie + le16_to_cpu(ie->length));
|
||||
++ictx->parent_pos[ictx->pindex];
|
||||
flags = next->ie_flags;
|
||||
|
||||
/* walk down if it has a subnode */
|
||||
|
||||
if (flags & INDEX_ENTRY_NODE) {
|
||||
next = ntfs_index_walk_down(next,ictx);
|
||||
} else {
|
||||
|
||||
/* walk up it has no subnode, nor data */
|
||||
|
||||
if (flags & INDEX_ENTRY_END) {
|
||||
next = ntfs_index_walk_up(next, ictx);
|
||||
}
|
||||
}
|
||||
}
|
||||
/* return NULL if stuck at end of a block */
|
||||
|
||||
if (next && (next->ie_flags & INDEX_ENTRY_END))
|
||||
next = (INDEX_ENTRY*)NULL;
|
||||
return (next);
|
||||
}
|
||||
|
||||
|
||||
|
@ -25,6 +25,34 @@
|
||||
#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"
|
||||
@ -35,6 +63,9 @@
|
||||
|
||||
#define MAX_PARENT_VCN 32
|
||||
|
||||
typedef int (*COLLATE)(ntfs_volume *vol, const void *data1, int len1,
|
||||
const void *data2, int len2);
|
||||
|
||||
/**
|
||||
* struct ntfs_index_context -
|
||||
* @ni: inode containing the @entry described by this context
|
||||
@ -88,7 +119,7 @@ typedef struct {
|
||||
INDEX_ENTRY *entry;
|
||||
void *data;
|
||||
u16 data_len;
|
||||
COLLATION_RULES cr;
|
||||
COLLATE collate;
|
||||
BOOL is_in_root;
|
||||
INDEX_ROOT *ir;
|
||||
ntfs_attr_search_ctx *actx;
|
||||
@ -108,11 +139,15 @@ 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 *ni, const void *key, const int keylen);
|
||||
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);
|
||||
|
||||
@ -124,5 +159,9 @@ 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 */
|
||||
|
||||
|
@ -5,6 +5,7 @@
|
||||
* Copyright (c) 2002-2008 Szabolcs Szakacsits
|
||||
* Copyright (c) 2004-2007 Yura Pakhuchiy
|
||||
* Copyright (c) 2004-2005 Richard Russon
|
||||
* Copyright (c) 2009-2010 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
|
||||
@ -35,11 +36,17 @@
|
||||
#ifdef HAVE_ERRNO_H
|
||||
#include <errno.h>
|
||||
#endif
|
||||
#ifdef HAVE_SETXATTR
|
||||
#include <sys/xattr.h>
|
||||
#endif
|
||||
|
||||
#include "param.h"
|
||||
#include "compat.h"
|
||||
#include "types.h"
|
||||
#include "attrib.h"
|
||||
#include "volume.h"
|
||||
#include "cache.h"
|
||||
#include "inode.h"
|
||||
#include "attrib.h"
|
||||
#include "debug.h"
|
||||
#include "mft.h"
|
||||
#include "attrlist.h"
|
||||
@ -149,12 +156,14 @@ static void __ntfs_inode_release(ntfs_inode *ni)
|
||||
* Return a pointer to the ntfs_inode structure on success or NULL on error,
|
||||
* with errno set to the error code.
|
||||
*/
|
||||
ntfs_inode *ntfs_inode_open(ntfs_volume *vol, const MFT_REF mref)
|
||||
static ntfs_inode *ntfs_inode_real_open(ntfs_volume *vol, const MFT_REF mref)
|
||||
{
|
||||
s64 l;
|
||||
ntfs_inode *ni = NULL;
|
||||
ntfs_attr_search_ctx *ctx;
|
||||
STANDARD_INFORMATION *std_info;
|
||||
le32 lthle;
|
||||
int olderrno;
|
||||
|
||||
ntfs_log_enter("Entering for inode %lld\n", (long long)MREF(mref));
|
||||
if (!vol) {
|
||||
@ -177,22 +186,41 @@ ntfs_inode *ntfs_inode_open(ntfs_volume *vol, const MFT_REF mref)
|
||||
/* Receive some basic information about inode. */
|
||||
if (ntfs_attr_lookup(AT_STANDARD_INFORMATION, AT_UNNAMED,
|
||||
0, CASE_SENSITIVE, 0, NULL, 0, ctx)) {
|
||||
ntfs_log_perror("No STANDARD_INFORMATION in base record\n");
|
||||
if (!ni->mrec->base_mft_record)
|
||||
ntfs_log_perror("No STANDARD_INFORMATION in base record"
|
||||
" %lld", (long long)MREF(mref));
|
||||
goto put_err_out;
|
||||
}
|
||||
std_info = (STANDARD_INFORMATION *)((u8 *)ctx->attr +
|
||||
le16_to_cpu(ctx->attr->value_offset));
|
||||
ni->flags = std_info->file_attributes;
|
||||
ni->creation_time = ntfs2utc(std_info->creation_time);
|
||||
ni->last_data_change_time = ntfs2utc(std_info->last_data_change_time);
|
||||
ni->last_mft_change_time = ntfs2utc(std_info->last_mft_change_time);
|
||||
ni->last_access_time = ntfs2utc(std_info->last_access_time);
|
||||
ni->creation_time = std_info->creation_time;
|
||||
ni->last_data_change_time = std_info->last_data_change_time;
|
||||
ni->last_mft_change_time = std_info->last_mft_change_time;
|
||||
ni->last_access_time = std_info->last_access_time;
|
||||
/* JPA insert v3 extensions if present */
|
||||
/* length may be seen as 72 (v1.x) or 96 (v3.x) */
|
||||
lthle = ctx->attr->length;
|
||||
if (le32_to_cpu(lthle) > sizeof(STANDARD_INFORMATION)) {
|
||||
set_nino_flag(ni, v3_Extensions);
|
||||
ni->owner_id = std_info->owner_id;
|
||||
ni->security_id = std_info->security_id;
|
||||
ni->quota_charged = std_info->quota_charged;
|
||||
ni->usn = std_info->usn;
|
||||
} else {
|
||||
clear_nino_flag(ni, v3_Extensions);
|
||||
ni->owner_id = const_cpu_to_le32(0);
|
||||
ni->security_id = const_cpu_to_le32(0);
|
||||
}
|
||||
/* Set attribute list information. */
|
||||
if (ntfs_attr_lookup(AT_ATTRIBUTE_LIST, AT_UNNAMED, 0, 0, 0, NULL, 0,
|
||||
ctx)) {
|
||||
olderrno = errno;
|
||||
if (ntfs_attr_lookup(AT_ATTRIBUTE_LIST, AT_UNNAMED, 0,
|
||||
CASE_SENSITIVE, 0, NULL, 0, ctx)) {
|
||||
if (errno != ENOENT)
|
||||
goto put_err_out;
|
||||
/* Attribute list attribute does not present. */
|
||||
/* restore previous errno to avoid misinterpretation */
|
||||
errno = olderrno;
|
||||
goto get_size;
|
||||
}
|
||||
NInoSetAttrList(ni);
|
||||
@ -201,7 +229,8 @@ ntfs_inode *ntfs_inode_open(ntfs_volume *vol, const MFT_REF mref)
|
||||
goto put_err_out;
|
||||
if (l > 0x40000) {
|
||||
errno = EIO;
|
||||
ntfs_log_perror("Too large attrlist (%lld)\n", (long long)l);
|
||||
ntfs_log_perror("Too large attrlist attribute (%lld), inode "
|
||||
"%lld", (long long)l, (long long)MREF(mref));
|
||||
goto put_err_out;
|
||||
}
|
||||
ni->attr_list_size = l;
|
||||
@ -213,15 +242,19 @@ ntfs_inode *ntfs_inode_open(ntfs_volume *vol, const MFT_REF mref)
|
||||
goto put_err_out;
|
||||
if (l != ni->attr_list_size) {
|
||||
errno = EIO;
|
||||
ntfs_log_perror("Unexpected attrlist size (%lld <> %u)\n",
|
||||
(long long)l, ni->attr_list_size);
|
||||
ntfs_log_perror("Unexpected attrlist size (%lld <> %u), inode "
|
||||
"%lld", (long long)l, ni->attr_list_size,
|
||||
(long long)MREF(mref));
|
||||
goto put_err_out;
|
||||
}
|
||||
get_size:
|
||||
olderrno = errno;
|
||||
if (ntfs_attr_lookup(AT_DATA, AT_UNNAMED, 0, 0, 0, NULL, 0, ctx)) {
|
||||
if (errno != ENOENT)
|
||||
goto put_err_out;
|
||||
/* Directory or special file. */
|
||||
/* restore previous errno to avoid misinterpretation */
|
||||
errno = olderrno;
|
||||
ni->data_size = ni->allocated_size = 0;
|
||||
} else {
|
||||
if (ctx->attr->non_resident) {
|
||||
@ -237,6 +270,7 @@ get_size:
|
||||
ni->data_size = le32_to_cpu(ctx->attr->value_length);
|
||||
ni->allocated_size = (ni->data_size + 7) & ~7;
|
||||
}
|
||||
set_nino_flag(ni,KnownSize);
|
||||
}
|
||||
ntfs_attr_put_search_ctx(ctx);
|
||||
out:
|
||||
@ -275,7 +309,8 @@ err_out:
|
||||
* EINVAL @ni is invalid (probably it is an extent inode).
|
||||
* EIO I/O error while trying to write inode to disk.
|
||||
*/
|
||||
int ntfs_inode_close(ntfs_inode *ni)
|
||||
|
||||
int ntfs_inode_real_close(ntfs_inode *ni)
|
||||
{
|
||||
int ret = -1;
|
||||
|
||||
@ -295,7 +330,7 @@ int ntfs_inode_close(ntfs_inode *ni)
|
||||
/* Is this a base inode with mapped extent inodes? */
|
||||
if (ni->nr_extents > 0) {
|
||||
while (ni->nr_extents > 0) {
|
||||
if (ntfs_inode_close(ni->extent_nis[0])) {
|
||||
if (ntfs_inode_real_close(ni->extent_nis[0])) {
|
||||
if (errno != EIO)
|
||||
errno = EBUSY;
|
||||
goto err;
|
||||
@ -335,8 +370,10 @@ int ntfs_inode_close(ntfs_inode *ni)
|
||||
/* Ignore errors, they don't really matter. */
|
||||
if (tmp_nis)
|
||||
base_ni->extent_nis = tmp_nis;
|
||||
} else if (tmp_nis)
|
||||
} else if (tmp_nis) {
|
||||
free(tmp_nis);
|
||||
base_ni->extent_nis = (ntfs_inode**)NULL;
|
||||
}
|
||||
/* Allow for error checking. */
|
||||
i = -1;
|
||||
break;
|
||||
@ -358,6 +395,154 @@ err:
|
||||
return ret;
|
||||
}
|
||||
|
||||
#if CACHE_NIDATA_SIZE
|
||||
|
||||
/*
|
||||
* Free an inode structure when there is not more space
|
||||
* in the cache
|
||||
*/
|
||||
|
||||
void ntfs_inode_nidata_free(const struct CACHED_GENERIC *cached)
|
||||
{
|
||||
ntfs_inode_real_close(((const struct CACHED_NIDATA*)cached)->ni);
|
||||
}
|
||||
|
||||
/*
|
||||
* Compute a hash value for an inode entry
|
||||
*/
|
||||
|
||||
int ntfs_inode_nidata_hash(const struct CACHED_GENERIC *item)
|
||||
{
|
||||
return (((const struct CACHED_NIDATA*)item)->inum
|
||||
% (2*CACHE_NIDATA_SIZE));
|
||||
}
|
||||
|
||||
/*
|
||||
* inum comparing for entering/fetching from cache
|
||||
*/
|
||||
|
||||
static int idata_cache_compare(const struct CACHED_GENERIC *cached,
|
||||
const struct CACHED_GENERIC *wanted)
|
||||
{
|
||||
return (((const struct CACHED_NIDATA*)cached)->inum
|
||||
!= ((const struct CACHED_NIDATA*)wanted)->inum);
|
||||
}
|
||||
|
||||
/*
|
||||
* Invalidate an inode entry when not needed anymore.
|
||||
* The entry should have been synced, it may be reused later,
|
||||
* if it is requested before it is dropped from cache.
|
||||
*/
|
||||
|
||||
void ntfs_inode_invalidate(ntfs_volume *vol, const MFT_REF mref)
|
||||
{
|
||||
struct CACHED_NIDATA item;
|
||||
int count;
|
||||
|
||||
item.inum = MREF(mref);
|
||||
item.ni = (ntfs_inode*)NULL;
|
||||
item.pathname = (const char*)NULL;
|
||||
item.varsize = 0;
|
||||
count = ntfs_invalidate_cache(vol->nidata_cache,
|
||||
GENERIC(&item),idata_cache_compare,CACHE_FREE);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Open an inode
|
||||
*
|
||||
* When possible, an entry recorded in the cache is reused
|
||||
*
|
||||
* **NEVER REOPEN** an inode, this can lead to a duplicated
|
||||
* cache entry (hard to detect), and to an obsolete one being
|
||||
* reused. System files are however protected from being cached.
|
||||
*/
|
||||
|
||||
ntfs_inode *ntfs_inode_open(ntfs_volume *vol, const MFT_REF mref)
|
||||
{
|
||||
ntfs_inode *ni;
|
||||
#if CACHE_NIDATA_SIZE
|
||||
struct CACHED_NIDATA item;
|
||||
struct CACHED_NIDATA *cached;
|
||||
|
||||
/* fetch idata from cache */
|
||||
item.inum = MREF(mref);
|
||||
debug_double_inode(item.inum,1);
|
||||
item.pathname = (const char*)NULL;
|
||||
item.varsize = 0;
|
||||
cached = (struct CACHED_NIDATA*)ntfs_fetch_cache(vol->nidata_cache,
|
||||
GENERIC(&item),idata_cache_compare);
|
||||
if (cached) {
|
||||
ni = cached->ni;
|
||||
/* do not keep open entries in cache */
|
||||
ntfs_remove_cache(vol->nidata_cache,
|
||||
(struct CACHED_GENERIC*)cached,0);
|
||||
} else {
|
||||
ni = ntfs_inode_real_open(vol, mref);
|
||||
}
|
||||
#else
|
||||
ni = ntfs_inode_real_open(vol, mref);
|
||||
#endif
|
||||
return (ni);
|
||||
}
|
||||
|
||||
/*
|
||||
* Close an inode entry
|
||||
*
|
||||
* If cacheing is in use, the entry is synced and kept available
|
||||
* in cache for further use.
|
||||
*
|
||||
* System files (inode < 16 or having the IS_4 flag) are protected
|
||||
* against being cached.
|
||||
*/
|
||||
|
||||
int ntfs_inode_close(ntfs_inode *ni)
|
||||
{
|
||||
int res;
|
||||
#if CACHE_NIDATA_SIZE
|
||||
BOOL dirty;
|
||||
struct CACHED_NIDATA item;
|
||||
|
||||
if (ni) {
|
||||
debug_double_inode(ni->mft_no,0);
|
||||
/* do not cache system files : could lead to double entries */
|
||||
if (ni->vol && ni->vol->nidata_cache
|
||||
&& ((ni->mft_no == FILE_root)
|
||||
|| ((ni->mft_no >= FILE_first_user)
|
||||
&& !(ni->mrec->flags & MFT_RECORD_IS_4)))) {
|
||||
/* If we have dirty metadata, write it out. */
|
||||
dirty = NInoDirty(ni) || NInoAttrListDirty(ni);
|
||||
if (dirty) {
|
||||
res = ntfs_inode_sync(ni);
|
||||
/* do a real close if sync failed */
|
||||
if (res)
|
||||
ntfs_inode_real_close(ni);
|
||||
} else
|
||||
res = 0;
|
||||
|
||||
if (!res) {
|
||||
/* feed idata into cache */
|
||||
item.inum = ni->mft_no;
|
||||
item.ni = ni;
|
||||
item.pathname = (const char*)NULL;
|
||||
item.varsize = 0;
|
||||
debug_cached_inode(ni);
|
||||
ntfs_enter_cache(ni->vol->nidata_cache,
|
||||
GENERIC(&item), idata_cache_compare);
|
||||
}
|
||||
} else {
|
||||
/* cache not ready or system file, really close */
|
||||
res = ntfs_inode_real_close(ni);
|
||||
}
|
||||
} else
|
||||
res = 0;
|
||||
#else
|
||||
res = ntfs_inode_real_close(ni);
|
||||
#endif
|
||||
return (res);
|
||||
}
|
||||
|
||||
/**
|
||||
* ntfs_extent_inode_open - load an extent inode and attach it to its base
|
||||
* @base_ni: base ntfs inode
|
||||
@ -514,6 +699,8 @@ static int ntfs_inode_sync_standard_information(ntfs_inode *ni)
|
||||
{
|
||||
ntfs_attr_search_ctx *ctx;
|
||||
STANDARD_INFORMATION *std_info;
|
||||
u32 lth;
|
||||
le32 lthle;
|
||||
|
||||
ntfs_log_trace("Entering for inode %lld\n", (long long)ni->mft_no);
|
||||
|
||||
@ -530,10 +717,27 @@ static int ntfs_inode_sync_standard_information(ntfs_inode *ni)
|
||||
std_info = (STANDARD_INFORMATION *)((u8 *)ctx->attr +
|
||||
le16_to_cpu(ctx->attr->value_offset));
|
||||
std_info->file_attributes = ni->flags;
|
||||
std_info->creation_time = utc2ntfs(ni->creation_time);
|
||||
std_info->last_data_change_time = utc2ntfs(ni->last_data_change_time);
|
||||
std_info->last_mft_change_time = utc2ntfs(ni->last_mft_change_time);
|
||||
std_info->last_access_time = utc2ntfs(ni->last_access_time);
|
||||
if (!test_nino_flag(ni, TimesSet)) {
|
||||
std_info->creation_time = ni->creation_time;
|
||||
std_info->last_data_change_time = ni->last_data_change_time;
|
||||
std_info->last_mft_change_time = ni->last_mft_change_time;
|
||||
std_info->last_access_time = ni->last_access_time;
|
||||
}
|
||||
|
||||
/* JPA update v3.x extensions, ensuring consistency */
|
||||
|
||||
lthle = ctx->attr->length;
|
||||
lth = le32_to_cpu(lthle);
|
||||
if (test_nino_flag(ni, v3_Extensions)
|
||||
&& (lth <= sizeof(STANDARD_INFORMATION)))
|
||||
ntfs_log_error("bad sync of standard information\n");
|
||||
|
||||
if (lth > sizeof(STANDARD_INFORMATION)) {
|
||||
std_info->owner_id = ni->owner_id;
|
||||
std_info->security_id = ni->security_id;
|
||||
std_info->quota_charged = ni->quota_charged;
|
||||
std_info->usn = ni->usn;
|
||||
}
|
||||
ntfs_inode_mark_dirty(ctx->ntfs_ino);
|
||||
ntfs_attr_put_search_ctx(ctx);
|
||||
return 0;
|
||||
@ -547,12 +751,15 @@ static int ntfs_inode_sync_standard_information(ntfs_inode *ni)
|
||||
*
|
||||
* Return 0 on success or -1 on error with errno set to the error code.
|
||||
*/
|
||||
static int ntfs_inode_sync_file_name(ntfs_inode *ni)
|
||||
static int ntfs_inode_sync_file_name(ntfs_inode *ni, ntfs_inode *dir_ni)
|
||||
{
|
||||
ntfs_attr_search_ctx *ctx = NULL;
|
||||
ntfs_index_context *ictx;
|
||||
ntfs_inode *index_ni;
|
||||
FILE_NAME_ATTR *fn;
|
||||
FILE_NAME_ATTR *fnx;
|
||||
REPARSE_POINT *rpp;
|
||||
le32 reparse_tag;
|
||||
int err = 0;
|
||||
|
||||
ntfs_log_trace("Entering for inode %lld\n", (long long)ni->mft_no);
|
||||
@ -562,6 +769,17 @@ static int ntfs_inode_sync_file_name(ntfs_inode *ni)
|
||||
err = errno;
|
||||
goto err_out;
|
||||
}
|
||||
/* Collect the reparse tag, if any */
|
||||
reparse_tag = cpu_to_le32(0);
|
||||
if (ni->flags & FILE_ATTR_REPARSE_POINT) {
|
||||
if (!ntfs_attr_lookup(AT_REPARSE_POINT, NULL,
|
||||
0, CASE_SENSITIVE, 0, NULL, 0, ctx)) {
|
||||
rpp = (REPARSE_POINT*)((u8 *)ctx->attr +
|
||||
le16_to_cpu(ctx->attr->value_offset));
|
||||
reparse_tag = rpp->reparse_tag;
|
||||
}
|
||||
ntfs_attr_reinit_search_ctx(ctx);
|
||||
}
|
||||
/* Walk through all FILE_NAME attributes and update them. */
|
||||
while (!ntfs_attr_lookup(AT_FILE_NAME, NULL, 0, 0, 0, NULL, 0, ctx)) {
|
||||
fn = (FILE_NAME_ATTR *)((u8 *)ctx->attr +
|
||||
@ -576,7 +794,10 @@ static int ntfs_inode_sync_file_name(ntfs_inode *ni)
|
||||
*/
|
||||
index_ni = ni;
|
||||
} else
|
||||
index_ni = ntfs_inode_open(ni->vol,
|
||||
if (dir_ni)
|
||||
index_ni = dir_ni;
|
||||
else
|
||||
index_ni = ntfs_inode_open(ni->vol,
|
||||
le64_to_cpu(fn->parent_directory));
|
||||
if (!index_ni) {
|
||||
if (!err)
|
||||
@ -591,7 +812,8 @@ static int ntfs_inode_sync_file_name(ntfs_inode *ni)
|
||||
err = errno;
|
||||
ntfs_log_perror("Failed to get index ctx, inode %lld",
|
||||
(long long)index_ni->mft_no);
|
||||
if (ni != index_ni && ntfs_inode_close(index_ni) && !err)
|
||||
if ((ni != index_ni) && !dir_ni
|
||||
&& ntfs_inode_close(index_ni) && !err)
|
||||
err = errno;
|
||||
continue;
|
||||
}
|
||||
@ -610,19 +832,34 @@ static int ntfs_inode_sync_file_name(ntfs_inode *ni)
|
||||
continue;
|
||||
}
|
||||
/* Update flags and file size. */
|
||||
fn = (FILE_NAME_ATTR *)ictx->data;
|
||||
fn->file_attributes =
|
||||
(fn->file_attributes & ~FILE_ATTR_VALID_FLAGS) |
|
||||
fnx = (FILE_NAME_ATTR *)ictx->data;
|
||||
fnx->file_attributes =
|
||||
(fnx->file_attributes & ~FILE_ATTR_VALID_FLAGS) |
|
||||
(ni->flags & FILE_ATTR_VALID_FLAGS);
|
||||
fn->allocated_size = cpu_to_sle64(ni->allocated_size);
|
||||
fn->data_size = cpu_to_sle64(ni->data_size);
|
||||
fn->creation_time = utc2ntfs(ni->creation_time);
|
||||
fn->last_data_change_time = utc2ntfs(ni->last_data_change_time);
|
||||
fn->last_mft_change_time = utc2ntfs(ni->last_mft_change_time);
|
||||
fn->last_access_time = utc2ntfs(ni->last_access_time);
|
||||
if (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY)
|
||||
fnx->data_size = fnx->allocated_size
|
||||
= const_cpu_to_le64(0);
|
||||
else {
|
||||
fnx->allocated_size = cpu_to_sle64(ni->allocated_size);
|
||||
fnx->data_size = cpu_to_sle64(ni->data_size);
|
||||
}
|
||||
/* update or clear the reparse tag in the index */
|
||||
fnx->reparse_point_tag = reparse_tag;
|
||||
if (!test_nino_flag(ni, TimesSet)) {
|
||||
fnx->creation_time = ni->creation_time;
|
||||
fnx->last_data_change_time = ni->last_data_change_time;
|
||||
fnx->last_mft_change_time = ni->last_mft_change_time;
|
||||
fnx->last_access_time = ni->last_access_time;
|
||||
} else {
|
||||
fnx->creation_time = fn->creation_time;
|
||||
fnx->last_data_change_time = fn->last_data_change_time;
|
||||
fnx->last_mft_change_time = fn->last_mft_change_time;
|
||||
fnx->last_access_time = fn->last_access_time;
|
||||
}
|
||||
ntfs_index_entry_mark_dirty(ictx);
|
||||
ntfs_index_ctx_put(ictx);
|
||||
if ((ni != index_ni) && ntfs_inode_close(index_ni) && !err)
|
||||
if ((ni != index_ni) && !dir_ni
|
||||
&& ntfs_inode_close(index_ni) && !err)
|
||||
err = errno;
|
||||
}
|
||||
/* Check for real error occurred. */
|
||||
@ -664,11 +901,10 @@ err_out:
|
||||
* EBUSY - Inode and/or one of its extents is busy, try again later.
|
||||
* EIO - I/O error while writing the inode (or one of its extents).
|
||||
*/
|
||||
int ntfs_inode_sync(ntfs_inode *ni)
|
||||
static int ntfs_inode_sync_in_dir(ntfs_inode *ni, ntfs_inode *dir_ni)
|
||||
{
|
||||
int ret = 0;
|
||||
int err = 0;
|
||||
|
||||
if (!ni) {
|
||||
errno = EINVAL;
|
||||
ntfs_log_error("Failed to sync NULL inode\n");
|
||||
@ -690,7 +926,7 @@ int ntfs_inode_sync(ntfs_inode *ni)
|
||||
/* Update FILE_NAME's in the index. */
|
||||
if ((ni->mrec->flags & MFT_RECORD_IN_USE) && ni->nr_extents != -1 &&
|
||||
NInoFileNameTestAndClearDirty(ni) &&
|
||||
ntfs_inode_sync_file_name(ni)) {
|
||||
ntfs_inode_sync_file_name(ni, dir_ni)) {
|
||||
if (!err || errno == EIO) {
|
||||
err = errno;
|
||||
if (err != EIO)
|
||||
@ -793,6 +1029,28 @@ sync_inode:
|
||||
return ret;
|
||||
}
|
||||
|
||||
int ntfs_inode_sync(ntfs_inode *ni)
|
||||
{
|
||||
return (ntfs_inode_sync_in_dir(ni, (ntfs_inode*)NULL));
|
||||
}
|
||||
|
||||
/*
|
||||
* Close an inode with an open parent inode
|
||||
*/
|
||||
|
||||
int ntfs_inode_close_in_dir(ntfs_inode *ni, ntfs_inode *dir_ni)
|
||||
{
|
||||
int res;
|
||||
|
||||
res = ntfs_inode_sync_in_dir(ni, dir_ni);
|
||||
if (res) {
|
||||
if (errno != EIO)
|
||||
errno = EBUSY;
|
||||
} else
|
||||
res = ntfs_inode_close(ni);
|
||||
return (res);
|
||||
}
|
||||
|
||||
/**
|
||||
* ntfs_inode_add_attrlist - add attribute list to inode and fill it
|
||||
* @ni: opened ntfs inode to which add attribute list
|
||||
@ -1079,7 +1337,7 @@ put_err_out:
|
||||
*/
|
||||
void ntfs_inode_update_times(ntfs_inode *ni, ntfs_time_update_flags mask)
|
||||
{
|
||||
time_t now;
|
||||
ntfs_time now;
|
||||
|
||||
if (!ni) {
|
||||
ntfs_log_error("%s(): Invalid arguments.\n", __FUNCTION__);
|
||||
@ -1090,7 +1348,7 @@ void ntfs_inode_update_times(ntfs_inode *ni, ntfs_time_update_flags mask)
|
||||
NVolReadOnly(ni->vol) || !mask)
|
||||
return;
|
||||
|
||||
now = time(NULL);
|
||||
now = ntfs_current_time();
|
||||
if (mask & NTFS_UPDATE_ATIME)
|
||||
ni->last_access_time = now;
|
||||
if (mask & NTFS_UPDATE_MTIME)
|
||||
@ -1145,3 +1403,164 @@ int ntfs_inode_badclus_bad(u64 mft_no, ATTR_RECORD *attr)
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
#ifdef HAVE_SETXATTR /* extended attributes interface required */
|
||||
|
||||
/*
|
||||
* Get high precision NTFS times
|
||||
*
|
||||
* They are returned in following order : create, update, access, change
|
||||
* provided they fit in requested size.
|
||||
*
|
||||
* Returns the modified size if successfull (or 32 if buffer size is null)
|
||||
* -errno if failed
|
||||
*/
|
||||
|
||||
int ntfs_inode_get_times(ntfs_inode *ni, char *value, size_t size)
|
||||
{
|
||||
ntfs_attr_search_ctx *ctx;
|
||||
STANDARD_INFORMATION *std_info;
|
||||
u64 *times;
|
||||
int ret;
|
||||
|
||||
ret = 0;
|
||||
ctx = ntfs_attr_get_search_ctx(ni, NULL);
|
||||
if (ctx) {
|
||||
if (ntfs_attr_lookup(AT_STANDARD_INFORMATION, AT_UNNAMED,
|
||||
0, CASE_SENSITIVE, 0, NULL, 0, ctx)) {
|
||||
ntfs_log_perror("Failed to get standard info (inode %lld)",
|
||||
(long long)ni->mft_no);
|
||||
} else {
|
||||
std_info = (STANDARD_INFORMATION *)((u8 *)ctx->attr +
|
||||
le16_to_cpu(ctx->attr->value_offset));
|
||||
if (value && (size >= 8)) {
|
||||
times = (u64*)value;
|
||||
times[0] = le64_to_cpu(std_info->creation_time);
|
||||
ret = 8;
|
||||
if (size >= 16) {
|
||||
times[1] = le64_to_cpu(std_info->last_data_change_time);
|
||||
ret = 16;
|
||||
}
|
||||
if (size >= 24) {
|
||||
times[2] = le64_to_cpu(std_info->last_access_time);
|
||||
ret = 24;
|
||||
}
|
||||
if (size >= 32) {
|
||||
times[3] = le64_to_cpu(std_info->last_mft_change_time);
|
||||
ret = 32;
|
||||
}
|
||||
} else
|
||||
if (!size)
|
||||
ret = 32;
|
||||
else
|
||||
ret = -ERANGE;
|
||||
}
|
||||
ntfs_attr_put_search_ctx(ctx);
|
||||
}
|
||||
return (ret ? ret : -errno);
|
||||
}
|
||||
|
||||
/*
|
||||
* Set high precision NTFS times
|
||||
*
|
||||
* They are expected in this order : create, update, access
|
||||
* provided they are present in input. The change time is set to
|
||||
* current time.
|
||||
*
|
||||
* The times are inserted directly in the standard_information and
|
||||
* file names attributes to avoid manipulating low precision times
|
||||
*
|
||||
* Returns 0 if success
|
||||
* -1 if there were an error (described by errno)
|
||||
*/
|
||||
|
||||
int ntfs_inode_set_times(ntfs_inode *ni, const char *value, size_t size,
|
||||
int flags)
|
||||
{
|
||||
ntfs_attr_search_ctx *ctx;
|
||||
STANDARD_INFORMATION *std_info;
|
||||
FILE_NAME_ATTR *fn;
|
||||
const u64 *times;
|
||||
ntfs_time now;
|
||||
int cnt;
|
||||
int ret;
|
||||
|
||||
ret = -1;
|
||||
if ((size >= 8) && !(flags & XATTR_CREATE)) {
|
||||
times = (const u64*)value;
|
||||
now = ntfs_current_time();
|
||||
/* update the standard information attribute */
|
||||
ctx = ntfs_attr_get_search_ctx(ni, NULL);
|
||||
if (ctx) {
|
||||
if (ntfs_attr_lookup(AT_STANDARD_INFORMATION,
|
||||
AT_UNNAMED, 0, CASE_SENSITIVE,
|
||||
0, NULL, 0, ctx)) {
|
||||
ntfs_log_perror("Failed to get standard info (inode %lld)",
|
||||
(long long)ni->mft_no);
|
||||
} else {
|
||||
std_info = (STANDARD_INFORMATION *)((u8 *)ctx->attr +
|
||||
le16_to_cpu(ctx->attr->value_offset));
|
||||
/*
|
||||
* Mark times set to avoid overwriting
|
||||
* them when the inode is closed.
|
||||
* The inode structure must also be updated
|
||||
* (with loss of precision) because of cacheing.
|
||||
* TODO : use NTFS precision in inode, and
|
||||
* return sub-second times in getattr()
|
||||
*/
|
||||
set_nino_flag(ni, TimesSet);
|
||||
std_info->creation_time = cpu_to_le64(times[0]);
|
||||
ni->creation_time
|
||||
= std_info->creation_time;
|
||||
if (size >= 16) {
|
||||
std_info->last_data_change_time = cpu_to_le64(times[1]);
|
||||
ni->last_data_change_time
|
||||
= std_info->last_data_change_time;
|
||||
}
|
||||
if (size >= 24) {
|
||||
std_info->last_access_time = cpu_to_le64(times[2]);
|
||||
ni->last_access_time
|
||||
= std_info->last_access_time;
|
||||
}
|
||||
std_info->last_mft_change_time = now;
|
||||
ni->last_mft_change_time = now;
|
||||
ntfs_inode_mark_dirty(ctx->ntfs_ino);
|
||||
NInoFileNameSetDirty(ni);
|
||||
|
||||
/* update the file names attributes */
|
||||
ntfs_attr_reinit_search_ctx(ctx);
|
||||
cnt = 0;
|
||||
while (!ntfs_attr_lookup(AT_FILE_NAME,
|
||||
AT_UNNAMED, 0, CASE_SENSITIVE,
|
||||
0, NULL, 0, ctx)) {
|
||||
fn = (FILE_NAME_ATTR*)((u8 *)ctx->attr +
|
||||
le16_to_cpu(ctx->attr->value_offset));
|
||||
fn->creation_time
|
||||
= cpu_to_le64(times[0]);
|
||||
if (size >= 16)
|
||||
fn->last_data_change_time
|
||||
= cpu_to_le64(times[1]);
|
||||
if (size >= 24)
|
||||
fn->last_access_time
|
||||
= cpu_to_le64(times[2]);
|
||||
fn->last_mft_change_time = now;
|
||||
cnt++;
|
||||
}
|
||||
if (cnt)
|
||||
ret = 0;
|
||||
else {
|
||||
ntfs_log_perror("Failed to get file names (inode %lld)",
|
||||
(long long)ni->mft_no);
|
||||
}
|
||||
}
|
||||
ntfs_attr_put_search_ctx(ctx);
|
||||
}
|
||||
} else
|
||||
if (size < 8)
|
||||
errno = ERANGE;
|
||||
else
|
||||
errno = EEXIST;
|
||||
return (ret);
|
||||
}
|
||||
|
||||
#endif /* HAVE_SETXATTR */
|
||||
|
@ -32,6 +32,7 @@ typedef struct _ntfs_inode ntfs_inode;
|
||||
#include "layout.h"
|
||||
#include "support.h"
|
||||
#include "volume.h"
|
||||
#include "ntfstime.h"
|
||||
|
||||
/**
|
||||
* enum ntfs_inode_state_bits -
|
||||
@ -48,6 +49,9 @@ typedef enum {
|
||||
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_TimesSet, /* 1: Use times which were set */
|
||||
NI_KnownSize, /* 1: Set if sizes are meaningful */
|
||||
} ntfs_inode_state_bits;
|
||||
|
||||
#define test_nino_flag(ni, flag) test_bit(NI_##flag, (ni)->state)
|
||||
@ -133,8 +137,11 @@ struct _ntfs_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).
|
||||
* For directories, they hold the index size, provided the
|
||||
* flag KnownSize is set.
|
||||
*/
|
||||
s64 data_size; /* Data size of unnamed DATA attribute. */
|
||||
s64 data_size; /* Data size of unnamed DATA attribute
|
||||
(or INDEX_ROOT for directories) */
|
||||
s64 allocated_size; /* Allocated size stored in the filename
|
||||
index. (NOTE: Equal to allocated size of
|
||||
the unnamed data attribute for normal or
|
||||
@ -147,10 +154,16 @@ struct _ntfs_inode {
|
||||
* 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_time creation_time;
|
||||
ntfs_time last_data_change_time;
|
||||
ntfs_time last_mft_change_time;
|
||||
ntfs_time 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 {
|
||||
@ -169,6 +182,19 @@ 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 int ntfs_inode_close_in_dir(ntfs_inode *ni, ntfs_inode *dir_ni);
|
||||
|
||||
#if CACHE_NIDATA_SIZE
|
||||
|
||||
struct CACHED_GENERIC;
|
||||
|
||||
extern int ntfs_inode_real_close(ntfs_inode *ni);
|
||||
extern void ntfs_inode_invalidate(ntfs_volume *vol, const MFT_REF mref);
|
||||
extern void ntfs_inode_nidata_free(const struct CACHED_GENERIC *cached);
|
||||
extern int ntfs_inode_nidata_hash(const struct CACHED_GENERIC *item);
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
extern ntfs_inode *ntfs_extent_inode_open(ntfs_inode *base_ni,
|
||||
const MFT_REF mref);
|
||||
@ -187,4 +213,13 @@ 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(ntfs_inode *ni, char *value, size_t size);
|
||||
|
||||
extern int ntfs_inode_set_times(ntfs_inode *ni, const char *value,
|
||||
size_t size, int flags);
|
||||
|
||||
/* debugging */
|
||||
#define debug_double_inode(num, type)
|
||||
#define debug_cached_inode(ni)
|
||||
|
||||
#endif /* defined _NTFS_INODE_H */
|
||||
|
@ -4,6 +4,7 @@
|
||||
* 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
|
||||
@ -54,6 +55,12 @@
|
||||
#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);
|
||||
@ -68,17 +75,44 @@ 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 == 1)
|
||||
if (zone == ZONE_MFT)
|
||||
ntfs_cluster_set_zone_pos(vol->mft_lcn, vol->mft_zone_end,
|
||||
&vol->mft_zone_pos, tc);
|
||||
else if (zone == 2)
|
||||
else if (zone == ZONE_DATA1)
|
||||
ntfs_cluster_set_zone_pos(vol->mft_zone_end, vol->nr_clusters,
|
||||
&vol->data1_zone_pos, tc);
|
||||
else /* zone == 4 */
|
||||
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;
|
||||
@ -87,30 +121,49 @@ static s64 max_empty_bit_range(unsigned char *buf, int size)
|
||||
|
||||
ntfs_log_trace("Entering\n");
|
||||
|
||||
for (i = 0; i < size; i++, buf++) {
|
||||
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++) {
|
||||
|
||||
if (*buf == 0) {
|
||||
run += 8;
|
||||
continue;
|
||||
}
|
||||
int bit = *buf & (1 << j);
|
||||
|
||||
for (j = 0; j < 8; j++) {
|
||||
if (bit) {
|
||||
if (run > max_range) {
|
||||
max_range = run;
|
||||
start_pos = (s64)i * 8 + (j - run);
|
||||
}
|
||||
run = 0;
|
||||
} else
|
||||
run++;
|
||||
}
|
||||
i++;
|
||||
buf++;
|
||||
|
||||
int bit = *buf & (1 << j);
|
||||
|
||||
if (bit) {
|
||||
if (run > max_range) {
|
||||
max_range = run;
|
||||
start_pos = i * 8 + j - run;
|
||||
}
|
||||
run = 0;
|
||||
} else
|
||||
run++;
|
||||
}
|
||||
}
|
||||
|
||||
if (run > max_range)
|
||||
start_pos = i * 8 - run;
|
||||
start_pos = (s64)i * 8 - run;
|
||||
|
||||
return start_pos;
|
||||
}
|
||||
@ -245,13 +298,13 @@ runlist *ntfs_cluster_alloc(ntfs_volume *vol, VCN start_vcn, s64 count,
|
||||
|
||||
if (zone_start < vol->mft_zone_start) {
|
||||
zone_end = vol->mft_zone_start;
|
||||
search_zone = 4;
|
||||
search_zone = ZONE_DATA2;
|
||||
} else if (zone_start < vol->mft_zone_end) {
|
||||
zone_end = vol->mft_zone_end;
|
||||
search_zone = 1;
|
||||
search_zone = ZONE_MFT;
|
||||
} else {
|
||||
zone_end = vol->nr_clusters;
|
||||
search_zone = 2;
|
||||
search_zone = ZONE_DATA1;
|
||||
}
|
||||
|
||||
bmp_pos = zone_start;
|
||||
@ -260,6 +313,9 @@ runlist *ntfs_cluster_alloc(ntfs_volume *vol, VCN start_vcn, s64 count,
|
||||
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);
|
||||
@ -371,9 +427,9 @@ runlist *ntfs_cluster_alloc(ntfs_volume *vol, VCN start_vcn, s64 count,
|
||||
|
||||
used_zone_pos = 1;
|
||||
|
||||
if (search_zone == 1)
|
||||
if (search_zone == ZONE_MFT)
|
||||
zone_start = vol->mft_zone_pos;
|
||||
else if (search_zone == 2)
|
||||
else if (search_zone == ZONE_DATA1)
|
||||
zone_start = vol->data1_zone_pos;
|
||||
else
|
||||
zone_start = vol->data2_zone_pos;
|
||||
@ -391,13 +447,12 @@ runlist *ntfs_cluster_alloc(ntfs_volume *vol, VCN start_vcn, s64 count,
|
||||
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 == 1)
|
||||
if (search_zone == ZONE_MFT)
|
||||
zone_start = vol->mft_zone_start;
|
||||
else if (search_zone == 2)
|
||||
else if (search_zone == ZONE_DATA1)
|
||||
zone_start = vol->mft_zone_end;
|
||||
else
|
||||
zone_start = 0;
|
||||
@ -413,11 +468,12 @@ zone_pass_done:
|
||||
/* pass == 2 */
|
||||
done_zones_check:
|
||||
done_zones |= search_zone;
|
||||
if (done_zones < 7) {
|
||||
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 +
|
||||
LCN tc = rl[rlpos - 1].lcn +
|
||||
rl[rlpos - 1].length + NTFS_LCNALLOC_SKIP;
|
||||
|
||||
if (used_zone_pos)
|
||||
@ -426,29 +482,29 @@ done_zones_check:
|
||||
}
|
||||
|
||||
switch (search_zone) {
|
||||
case 1:
|
||||
case ZONE_MFT:
|
||||
ntfs_log_trace("Zone switch: mft -> data1\n");
|
||||
switch_to_data1_zone: search_zone = 2;
|
||||
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 2:
|
||||
case ZONE_DATA1:
|
||||
ntfs_log_trace("Zone switch: data1 -> data2\n");
|
||||
search_zone = 4;
|
||||
search_zone = ZONE_DATA2;
|
||||
zone_start = vol->data2_zone_pos;
|
||||
zone_end = vol->mft_zone_start;
|
||||
if (!zone_start)
|
||||
pass = 2;
|
||||
break;
|
||||
case 4:
|
||||
if (!(done_zones & 2)) {
|
||||
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 = 1;
|
||||
search_zone = ZONE_MFT;
|
||||
zone_start = vol->mft_zone_pos;
|
||||
zone_end = vol->mft_zone_end;
|
||||
if (zone_start == vol->mft_zone_start)
|
||||
@ -530,6 +586,7 @@ int ntfs_cluster_free_from_rl(ntfs_volume *vol, runlist *rl)
|
||||
(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 "
|
||||
@ -552,6 +609,42 @@ out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Basic cluster run free
|
||||
* Returns 0 if successful
|
||||
*/
|
||||
|
||||
int ntfs_cluster_free_basic(ntfs_volume *vol, s64 lcn, s64 count)
|
||||
{
|
||||
s64 nr_freed = 0;
|
||||
int ret = -1;
|
||||
|
||||
ntfs_log_trace("Entering.\n");
|
||||
ntfs_log_trace("Dealloc lcn 0x%llx, len 0x%llx.\n",
|
||||
(long long)lcn, (long long)count);
|
||||
|
||||
if (lcn >= 0) {
|
||||
update_full_status(vol,lcn);
|
||||
if (ntfs_bitmap_clear_run(vol->lcnbmp_na, lcn,
|
||||
count)) {
|
||||
ntfs_log_perror("Cluster deallocation failed "
|
||||
"(%lld, %lld)",
|
||||
(long long)lcn,
|
||||
(long long)count);
|
||||
goto out;
|
||||
}
|
||||
nr_freed += count;
|
||||
}
|
||||
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
|
||||
@ -609,6 +702,7 @@ int ntfs_cluster_free(ntfs_volume *vol, ntfs_attr *na, VCN start_vcn, s64 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;
|
||||
@ -641,6 +735,7 @@ int ntfs_cluster_free(ntfs_volume *vol, ntfs_attr *na, VCN start_vcn, s64 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)
|
||||
|
@ -42,6 +42,7 @@ 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_basic(ntfs_volume *vol, s64 lcn, s64 count);
|
||||
|
||||
extern int ntfs_cluster_free(ntfs_volume *vol, ntfs_attr *na, VCN start_vcn,
|
||||
s64 count);
|
||||
|
@ -277,6 +277,7 @@ static const char * ntfs_log_get_prefix(u32 level)
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifndef __HAIKU__
|
||||
/**
|
||||
* ntfs_log_set_handler - Provide an alternate logging handler
|
||||
* @handler: function to perform the logging
|
||||
@ -295,6 +296,7 @@ void ntfs_log_set_handler(ntfs_log_handler *handler)
|
||||
} else
|
||||
ntfs_log.handler = ntfs_log_handler_null;
|
||||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
* ntfs_log_redirect - Pass on the request to the real handler
|
||||
|
@ -37,10 +37,8 @@
|
||||
typedef int (ntfs_log_handler)(const char *function, const char *file, int line,
|
||||
u32 level, void *data, const char *format, va_list args);
|
||||
|
||||
#ifndef __HAIKU__
|
||||
/* Set the logging handler from one of the functions, below. */
|
||||
#ifdef __HAIKU__
|
||||
void ntfs_log_set_handler(ntfs_log_handler *handler);
|
||||
#else
|
||||
void ntfs_log_set_handler(ntfs_log_handler *handler
|
||||
__attribute__((format(printf, 6, 0))));
|
||||
#endif
|
||||
|
@ -38,6 +38,9 @@
|
||||
#ifdef HAVE_STRING_H
|
||||
#include <string.h>
|
||||
#endif
|
||||
#ifdef HAVE_LIMITS_H
|
||||
#include <limits.h>
|
||||
#endif
|
||||
#include <time.h>
|
||||
|
||||
#include "compat.h"
|
||||
@ -718,7 +721,7 @@ static int ntfs_mft_bitmap_extend_allocation_i(ntfs_volume *vol)
|
||||
goto undo_alloc;
|
||||
}
|
||||
/* Get the size for the new mapping pairs array for this extent. */
|
||||
mp_size = ntfs_get_size_for_mapping_pairs(vol, rl2, ll);
|
||||
mp_size = ntfs_get_size_for_mapping_pairs(vol, rl2, ll, INT_MAX);
|
||||
if (mp_size <= 0) {
|
||||
ntfs_log_error("Get size for mapping pairs failed for "
|
||||
"mft bitmap attribute extent.\n");
|
||||
@ -1067,7 +1070,7 @@ static int ntfs_mft_data_extend_allocation(ntfs_volume *vol)
|
||||
goto undo_alloc;
|
||||
}
|
||||
/* Get the size for the new mapping pairs array for this extent. */
|
||||
mp_size = ntfs_get_size_for_mapping_pairs(vol, rl2, ll);
|
||||
mp_size = ntfs_get_size_for_mapping_pairs(vol, rl2, ll, INT_MAX);
|
||||
if (mp_size <= 0) {
|
||||
ntfs_log_error("Get size for mapping pairs failed for "
|
||||
"mft data attribute extent.\n");
|
||||
@ -1359,7 +1362,7 @@ static ntfs_inode *ntfs_mft_rec_alloc(ntfs_volume *vol)
|
||||
ntfs_inode *ni = NULL;
|
||||
ntfs_inode *base_ni;
|
||||
int err;
|
||||
u16 seq_no, usn;
|
||||
le16 seq_no, usn;
|
||||
|
||||
ntfs_log_enter("Entering\n");
|
||||
|
||||
@ -1413,17 +1416,17 @@ found_free_rec:
|
||||
}
|
||||
|
||||
seq_no = m->sequence_number;
|
||||
usn = *(u16*)((u8*)m + le16_to_cpu(m->usa_ofs));
|
||||
usn = *(le16*)((u8*)m + le16_to_cpu(m->usa_ofs));
|
||||
if (ntfs_mft_record_layout(vol, bit, m)) {
|
||||
ntfs_log_error("Failed to re-format mft record.\n");
|
||||
free(m);
|
||||
goto undo_mftbmp_alloc;
|
||||
}
|
||||
if (le16_to_cpu(seq_no))
|
||||
if (seq_no)
|
||||
m->sequence_number = seq_no;
|
||||
seq_no = le16_to_cpu(usn);
|
||||
if (seq_no && seq_no != 0xffff)
|
||||
*(u16*)((u8*)m + le16_to_cpu(m->usa_ofs)) = usn;
|
||||
seq_no = usn;
|
||||
if (seq_no && seq_no != const_cpu_to_le16(0xffff))
|
||||
*(le16*)((u8*)m + le16_to_cpu(m->usa_ofs)) = usn;
|
||||
/* Set the mft record itself in use. */
|
||||
m->flags |= MFT_RECORD_IN_USE;
|
||||
/* Now need to open an ntfs inode for the mft record. */
|
||||
@ -1475,7 +1478,7 @@ found_free_rec:
|
||||
ni->flags = 0;
|
||||
ni->creation_time = ni->last_data_change_time =
|
||||
ni->last_mft_change_time =
|
||||
ni->last_access_time = time(NULL);
|
||||
ni->last_access_time = ntfs_current_time();
|
||||
/* Update the default mft allocation position if it was used. */
|
||||
if (!base_ni)
|
||||
vol->mft_data_pos = bit + 1;
|
||||
@ -1588,7 +1591,7 @@ ntfs_inode *ntfs_mft_record_alloc(ntfs_volume *vol, ntfs_inode *base_ni)
|
||||
MFT_RECORD *m;
|
||||
ntfs_inode *ni = NULL;
|
||||
int err;
|
||||
u16 seq_no, usn;
|
||||
le16 seq_no, usn;
|
||||
|
||||
if (base_ni)
|
||||
ntfs_log_enter("Entering (allocating an extent mft record for "
|
||||
@ -1714,17 +1717,17 @@ found_free_rec:
|
||||
goto retry;
|
||||
}
|
||||
seq_no = m->sequence_number;
|
||||
usn = *(u16*)((u8*)m + le16_to_cpu(m->usa_ofs));
|
||||
usn = *(le16*)((u8*)m + le16_to_cpu(m->usa_ofs));
|
||||
if (ntfs_mft_record_layout(vol, bit, m)) {
|
||||
ntfs_log_error("Failed to re-format mft record.\n");
|
||||
free(m);
|
||||
goto undo_mftbmp_alloc;
|
||||
}
|
||||
if (le16_to_cpu(seq_no))
|
||||
if (seq_no)
|
||||
m->sequence_number = seq_no;
|
||||
seq_no = le16_to_cpu(usn);
|
||||
if (seq_no && seq_no != 0xffff)
|
||||
*(u16*)((u8*)m + le16_to_cpu(m->usa_ofs)) = usn;
|
||||
seq_no = usn;
|
||||
if (seq_no && seq_no != const_cpu_to_le16(0xffff))
|
||||
*(le16*)((u8*)m + le16_to_cpu(m->usa_ofs)) = usn;
|
||||
/* Set the mft record itself in use. */
|
||||
m->flags |= MFT_RECORD_IN_USE;
|
||||
/* Now need to open an ntfs inode for the mft record. */
|
||||
@ -1777,7 +1780,7 @@ found_free_rec:
|
||||
ni->flags = 0;
|
||||
ni->creation_time = ni->last_data_change_time =
|
||||
ni->last_mft_change_time =
|
||||
ni->last_access_time = time(NULL);
|
||||
ni->last_access_time = ntfs_current_time();
|
||||
/* Update the default mft allocation position if it was used. */
|
||||
if (!base_ni)
|
||||
vol->mft_data_pos = bit + 1;
|
||||
@ -1816,7 +1819,8 @@ int ntfs_mft_record_free(ntfs_volume *vol, ntfs_inode *ni)
|
||||
{
|
||||
u64 mft_no;
|
||||
int err;
|
||||
u16 seq_no, old_seq_no;
|
||||
u16 seq_no;
|
||||
le16 old_seq_no;
|
||||
|
||||
ntfs_log_trace("Entering for inode 0x%llx.\n", (long long) ni->mft_no);
|
||||
|
||||
@ -1856,7 +1860,11 @@ int ntfs_mft_record_free(ntfs_volume *vol, ntfs_inode *ni)
|
||||
}
|
||||
|
||||
/* Throw away the now freed inode. */
|
||||
#if CACHE_NIDATA_SIZE
|
||||
if (!ntfs_inode_real_close(ni)) {
|
||||
#else
|
||||
if (!ntfs_inode_close(ni)) {
|
||||
#endif
|
||||
vol->free_mft_records++;
|
||||
return 0;
|
||||
}
|
||||
@ -1883,13 +1891,14 @@ sync_rollback:
|
||||
*/
|
||||
int ntfs_mft_usn_dec(MFT_RECORD *mrec)
|
||||
{
|
||||
u16 usn, *usnp;
|
||||
u16 usn;
|
||||
le16 *usnp;
|
||||
|
||||
if (!mrec) {
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
usnp = (u16 *)((char *)mrec + le16_to_cpu(mrec->usa_ofs));
|
||||
usnp = (le16*)((char*)mrec + le16_to_cpu(mrec->usa_ofs));
|
||||
usn = le16_to_cpup(usnp);
|
||||
if (usn-- <= 1)
|
||||
usn = 0xfffe;
|
||||
|
@ -1,3 +1,25 @@
|
||||
/**
|
||||
* misc.c : miscellaneous :
|
||||
* - dealing with errors in memory allocation
|
||||
*
|
||||
* 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
|
||||
@ -5,7 +27,11 @@
|
||||
#ifdef HAVE_STDLIB_H
|
||||
#include <stdlib.h>
|
||||
#endif
|
||||
#ifdef HAVE_STRING_H
|
||||
#include <string.h>
|
||||
#endif
|
||||
|
||||
#include "types.h"
|
||||
#include "misc.h"
|
||||
#include "logging.h"
|
||||
|
||||
@ -33,4 +59,3 @@ void *ntfs_malloc(size_t size)
|
||||
ntfs_log_perror("Failed to malloc %lld bytes", (long long)size);
|
||||
return p;
|
||||
}
|
||||
|
||||
|
@ -1,3 +1,25 @@
|
||||
/*
|
||||
* misc.h : miscellaneous exports
|
||||
* - memory allocation
|
||||
*
|
||||
* 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_
|
||||
|
||||
|
@ -3,6 +3,7 @@
|
||||
*
|
||||
* Copyright (c) 2005 Anton Altaparmakov
|
||||
* Copyright (c) 2005 Yura Pakhuchiy
|
||||
* Copyright (c) 2010 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
|
||||
@ -26,44 +27,105 @@
|
||||
#ifdef HAVE_TIME_H
|
||||
#include <time.h>
|
||||
#endif
|
||||
#ifdef HAVE_SYS_STAT_H
|
||||
#include <sys/stat.h>
|
||||
#endif
|
||||
#ifdef HAVE_GETTIMEOFDAY
|
||||
#include <sys/time.h>
|
||||
#endif
|
||||
|
||||
#include "types.h"
|
||||
|
||||
/*
|
||||
* assume "struct timespec" is not defined if st_mtime is not defined
|
||||
*/
|
||||
#if !defined(st_mtime) & !defined(__timespec_defined)
|
||||
struct timespec {
|
||||
time_t tv_sec;
|
||||
long tv_nsec;
|
||||
} ;
|
||||
#endif
|
||||
|
||||
/*
|
||||
* There are four times more conversions of internal representation
|
||||
* to ntfs representation than any other conversion, so the most
|
||||
* efficient internal representation is ntfs representation
|
||||
* (with low endianness)
|
||||
*/
|
||||
typedef sle64 ntfs_time;
|
||||
|
||||
#define NTFS_TIME_OFFSET ((s64)(369 * 365 + 89) * 24 * 3600 * 10000000)
|
||||
|
||||
/**
|
||||
* ntfs2utc - Convert an NTFS time to Unix time
|
||||
* ntfs2timespec - 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)
|
||||
* Return: A Unix time (number of seconds since 1970, and nanoseconds)
|
||||
*/
|
||||
static __inline__ time_t ntfs2utc(s64 ntfs_time)
|
||||
static __inline__ struct timespec ntfs2timespec(ntfs_time ntfstime)
|
||||
{
|
||||
return (sle64_to_cpu(ntfs_time) - (NTFS_TIME_OFFSET)) / 10000000;
|
||||
struct timespec spec;
|
||||
s64 cputime;
|
||||
|
||||
cputime = sle64_to_cpu(ntfstime);
|
||||
spec.tv_sec = (cputime - (NTFS_TIME_OFFSET)) / 10000000;
|
||||
spec.tv_nsec = (cputime - (NTFS_TIME_OFFSET)
|
||||
- (s64)spec.tv_sec*10000000)*100;
|
||||
/* force zero nsec for overflowing dates */
|
||||
if ((spec.tv_nsec < 0) || (spec.tv_nsec > 999999999))
|
||||
spec.tv_nsec = 0;
|
||||
return (spec);
|
||||
}
|
||||
|
||||
/**
|
||||
* utc2ntfs - Convert Linux time to NTFS time
|
||||
* timespec2ntfs - 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.
|
||||
* 1-second intervals since 1st January 1970, 00:00:00 UTC
|
||||
* with a separated non-negative nanosecond value
|
||||
*
|
||||
* NTFS uses Microsoft's standard time format which is stored in a s64 and is
|
||||
* NTFS uses Microsoft's standard time format which is stored in a sle64 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)
|
||||
* Return: An NTFS time (100ns units since Jan 1601)
|
||||
*/
|
||||
static __inline__ s64 utc2ntfs(time_t utc_time)
|
||||
static __inline__ ntfs_time timespec2ntfs(struct timespec spec)
|
||||
{
|
||||
/* Convert to 100ns intervals and then add the NTFS time offset. */
|
||||
return cpu_to_sle64((s64)utc_time * 10000000 + NTFS_TIME_OFFSET);
|
||||
s64 units;
|
||||
|
||||
units = (s64)spec.tv_sec * 10000000
|
||||
+ NTFS_TIME_OFFSET + spec.tv_nsec/100;
|
||||
return (cpu_to_le64(units));
|
||||
}
|
||||
|
||||
/*
|
||||
* Return the current time in ntfs format
|
||||
*/
|
||||
|
||||
static __inline__ ntfs_time ntfs_current_time(void)
|
||||
{
|
||||
struct timespec now;
|
||||
|
||||
#if defined(HAVE_CLOCK_GETTIME) || defined(HAVE_SYS_CLOCK_GETTIME)
|
||||
clock_gettime(CLOCK_REALTIME, &now);
|
||||
#elif defined(HAVE_GETTIMEOFDAY)
|
||||
struct timeval microseconds;
|
||||
|
||||
gettimeofday(µseconds, (struct timezone*)NULL);
|
||||
now.tv_sec = microseconds.tv_sec;
|
||||
now.tv_nsec = microseconds.tv_usec*1000;
|
||||
#else
|
||||
now.tv_sec = time((time_t*)NULL);
|
||||
now.tv_nsec = 0;
|
||||
#endif
|
||||
return (timespec2ntfs(now));
|
||||
}
|
||||
|
||||
#endif /* _NTFS_NTFSTIME_H */
|
||||
|
637
src/add-ons/kernel/file_systems/ntfs/libntfs/object_id.c
Normal file
637
src/add-ons/kernel/file_systems/ntfs/libntfs/object_id.c
Normal file
@ -0,0 +1,637 @@
|
||||
/**
|
||||
* object_id.c - Processing of object ids
|
||||
*
|
||||
* This module is part of ntfs-3g library
|
||||
*
|
||||
* 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 "volume.h"
|
||||
#include "mft.h"
|
||||
#include "index.h"
|
||||
#include "lcnalloc.h"
|
||||
#include "object_id.h"
|
||||
#include "logging.h"
|
||||
#include "misc.h"
|
||||
|
||||
/*
|
||||
* Endianness considerations
|
||||
*
|
||||
* According to RFC 4122, GUIDs should be printed with the most
|
||||
* significant byte first, and the six fields be compared individually
|
||||
* for ordering. RFC 4122 does not define the internal representation.
|
||||
*
|
||||
* Here we always copy disk images with no endianness change,
|
||||
* and, for indexing, GUIDs are compared as if they were a sequence
|
||||
* of four unsigned 32 bit integers.
|
||||
*
|
||||
* --------------------- begin from RFC 4122 ----------------------
|
||||
* Consider each field of the UUID to be an unsigned integer as shown
|
||||
* in the table in section Section 4.1.2. Then, to compare a pair of
|
||||
* UUIDs, arithmetically compare the corresponding fields from each
|
||||
* UUID in order of significance and according to their data type.
|
||||
* Two UUIDs are equal if and only if all the corresponding fields
|
||||
* are equal.
|
||||
*
|
||||
* UUIDs, as defined in this document, can also be ordered
|
||||
* lexicographically. For a pair of UUIDs, the first one follows the
|
||||
* second if the most significant field in which the UUIDs differ is
|
||||
* greater for the first UUID. The second precedes the first if the
|
||||
* most significant field in which the UUIDs differ is greater for
|
||||
* the second UUID.
|
||||
*
|
||||
* The fields are encoded as 16 octets, with the sizes and order of the
|
||||
* fields defined above, and with each field encoded with the Most
|
||||
* Significant Byte first (known as network byte order). Note that the
|
||||
* field names, particularly for multiplexed fields, follow historical
|
||||
* practice.
|
||||
*
|
||||
* 0 1 2 3
|
||||
* 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
|
||||
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
* | time_low |
|
||||
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
* | time_mid | time_hi_and_version |
|
||||
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
* |clk_seq_hi_res | clk_seq_low | node (0-1) |
|
||||
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
* | node (2-5) |
|
||||
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
*
|
||||
* ---------------------- end from RFC 4122 -----------------------
|
||||
*/
|
||||
|
||||
typedef struct {
|
||||
GUID object_id;
|
||||
} OBJECT_ID_INDEX_KEY;
|
||||
|
||||
typedef struct {
|
||||
le64 file_id;
|
||||
GUID birth_volume_id;
|
||||
GUID birth_object_id;
|
||||
GUID domain_id;
|
||||
} OBJECT_ID_INDEX_DATA; // known as OBJ_ID_INDEX_DATA
|
||||
|
||||
struct OBJECT_ID_INDEX { /* index entry in $Extend/$ObjId */
|
||||
INDEX_ENTRY_HEADER header;
|
||||
OBJECT_ID_INDEX_KEY key;
|
||||
OBJECT_ID_INDEX_DATA data;
|
||||
} ;
|
||||
|
||||
static ntfschar objid_index_name[] = { const_cpu_to_le16('$'),
|
||||
const_cpu_to_le16('O') };
|
||||
#ifdef HAVE_SETXATTR /* extended attributes interface required */
|
||||
|
||||
/*
|
||||
* Set the index for a new object id
|
||||
*
|
||||
* Returns 0 if success
|
||||
* -1 if failure, explained by errno
|
||||
*/
|
||||
|
||||
static int set_object_id_index(ntfs_inode *ni, ntfs_index_context *xo,
|
||||
const OBJECT_ID_ATTR *object_id)
|
||||
{
|
||||
struct OBJECT_ID_INDEX indx;
|
||||
u64 file_id_cpu;
|
||||
le64 file_id;
|
||||
le16 seqn;
|
||||
|
||||
seqn = ni->mrec->sequence_number;
|
||||
file_id_cpu = MK_MREF(ni->mft_no,le16_to_cpu(seqn));
|
||||
file_id = cpu_to_le64(file_id_cpu);
|
||||
indx.header.data_offset = const_cpu_to_le16(
|
||||
sizeof(INDEX_ENTRY_HEADER)
|
||||
+ sizeof(OBJECT_ID_INDEX_KEY));
|
||||
indx.header.data_length = const_cpu_to_le16(
|
||||
sizeof(OBJECT_ID_INDEX_DATA));
|
||||
indx.header.reservedV = const_cpu_to_le32(0);
|
||||
indx.header.length = const_cpu_to_le16(
|
||||
sizeof(struct OBJECT_ID_INDEX));
|
||||
indx.header.key_length = const_cpu_to_le16(
|
||||
sizeof(OBJECT_ID_INDEX_KEY));
|
||||
indx.header.flags = const_cpu_to_le16(0);
|
||||
indx.header.reserved = const_cpu_to_le16(0);
|
||||
|
||||
memcpy(&indx.key.object_id,object_id,sizeof(GUID));
|
||||
|
||||
indx.data.file_id = file_id;
|
||||
memcpy(&indx.data.birth_volume_id,
|
||||
&object_id->birth_volume_id,sizeof(GUID));
|
||||
memcpy(&indx.data.birth_object_id,
|
||||
&object_id->birth_object_id,sizeof(GUID));
|
||||
memcpy(&indx.data.domain_id,
|
||||
&object_id->domain_id,sizeof(GUID));
|
||||
ntfs_index_ctx_reinit(xo);
|
||||
return (ntfs_ie_add(xo,(INDEX_ENTRY*)&indx));
|
||||
}
|
||||
|
||||
#endif /* HAVE_SETXATTR */
|
||||
|
||||
/*
|
||||
* Open the $Extend/$ObjId file and its index
|
||||
*
|
||||
* Return the index context if opened
|
||||
* or NULL if an error occurred (errno tells why)
|
||||
*
|
||||
* The index has to be freed and inode closed when not needed any more.
|
||||
*/
|
||||
|
||||
static ntfs_index_context *open_object_id_index(ntfs_volume *vol)
|
||||
{
|
||||
u64 inum;
|
||||
ntfs_inode *ni;
|
||||
ntfs_inode *dir_ni;
|
||||
ntfs_index_context *xo;
|
||||
|
||||
/* do not use path_name_to inode - could reopen root */
|
||||
dir_ni = ntfs_inode_open(vol, FILE_Extend);
|
||||
ni = (ntfs_inode*)NULL;
|
||||
if (dir_ni) {
|
||||
inum = ntfs_inode_lookup_by_mbsname(dir_ni,"$ObjId");
|
||||
if (inum != (u64)-1)
|
||||
ni = ntfs_inode_open(vol, inum);
|
||||
ntfs_inode_close(dir_ni);
|
||||
}
|
||||
if (ni) {
|
||||
xo = ntfs_index_ctx_get(ni, objid_index_name, 2);
|
||||
if (!xo) {
|
||||
ntfs_inode_close(ni);
|
||||
}
|
||||
} else
|
||||
xo = (ntfs_index_context*)NULL;
|
||||
return (xo);
|
||||
}
|
||||
|
||||
#ifdef HAVE_SETXATTR /* extended attributes interface required */
|
||||
|
||||
/*
|
||||
* Merge object_id data stored in the index into
|
||||
* a full object_id struct.
|
||||
*
|
||||
* returns 0 if merging successful
|
||||
* -1 if no data could be merged. This is generally not an error
|
||||
*/
|
||||
|
||||
static int merge_index_data(ntfs_inode *ni,
|
||||
const OBJECT_ID_ATTR *objectid_attr,
|
||||
OBJECT_ID_ATTR *full_objectid)
|
||||
{
|
||||
OBJECT_ID_INDEX_KEY key;
|
||||
struct OBJECT_ID_INDEX *entry;
|
||||
ntfs_index_context *xo;
|
||||
ntfs_inode *xoni;
|
||||
int res;
|
||||
|
||||
res = -1;
|
||||
xo = open_object_id_index(ni->vol);
|
||||
if (xo) {
|
||||
memcpy(&key.object_id,objectid_attr,sizeof(GUID));
|
||||
if (!ntfs_index_lookup(&key,
|
||||
sizeof(OBJECT_ID_INDEX_KEY), xo)) {
|
||||
entry = (struct OBJECT_ID_INDEX*)xo->entry;
|
||||
/* make sure inode numbers match */
|
||||
if (entry
|
||||
&& (MREF(le64_to_cpu(entry->data.file_id))
|
||||
== ni->mft_no)) {
|
||||
memcpy(&full_objectid->birth_volume_id,
|
||||
&entry->data.birth_volume_id,
|
||||
sizeof(GUID));
|
||||
memcpy(&full_objectid->birth_object_id,
|
||||
&entry->data.birth_object_id,
|
||||
sizeof(GUID));
|
||||
memcpy(&full_objectid->domain_id,
|
||||
&entry->data.domain_id,
|
||||
sizeof(GUID));
|
||||
res = 0;
|
||||
}
|
||||
}
|
||||
xoni = xo->ni;
|
||||
ntfs_index_ctx_put(xo);
|
||||
ntfs_inode_close(xoni);
|
||||
}
|
||||
return (res);
|
||||
}
|
||||
|
||||
#endif /* HAVE_SETXATTR */
|
||||
|
||||
/*
|
||||
* Remove an object id index entry if attribute present
|
||||
*
|
||||
* Returns the size of existing object id
|
||||
* (the existing object_d is returned)
|
||||
* -1 if failure, explained by errno
|
||||
*/
|
||||
|
||||
static int remove_object_id_index(ntfs_attr *na, ntfs_index_context *xo,
|
||||
OBJECT_ID_ATTR *old_attr)
|
||||
{
|
||||
OBJECT_ID_INDEX_KEY key;
|
||||
struct OBJECT_ID_INDEX *entry;
|
||||
s64 size;
|
||||
int ret;
|
||||
|
||||
ret = na->data_size;
|
||||
if (ret) {
|
||||
/* read the existing object id attribute */
|
||||
size = ntfs_attr_pread(na, 0, sizeof(GUID), old_attr);
|
||||
if (size >= (s64)sizeof(GUID)) {
|
||||
memcpy(&key.object_id,
|
||||
&old_attr->object_id,sizeof(GUID));
|
||||
size = sizeof(GUID);
|
||||
if (!ntfs_index_lookup(&key,
|
||||
sizeof(OBJECT_ID_INDEX_KEY), xo)) {
|
||||
entry = (struct OBJECT_ID_INDEX*)xo->entry;
|
||||
memcpy(&old_attr->birth_volume_id,
|
||||
&entry->data.birth_volume_id,
|
||||
sizeof(GUID));
|
||||
memcpy(&old_attr->birth_object_id,
|
||||
&entry->data.birth_object_id,
|
||||
sizeof(GUID));
|
||||
memcpy(&old_attr->domain_id,
|
||||
&entry->data.domain_id,
|
||||
sizeof(GUID));
|
||||
size = sizeof(OBJECT_ID_ATTR);
|
||||
if (ntfs_index_rm(xo))
|
||||
ret = -1;
|
||||
}
|
||||
} else {
|
||||
ret = -1;
|
||||
errno = ENODATA;
|
||||
}
|
||||
}
|
||||
return (ret);
|
||||
}
|
||||
|
||||
#ifdef HAVE_SETXATTR /* extended attributes interface required */
|
||||
|
||||
/*
|
||||
* Update the object id and index
|
||||
*
|
||||
* The object_id attribute should have been created and the
|
||||
* non-duplication of the GUID should have been checked before.
|
||||
*
|
||||
* Returns 0 if success
|
||||
* -1 if failure, explained by errno
|
||||
* If could not remove the existing index, nothing is done,
|
||||
* If could not write the new data, no index entry is inserted
|
||||
* If failed to insert the index, data is removed
|
||||
*/
|
||||
|
||||
static int update_object_id(ntfs_inode *ni, ntfs_index_context *xo,
|
||||
const OBJECT_ID_ATTR *value, size_t size)
|
||||
{
|
||||
OBJECT_ID_ATTR old_attr;
|
||||
ntfs_attr *na;
|
||||
int oldsize;
|
||||
int written;
|
||||
int res;
|
||||
|
||||
res = 0;
|
||||
|
||||
na = ntfs_attr_open(ni, AT_OBJECT_ID, AT_UNNAMED, 0);
|
||||
if (na) {
|
||||
|
||||
/* remove the existing index entry */
|
||||
oldsize = remove_object_id_index(na,xo,&old_attr);
|
||||
if (oldsize < 0)
|
||||
res = -1;
|
||||
else {
|
||||
/* resize attribute */
|
||||
res = ntfs_attr_truncate(na, (s64)sizeof(GUID));
|
||||
/* write the object_id in attribute */
|
||||
if (!res && value) {
|
||||
written = (int)ntfs_attr_pwrite(na,
|
||||
(s64)0, (s64)sizeof(GUID),
|
||||
&value->object_id);
|
||||
if (written != (s64)sizeof(GUID)) {
|
||||
ntfs_log_error("Failed to update "
|
||||
"object id\n");
|
||||
errno = EIO;
|
||||
res = -1;
|
||||
}
|
||||
}
|
||||
/* write index part if provided */
|
||||
if (!res
|
||||
&& ((size < sizeof(OBJECT_ID_ATTR))
|
||||
|| set_object_id_index(ni,xo,value))) {
|
||||
/*
|
||||
* If cannot index, try to remove the object
|
||||
* id and log the error. There will be an
|
||||
* inconsistency if removal fails.
|
||||
*/
|
||||
ntfs_attr_rm(na);
|
||||
ntfs_log_error("Failed to index object id."
|
||||
" Possible corruption.\n");
|
||||
}
|
||||
}
|
||||
ntfs_attr_close(na);
|
||||
NInoSetDirty(ni);
|
||||
} else
|
||||
res = -1;
|
||||
return (res);
|
||||
}
|
||||
|
||||
/*
|
||||
* Add a (dummy) object id to an inode if it does not exist
|
||||
*
|
||||
* returns 0 if attribute was inserted (or already present)
|
||||
* -1 if adding failed (explained by errno)
|
||||
*/
|
||||
|
||||
static int add_object_id(ntfs_inode *ni, int flags)
|
||||
{
|
||||
int res;
|
||||
u8 dummy;
|
||||
|
||||
res = -1; /* default return */
|
||||
if (!ntfs_attr_exist(ni,AT_OBJECT_ID, AT_UNNAMED,0)) {
|
||||
if (!(flags & XATTR_REPLACE)) {
|
||||
/*
|
||||
* no object id attribute : add one,
|
||||
* apparently, this does not feed the new value in
|
||||
* Note : NTFS version must be >= 3
|
||||
*/
|
||||
if (ni->vol->major_ver >= 3) {
|
||||
res = ntfs_attr_add(ni, AT_OBJECT_ID,
|
||||
AT_UNNAMED, 0, &dummy, (s64)0);
|
||||
NInoSetDirty(ni);
|
||||
} else
|
||||
errno = EOPNOTSUPP;
|
||||
} else
|
||||
errno = ENODATA;
|
||||
} else {
|
||||
if (flags & XATTR_CREATE)
|
||||
errno = EEXIST;
|
||||
else
|
||||
res = 0;
|
||||
}
|
||||
return (res);
|
||||
}
|
||||
|
||||
#endif /* HAVE_SETXATTR */
|
||||
|
||||
/*
|
||||
* Delete an object_id index entry
|
||||
*
|
||||
* Returns 0 if success
|
||||
* -1 if failure, explained by errno
|
||||
*/
|
||||
|
||||
int ntfs_delete_object_id_index(ntfs_inode *ni)
|
||||
{
|
||||
ntfs_index_context *xo;
|
||||
ntfs_inode *xoni;
|
||||
ntfs_attr *na;
|
||||
OBJECT_ID_ATTR old_attr;
|
||||
int res;
|
||||
|
||||
res = 0;
|
||||
na = ntfs_attr_open(ni, AT_OBJECT_ID, AT_UNNAMED, 0);
|
||||
if (na) {
|
||||
/*
|
||||
* read the existing object id
|
||||
* and un-index it
|
||||
*/
|
||||
xo = open_object_id_index(ni->vol);
|
||||
if (xo) {
|
||||
if (remove_object_id_index(na,xo,&old_attr) < 0)
|
||||
res = -1;
|
||||
xoni = xo->ni;
|
||||
ntfs_index_entry_mark_dirty(xo);
|
||||
NInoSetDirty(xoni);
|
||||
ntfs_index_ctx_put(xo);
|
||||
ntfs_inode_close(xoni);
|
||||
}
|
||||
ntfs_attr_close(na);
|
||||
}
|
||||
return (res);
|
||||
}
|
||||
|
||||
#ifdef HAVE_SETXATTR /* extended attributes interface required */
|
||||
|
||||
/*
|
||||
* Get the ntfs object id into an extended attribute
|
||||
*
|
||||
* If present, the object_id from the attribute and the GUIDs
|
||||
* from the index are returned (formatted as OBJECT_ID_ATTR)
|
||||
*
|
||||
* Returns the global size (can be 0, 16 or 64)
|
||||
* and the buffer is updated if it is long enough
|
||||
*/
|
||||
|
||||
int ntfs_get_ntfs_object_id(ntfs_inode *ni, char *value, size_t size)
|
||||
{
|
||||
OBJECT_ID_ATTR full_objectid;
|
||||
OBJECT_ID_ATTR *objectid_attr;
|
||||
s64 attr_size;
|
||||
int full_size;
|
||||
|
||||
full_size = 0; /* default to no data and some error to be defined */
|
||||
if (ni) {
|
||||
objectid_attr = (OBJECT_ID_ATTR*)ntfs_attr_readall(ni,
|
||||
AT_OBJECT_ID,(ntfschar*)NULL, 0, &attr_size);
|
||||
if (objectid_attr) {
|
||||
/* restrict to only GUID present in attr */
|
||||
if (attr_size == sizeof(GUID)) {
|
||||
memcpy(&full_objectid.object_id,
|
||||
objectid_attr,sizeof(GUID));
|
||||
full_size = sizeof(GUID);
|
||||
/* get data from index, if any */
|
||||
if (!merge_index_data(ni, objectid_attr,
|
||||
&full_objectid)) {
|
||||
full_size = sizeof(OBJECT_ID_ATTR);
|
||||
}
|
||||
if (full_size <= (s64)size) {
|
||||
if (value)
|
||||
memcpy(value,&full_objectid,
|
||||
full_size);
|
||||
else
|
||||
errno = EINVAL;
|
||||
}
|
||||
} else {
|
||||
/* unexpected size, better return unsupported */
|
||||
errno = EOPNOTSUPP;
|
||||
full_size = 0;
|
||||
}
|
||||
free(objectid_attr);
|
||||
} else
|
||||
errno = ENODATA;
|
||||
}
|
||||
return (full_size ? (int)full_size : -errno);
|
||||
}
|
||||
|
||||
/*
|
||||
* Set the object id from an extended attribute
|
||||
*
|
||||
* If the size is 64, the attribute and index are set.
|
||||
* else if the size is not less than 16 only the attribute is set.
|
||||
* The object id index is set accordingly.
|
||||
*
|
||||
* Returns 0, or -1 if there is a problem
|
||||
*/
|
||||
|
||||
int ntfs_set_ntfs_object_id(ntfs_inode *ni,
|
||||
const char *value, size_t size, int flags)
|
||||
{
|
||||
OBJECT_ID_INDEX_KEY key;
|
||||
ntfs_inode *xoni;
|
||||
ntfs_index_context *xo;
|
||||
int res;
|
||||
|
||||
res = 0;
|
||||
if (ni && value && (size >= sizeof(GUID))) {
|
||||
xo = open_object_id_index(ni->vol);
|
||||
if (xo) {
|
||||
/* make sure the GUID was not used somewhere */
|
||||
memcpy(&key.object_id, value, sizeof(GUID));
|
||||
if (ntfs_index_lookup(&key,
|
||||
sizeof(OBJECT_ID_INDEX_KEY), xo)) {
|
||||
ntfs_index_ctx_reinit(xo);
|
||||
res = add_object_id(ni, flags);
|
||||
if (!res) {
|
||||
/* update value and index */
|
||||
res = update_object_id(ni,xo,
|
||||
(const OBJECT_ID_ATTR*)value,
|
||||
size);
|
||||
}
|
||||
} else {
|
||||
/* GUID is present elsewhere */
|
||||
res = -1;
|
||||
errno = EEXIST;
|
||||
}
|
||||
xoni = xo->ni;
|
||||
ntfs_index_entry_mark_dirty(xo);
|
||||
NInoSetDirty(xoni);
|
||||
ntfs_index_ctx_put(xo);
|
||||
ntfs_inode_close(xoni);
|
||||
} else {
|
||||
res = -1;
|
||||
}
|
||||
} else {
|
||||
errno = EINVAL;
|
||||
res = -1;
|
||||
}
|
||||
return (res ? -1 : 0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Remove the object id
|
||||
*
|
||||
* Returns 0, or -1 if there is a problem
|
||||
*/
|
||||
|
||||
int ntfs_remove_ntfs_object_id(ntfs_inode *ni)
|
||||
{
|
||||
int res;
|
||||
int olderrno;
|
||||
ntfs_attr *na;
|
||||
ntfs_inode *xoni;
|
||||
ntfs_index_context *xo;
|
||||
int oldsize;
|
||||
OBJECT_ID_ATTR old_attr;
|
||||
|
||||
res = 0;
|
||||
if (ni) {
|
||||
/*
|
||||
* open and delete the object id
|
||||
*/
|
||||
na = ntfs_attr_open(ni, AT_OBJECT_ID,
|
||||
AT_UNNAMED,0);
|
||||
if (na) {
|
||||
/* first remove index (old object id needed) */
|
||||
xo = open_object_id_index(ni->vol);
|
||||
if (xo) {
|
||||
oldsize = remove_object_id_index(na,xo,
|
||||
&old_attr);
|
||||
if (oldsize < 0) {
|
||||
res = -1;
|
||||
} else {
|
||||
/* now remove attribute */
|
||||
res = ntfs_attr_rm(na);
|
||||
if (res
|
||||
&& (oldsize > (int)sizeof(GUID))) {
|
||||
/*
|
||||
* If we could not remove the
|
||||
* attribute, try to restore the
|
||||
* index and log the error. There
|
||||
* will be an inconsistency if
|
||||
* the reindexing fails.
|
||||
*/
|
||||
set_object_id_index(ni, xo,
|
||||
&old_attr);
|
||||
ntfs_log_error(
|
||||
"Failed to remove object id."
|
||||
" Possible corruption.\n");
|
||||
}
|
||||
}
|
||||
|
||||
xoni = xo->ni;
|
||||
ntfs_index_entry_mark_dirty(xo);
|
||||
NInoSetDirty(xoni);
|
||||
ntfs_index_ctx_put(xo);
|
||||
ntfs_inode_close(xoni);
|
||||
}
|
||||
olderrno = errno;
|
||||
ntfs_attr_close(na);
|
||||
/* avoid errno pollution */
|
||||
if (errno == ENOENT)
|
||||
errno = olderrno;
|
||||
} else {
|
||||
errno = ENODATA;
|
||||
res = -1;
|
||||
}
|
||||
NInoSetDirty(ni);
|
||||
} else {
|
||||
errno = EINVAL;
|
||||
res = -1;
|
||||
}
|
||||
return (res ? -1 : 0);
|
||||
}
|
||||
|
||||
#endif /* HAVE_SETXATTR */
|
35
src/add-ons/kernel/file_systems/ntfs/libntfs/object_id.h
Normal file
35
src/add-ons/kernel/file_systems/ntfs/libntfs/object_id.h
Normal file
@ -0,0 +1,35 @@
|
||||
/*
|
||||
*
|
||||
* 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 OBJECT_ID_H
|
||||
#define OBJECT_ID_H
|
||||
|
||||
int ntfs_get_ntfs_object_id(ntfs_inode *ni, char *value, size_t size);
|
||||
|
||||
int ntfs_set_ntfs_object_id(ntfs_inode *ni, const char *value,
|
||||
size_t size, int flags);
|
||||
int ntfs_remove_ntfs_object_id(ntfs_inode *ni);
|
||||
|
||||
int ntfs_delete_object_id_index(ntfs_inode *ni);
|
||||
|
||||
#endif /* OBJECT_ID_H */
|
82
src/add-ons/kernel/file_systems/ntfs/libntfs/param.h
Normal file
82
src/add-ons/kernel/file_systems/ntfs/libntfs/param.h
Normal file
@ -0,0 +1,82 @@
|
||||
/*
|
||||
* param.h - Parameter values for ntfs-3g
|
||||
*
|
||||
* 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
|
||||
*/
|
||||
|
||||
#ifndef _NTFS_PARAM_H
|
||||
#define _NTFS_PARAM_H
|
||||
|
||||
#define CACHE_INODE_SIZE 32 /* inode cache, zero or >= 3 and not too big */
|
||||
#define CACHE_NIDATA_SIZE 64 /* idata cache, zero or >= 3 and not too big */
|
||||
#define CACHE_LOOKUP_SIZE 64 /* lookup 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 */
|
||||
|
||||
#define FORCE_FORMAT_v1x 0 /* Insert security data as in NTFS v1.x */
|
||||
#define OWNERFROMACL 1 /* Get the owner from ACL (not Windows owner) */
|
||||
|
||||
/* default security sub-authorities */
|
||||
enum {
|
||||
DEFSECAUTH1 = -1153374643, /* 3141592653 */
|
||||
DEFSECAUTH2 = 589793238,
|
||||
DEFSECAUTH3 = 462843383,
|
||||
DEFSECBASE = 10000
|
||||
};
|
||||
|
||||
/*
|
||||
* Parameters for compression
|
||||
*/
|
||||
|
||||
/* default option for compression */
|
||||
#define DEFAULT_COMPRESSION FALSE
|
||||
/* (log2 of) number of clusters in a compression block for new files */
|
||||
#define STANDARD_COMPRESSION_UNIT 4
|
||||
/* maximum cluster size for allowing compression for new files */
|
||||
#define MAX_COMPRESSION_CLUSTER_SIZE 4096
|
||||
|
||||
/*
|
||||
* Permission checking modes for high level and low level
|
||||
*
|
||||
* The choices for high and low lowel are independent, they have
|
||||
* no effect on the library
|
||||
*
|
||||
* Stick to the recommended values unless you understand the consequences
|
||||
* on protection and performances. Use of cacheing is good for
|
||||
* performances, but bad on security.
|
||||
*
|
||||
* Possible values for high level :
|
||||
* 1 : no cache, kernel control (recommended)
|
||||
* 4 : no cache, file system control
|
||||
* 7 : no cache, kernel control for ACLs
|
||||
*
|
||||
* Possible values for low level :
|
||||
* 2 : no cache, kernel control
|
||||
* 3 : use kernel/fuse cache, kernel control
|
||||
* 5 : no cache, file system control (recommended)
|
||||
* 8 : no cache, kernel control for ACLs
|
||||
*
|
||||
* Use of options 7 and 8 requires a patch to fuse
|
||||
* When Posix ACLs are selected in the configure options, a value
|
||||
* of 6 is added in the mount report.
|
||||
*/
|
||||
|
||||
#define HPERMSCONFIG 1
|
||||
#define LPERMSCONFIG 5
|
||||
|
||||
#endif /* defined _NTFS_PARAM_H */
|
1222
src/add-ons/kernel/file_systems/ntfs/libntfs/reparse.c
Normal file
1222
src/add-ons/kernel/file_systems/ntfs/libntfs/reparse.c
Normal file
File diff suppressed because it is too large
Load Diff
39
src/add-ons/kernel/file_systems/ntfs/libntfs/reparse.h
Normal file
39
src/add-ons/kernel/file_systems/ntfs/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(ntfs_inode *ni, const char *mnt_point,
|
||||
int *pattr_size);
|
||||
BOOL ntfs_possible_symlink(ntfs_inode *ni);
|
||||
|
||||
int ntfs_get_ntfs_reparse_data(ntfs_inode *ni, char *value, size_t size);
|
||||
|
||||
int ntfs_set_ntfs_reparse_data(ntfs_inode *ni, const char *value,
|
||||
size_t size, int flags);
|
||||
int ntfs_remove_ntfs_reparse_data(ntfs_inode *ni);
|
||||
|
||||
int ntfs_delete_reparse_index(ntfs_inode *ni);
|
||||
|
||||
#endif /* REPARSE_H */
|
@ -5,6 +5,7 @@
|
||||
* Copyright (c) 2002-2005 Richard Russon
|
||||
* Copyright (c) 2002-2008 Szabolcs Szakacsits
|
||||
* Copyright (c) 2004 Yura Pakhuchiy
|
||||
* Copyright (c) 2007-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
|
||||
@ -110,6 +111,42 @@ static runlist_element *ntfs_rl_realloc(runlist_element *rl, int old_size,
|
||||
return realloc(rl, new_size);
|
||||
}
|
||||
|
||||
/*
|
||||
* Extend a runlist by some entry count
|
||||
* The runlist may have to be reallocated
|
||||
*
|
||||
* Returns the reallocated runlist
|
||||
* or NULL if reallocation was not possible (with errno set)
|
||||
* the runlist is left unchanged if the reallocation fails
|
||||
*/
|
||||
|
||||
runlist_element *ntfs_rl_extend(ntfs_attr *na, runlist_element *rl,
|
||||
int more_entries)
|
||||
{
|
||||
runlist_element *newrl;
|
||||
int last;
|
||||
int irl;
|
||||
|
||||
if (na->rl && rl) {
|
||||
irl = (int)(rl - na->rl);
|
||||
last = irl;
|
||||
while (na->rl[last].length)
|
||||
last++;
|
||||
newrl = ntfs_rl_realloc(na->rl,last+1,last+more_entries+1);
|
||||
if (!newrl) {
|
||||
errno = ENOMEM;
|
||||
rl = (runlist_element*)NULL;
|
||||
} else
|
||||
na->rl = newrl;
|
||||
rl = &newrl[irl];
|
||||
} else {
|
||||
ntfs_log_error("Cannot extend unmapped runlist");
|
||||
errno = EIO;
|
||||
rl = (runlist_element*)NULL;
|
||||
}
|
||||
return (rl);
|
||||
}
|
||||
|
||||
/**
|
||||
* ntfs_rl_are_mergeable - test if two runlists can be joined together
|
||||
* @dst: original runlist
|
||||
@ -742,7 +779,7 @@ runlist_element *ntfs_runlists_merge(runlist_element *drl,
|
||||
* two into one, if that is possible (we check for overlap and discard the new
|
||||
* runlist if overlap present before returning NULL, with errno = ERANGE).
|
||||
*/
|
||||
runlist_element *ntfs_mapping_pairs_decompress_i(const ntfs_volume *vol,
|
||||
static runlist_element *ntfs_mapping_pairs_decompress_i(const ntfs_volume *vol,
|
||||
const ATTR_RECORD *attr, runlist_element *old_rl)
|
||||
{
|
||||
VCN vcn; /* Current vcn. */
|
||||
@ -1124,8 +1161,9 @@ rl_err_out:
|
||||
/**
|
||||
* ntfs_rl_pwrite - scatter write to disk
|
||||
* @vol: ntfs volume to write to
|
||||
* @rl: runlist specifying where to write the data to
|
||||
* @pos: byte position within runlist @rl at which to begin the write
|
||||
* @rl: runlist entry specifying where to write the data to
|
||||
* @ofs: offset in file for runlist element indicated in @rl
|
||||
* @pos: byte position from runlist beginning at which to begin the write
|
||||
* @count: number of bytes to write
|
||||
* @b: data buffer to write to disk
|
||||
*
|
||||
@ -1144,9 +1182,9 @@ rl_err_out:
|
||||
* of invalid arguments.
|
||||
*/
|
||||
s64 ntfs_rl_pwrite(const ntfs_volume *vol, const runlist_element *rl,
|
||||
const s64 pos, s64 count, void *b)
|
||||
s64 ofs, const s64 pos, s64 count, void *b)
|
||||
{
|
||||
s64 written, to_write, ofs, total = 0;
|
||||
s64 written, to_write, total = 0;
|
||||
int err = EIO;
|
||||
|
||||
if (!vol || !rl || pos < 0 || count < 0) {
|
||||
@ -1159,9 +1197,11 @@ s64 ntfs_rl_pwrite(const ntfs_volume *vol, const runlist_element *rl,
|
||||
if (!count)
|
||||
goto out;
|
||||
/* Seek in @rl to the run containing @pos. */
|
||||
for (ofs = 0; rl->length && (ofs + (rl->length <<
|
||||
vol->cluster_size_bits) <= pos); rl++)
|
||||
while (rl->length && (ofs + (rl->length <<
|
||||
vol->cluster_size_bits) <= pos)) {
|
||||
ofs += (rl->length << vol->cluster_size_bits);
|
||||
rl++;
|
||||
}
|
||||
/* Offset in the run at which to begin writing. */
|
||||
ofs = pos - ofs;
|
||||
for (total = 0LL; count; rl++, ofs = 0) {
|
||||
@ -1230,19 +1270,18 @@ errno_set:
|
||||
*/
|
||||
int ntfs_get_nr_significant_bytes(const s64 n)
|
||||
{
|
||||
s64 l = n;
|
||||
u64 l;
|
||||
int i;
|
||||
s8 j;
|
||||
|
||||
i = 0;
|
||||
do {
|
||||
l >>= 8;
|
||||
i++;
|
||||
} while (l != 0LL && l != -1LL);
|
||||
j = (n >> 8 * (i - 1)) & 0xff;
|
||||
/* If the sign bit is wrong, we need an extra byte. */
|
||||
if ((n < 0LL && j >= 0) || (n > 0LL && j < 0))
|
||||
i++;
|
||||
l = (n < 0 ? ~n : n);
|
||||
i = 1;
|
||||
if (l >= 128) {
|
||||
l >>= 7;
|
||||
do {
|
||||
i++;
|
||||
l >>= 8;
|
||||
} while (l);
|
||||
}
|
||||
return i;
|
||||
}
|
||||
|
||||
@ -1267,7 +1306,7 @@ int ntfs_get_nr_significant_bytes(const s64 n)
|
||||
* EIO - The runlist is corrupt.
|
||||
*/
|
||||
int ntfs_get_size_for_mapping_pairs(const ntfs_volume *vol,
|
||||
const runlist_element *rl, const VCN start_vcn)
|
||||
const runlist_element *rl, const VCN start_vcn, int max_size)
|
||||
{
|
||||
LCN prev_lcn;
|
||||
int rls;
|
||||
@ -1326,7 +1365,7 @@ int ntfs_get_size_for_mapping_pairs(const ntfs_volume *vol,
|
||||
rl++;
|
||||
}
|
||||
/* Do the full runs. */
|
||||
for (; rl->length; rl++) {
|
||||
for (; rl->length && (rls <= max_size); rl++) {
|
||||
if (rl->length < 0 || rl->lcn < LCN_HOLE)
|
||||
goto err_out;
|
||||
/* Header byte + length. */
|
||||
@ -1441,7 +1480,7 @@ err_out:
|
||||
*/
|
||||
int ntfs_mapping_pairs_build(const ntfs_volume *vol, u8 *dst,
|
||||
const int dst_len, const runlist_element *rl,
|
||||
const VCN start_vcn, VCN *const stop_vcn)
|
||||
const VCN start_vcn, runlist_element const **stop_rl)
|
||||
{
|
||||
LCN prev_lcn;
|
||||
u8 *dst_max, *dst_next;
|
||||
@ -1453,8 +1492,8 @@ int ntfs_mapping_pairs_build(const ntfs_volume *vol, u8 *dst,
|
||||
if (!rl) {
|
||||
if (start_vcn)
|
||||
goto val_err;
|
||||
if (stop_vcn)
|
||||
*stop_vcn = 0;
|
||||
if (stop_rl)
|
||||
*stop_rl = rl;
|
||||
if (dst_len < 1)
|
||||
goto nospc_err;
|
||||
goto ok;
|
||||
@ -1549,8 +1588,8 @@ int ntfs_mapping_pairs_build(const ntfs_volume *vol, u8 *dst,
|
||||
dst += 1 + len_len + lcn_len;
|
||||
}
|
||||
/* Set stop vcn. */
|
||||
if (stop_vcn)
|
||||
*stop_vcn = rl->vcn;
|
||||
if (stop_rl)
|
||||
*stop_rl = rl;
|
||||
ok:
|
||||
/* Add terminator byte. */
|
||||
*dst = 0;
|
||||
@ -1558,8 +1597,8 @@ out:
|
||||
return ret;
|
||||
size_err:
|
||||
/* Set stop vcn. */
|
||||
if (stop_vcn)
|
||||
*stop_vcn = rl->vcn;
|
||||
if (stop_rl)
|
||||
*stop_rl = rl;
|
||||
/* Add terminator byte. */
|
||||
*dst = 0;
|
||||
nospc_err:
|
||||
@ -1598,7 +1637,11 @@ int ntfs_rl_truncate(runlist **arl, const VCN start_vcn)
|
||||
|
||||
if (!arl || !*arl) {
|
||||
errno = EINVAL;
|
||||
ntfs_log_perror("rl_truncate error: arl: %p *arl: %p", arl, *arl);
|
||||
if (!arl)
|
||||
ntfs_log_perror("rl_truncate error: arl: %p", arl);
|
||||
else
|
||||
ntfs_log_perror("rl_truncate error:"
|
||||
" arl: %p *arl: %p", arl, *arl);
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
@ -49,12 +49,15 @@ struct _runlist_element {/* In memory vcn to lcn mapping structure element. */
|
||||
s64 length; /* Run length in clusters. */
|
||||
};
|
||||
|
||||
extern runlist_element *ntfs_rl_extend(ntfs_attr *na, 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,
|
||||
const s64 pos, s64 count, void *b);
|
||||
s64 ofs, const s64 pos, s64 count, void *b);
|
||||
|
||||
extern runlist_element *ntfs_runlists_merge(runlist_element *drl,
|
||||
runlist_element *srl);
|
||||
@ -65,14 +68,14 @@ extern runlist_element *ntfs_mapping_pairs_decompress(const ntfs_volume *vol,
|
||||
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);
|
||||
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, VCN *const stop_vcn);
|
||||
const VCN start_vcn, runlist_element const **stop_rl);
|
||||
|
||||
extern int ntfs_rl_truncate(runlist **arl, const VCN start_vcn);
|
||||
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -4,6 +4,7 @@
|
||||
*
|
||||
* 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
|
||||
@ -27,8 +28,192 @@
|
||||
#include "types.h"
|
||||
#include "layout.h"
|
||||
#include "inode.h"
|
||||
#include "dir.h"
|
||||
|
||||
extern const GUID *const zero_guid;
|
||||
#ifndef POSIXACLS
|
||||
#define POSIXACLS 0
|
||||
#endif
|
||||
|
||||
typedef u16 be16;
|
||||
typedef u32 be32;
|
||||
|
||||
#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;
|
||||
struct CACHED_PERMISSIONS_LEGACY *previous;
|
||||
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;
|
||||
struct CACHED_SECURID *previous;
|
||||
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;
|
||||
} __attribute__((__packed__));
|
||||
|
||||
struct POSIX_ACL {
|
||||
u8 version;
|
||||
u8 flags;
|
||||
u16 filler;
|
||||
struct POSIX_ACE ace[0];
|
||||
} __attribute__((__packed__));
|
||||
|
||||
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);
|
||||
@ -58,4 +243,115 @@ 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,
|
||||
BOOL allowdef);
|
||||
int ntfs_get_owner_mode(struct SECURITY_CONTEXT *scx,
|
||||
ntfs_inode *ni, struct stat*);
|
||||
int ntfs_set_mode(struct SECURITY_CONTEXT *scx, ntfs_inode *ni, mode_t mode);
|
||||
BOOL ntfs_allowed_as_owner(struct SECURITY_CONTEXT *scx, ntfs_inode *ni);
|
||||
int ntfs_allowed_access(struct SECURITY_CONTEXT *scx,
|
||||
ntfs_inode *ni, int accesstype);
|
||||
BOOL old_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, 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, ntfs_inode *ni,
|
||||
uid_t uid, gid_t gid);
|
||||
int ntfs_set_ownmod(struct SECURITY_CONTEXT *scx,
|
||||
ntfs_inode *ni, uid_t uid, gid_t gid, mode_t mode);
|
||||
#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,
|
||||
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,
|
||||
ntfs_inode *dir_ni, mode_t mode);
|
||||
int ntfs_get_posix_acl(struct SECURITY_CONTEXT *scx, ntfs_inode *ni,
|
||||
const char *name, char *value, size_t size);
|
||||
int ntfs_set_posix_acl(struct SECURITY_CONTEXT *scx, ntfs_inode *ni,
|
||||
const char *name, const char *value, size_t size,
|
||||
int flags);
|
||||
int ntfs_remove_posix_acl(struct SECURITY_CONTEXT *scx, ntfs_inode *ni,
|
||||
const char *name);
|
||||
#endif
|
||||
|
||||
int ntfs_get_ntfs_acl(struct SECURITY_CONTEXT *scx, ntfs_inode *ni,
|
||||
char *value, size_t size);
|
||||
int ntfs_set_ntfs_acl(struct SECURITY_CONTEXT *scx, ntfs_inode *ni,
|
||||
const char *value, size_t size, int flags);
|
||||
|
||||
int ntfs_get_ntfs_attrib(ntfs_inode *ni, char *value, size_t size);
|
||||
int ntfs_set_ntfs_attrib(ntfs_inode *ni,
|
||||
const char *value, size_t size, int flags);
|
||||
|
||||
|
||||
/*
|
||||
* 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 */
|
||||
|
@ -45,6 +45,12 @@
|
||||
#include <locale.h>
|
||||
#endif
|
||||
|
||||
#if defined(__APPLE__) || defined(__DARWIN__)
|
||||
#ifdef ENABLE_NFCONV
|
||||
#include <CoreFoundation/CoreFoundation.h>
|
||||
#endif /* ENABLE_NFCONV */
|
||||
#endif /* defined(__APPLE__) || defined(__DARWIN__) */
|
||||
|
||||
#include "compat.h"
|
||||
#include "attrib.h"
|
||||
#include "types.h"
|
||||
@ -71,8 +77,21 @@
|
||||
* All these routines assume that the Unicode characters are in little endian
|
||||
* encoding inside the strings!!!
|
||||
*/
|
||||
|
||||
static int use_utf8 = 1; /* use UTF-8 encoding for file names */
|
||||
|
||||
#if defined(__APPLE__) || defined(__DARWIN__)
|
||||
#ifdef ENABLE_NFCONV
|
||||
/**
|
||||
* This variable controls whether or not automatic normalization form conversion
|
||||
* should be performed when translating NTFS unicode file names to UTF-8.
|
||||
* Defaults to on, but can be controlled from the outside using the function
|
||||
* int ntfs_macosx_normalize_filenames(int normalize);
|
||||
*/
|
||||
static int nfconvert_utf8 = 1;
|
||||
#endif /* ENABLE_NFCONV */
|
||||
#endif /* defined(__APPLE__) || defined(__DARWIN__) */
|
||||
|
||||
/*
|
||||
* This is used by the name collation functions to quickly determine what
|
||||
* characters are (in)valid.
|
||||
@ -122,34 +141,30 @@ BOOL ntfs_names_are_equal(const ntfschar *s1, size_t s1_len,
|
||||
TRUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* ntfs_names_collate - collate two Unicode names
|
||||
/*
|
||||
* ntfs_names_full_collate() fully collate two Unicode names
|
||||
*
|
||||
* @name1: first Unicode name to compare
|
||||
* @name1_len: length of first Unicode name to compare
|
||||
* @name2: second Unicode name to compare
|
||||
* @name2_len: length of second Unicode name to compare
|
||||
* @err_val: if @name1 contains an invalid character return this value
|
||||
* @ic: either CASE_SENSITIVE or IGNORE_CASE
|
||||
* @upcase: upcase table (ignored if @ic is CASE_SENSITIVE)
|
||||
* @upcase_len: upcase table size (ignored if @ic is CASE_SENSITIVE)
|
||||
*
|
||||
* ntfs_names_collate() collates two Unicode names and returns:
|
||||
*
|
||||
* -1 if the first name collates before the second one,
|
||||
* 0 if the names match,
|
||||
* 1 if the second name collates before the first one, or
|
||||
* @err_val if an invalid character is found in @name1 during the comparison.
|
||||
*
|
||||
* The following characters are considered invalid: '"', '*', '<', '>' and '?'.
|
||||
*/
|
||||
int ntfs_names_collate(const ntfschar *name1, const u32 name1_len,
|
||||
int ntfs_names_full_collate(const ntfschar *name1, const u32 name1_len,
|
||||
const ntfschar *name2, const u32 name2_len,
|
||||
const int err_val __attribute__((unused)),
|
||||
const IGNORE_CASE_BOOL ic, const ntfschar *upcase,
|
||||
const u32 upcase_len)
|
||||
{
|
||||
u32 cnt;
|
||||
ntfschar c1, c2;
|
||||
u16 c1, c2;
|
||||
u16 u1, u2;
|
||||
|
||||
#ifdef DEBUG
|
||||
if (!name1 || !name2 || (ic && (!upcase || !upcase_len))) {
|
||||
@ -157,37 +172,71 @@ int ntfs_names_collate(const ntfschar *name1, const u32 name1_len,
|
||||
exit(1);
|
||||
}
|
||||
#endif
|
||||
for (cnt = 0; cnt < min(name1_len, name2_len); ++cnt) {
|
||||
c1 = le16_to_cpu(*name1);
|
||||
name1++;
|
||||
c2 = le16_to_cpu(*name2);
|
||||
name2++;
|
||||
if (ic) {
|
||||
if (c1 < upcase_len)
|
||||
c1 = le16_to_cpu(upcase[c1]);
|
||||
if (c2 < upcase_len)
|
||||
c2 = le16_to_cpu(upcase[c2]);
|
||||
cnt = min(name1_len, name2_len);
|
||||
if (cnt > 0) {
|
||||
if (ic == CASE_SENSITIVE) {
|
||||
do {
|
||||
c1 = le16_to_cpu(*name1);
|
||||
name1++;
|
||||
c2 = le16_to_cpu(*name2);
|
||||
name2++;
|
||||
} while (--cnt && (c1 == c2));
|
||||
u1 = c1;
|
||||
u2 = c2;
|
||||
if (u1 < upcase_len)
|
||||
u1 = le16_to_cpu(upcase[u1]);
|
||||
if (u2 < upcase_len)
|
||||
u2 = le16_to_cpu(upcase[u2]);
|
||||
if ((u1 == u2) && cnt)
|
||||
do {
|
||||
u1 = le16_to_cpu(*name1);
|
||||
name1++;
|
||||
u2 = le16_to_cpu(*name2);
|
||||
name2++;
|
||||
if (u1 < upcase_len)
|
||||
u1 = le16_to_cpu(upcase[u1]);
|
||||
if (u2 < upcase_len)
|
||||
u2 = le16_to_cpu(upcase[u2]);
|
||||
} while ((u1 == u2) && --cnt);
|
||||
if (u1 < u2)
|
||||
return -1;
|
||||
if (u1 > u2)
|
||||
return 1;
|
||||
if (name1_len < name2_len)
|
||||
return -1;
|
||||
if (name1_len > name2_len)
|
||||
return 1;
|
||||
if (c1 < c2)
|
||||
return -1;
|
||||
if (c1 > c2)
|
||||
return 1;
|
||||
} else {
|
||||
do {
|
||||
u1 = c1 = le16_to_cpu(*name1);
|
||||
name1++;
|
||||
u2 = c2 = le16_to_cpu(*name2);
|
||||
name2++;
|
||||
if (u1 < upcase_len)
|
||||
u1 = le16_to_cpu(upcase[u1]);
|
||||
if (u2 < upcase_len)
|
||||
u2 = le16_to_cpu(upcase[u2]);
|
||||
} while ((u1 == u2) && --cnt);
|
||||
if (u1 < u2)
|
||||
return -1;
|
||||
if (u1 > u2)
|
||||
return 1;
|
||||
if (name1_len < name2_len)
|
||||
return -1;
|
||||
if (name1_len > name2_len)
|
||||
return 1;
|
||||
}
|
||||
#if 0
|
||||
if (c1 < 64 && legal_ansi_char_array[c1] & 8)
|
||||
return err_val;
|
||||
#endif
|
||||
if (c1 < c2)
|
||||
} else {
|
||||
if (name1_len < name2_len)
|
||||
return -1;
|
||||
if (c1 > c2)
|
||||
if (name1_len > name2_len)
|
||||
return 1;
|
||||
}
|
||||
if (name1_len < name2_len)
|
||||
return -1;
|
||||
if (name1_len == name2_len)
|
||||
return 0;
|
||||
/* name1_len > name2_len */
|
||||
#if 0
|
||||
c1 = le16_to_cpu(*name1);
|
||||
if (c1 < 64 && legal_ansi_char_array[c1] & 8)
|
||||
return err_val;
|
||||
#endif
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -249,7 +298,7 @@ int ntfs_ucsncmp(const ntfschar *s1, const ntfschar *s2, size_t n)
|
||||
int ntfs_ucsncasecmp(const ntfschar *s1, const ntfschar *s2, size_t n,
|
||||
const ntfschar *upcase, const u32 upcase_size)
|
||||
{
|
||||
ntfschar c1, c2;
|
||||
u16 c1, c2;
|
||||
size_t i;
|
||||
|
||||
#ifdef DEBUG
|
||||
@ -342,13 +391,28 @@ void ntfs_name_upcase(ntfschar *name, u32 name_len, const ntfschar *upcase,
|
||||
const u32 upcase_len)
|
||||
{
|
||||
u32 i;
|
||||
ntfschar u;
|
||||
u16 u;
|
||||
|
||||
for (i = 0; i < name_len; i++)
|
||||
if ((u = le16_to_cpu(name[i])) < upcase_len)
|
||||
name[i] = upcase[u];
|
||||
}
|
||||
|
||||
/**
|
||||
* ntfs_name_locase - Map a Unicode name to its lowercase equivalent
|
||||
*/
|
||||
void ntfs_name_locase(ntfschar *name, u32 name_len, const ntfschar *locase,
|
||||
const u32 locase_len)
|
||||
{
|
||||
u32 i;
|
||||
u16 u;
|
||||
|
||||
if (locase)
|
||||
for (i = 0; i < name_len; i++)
|
||||
if ((u = le16_to_cpu(name[i])) < locase_len)
|
||||
name[i] = locase[u];
|
||||
}
|
||||
|
||||
/**
|
||||
* ntfs_file_value_upcase - Convert a filename to upper case
|
||||
* @file_name_attr:
|
||||
@ -366,31 +430,6 @@ void ntfs_file_value_upcase(FILE_NAME_ATTR *file_name_attr,
|
||||
file_name_attr->file_name_length, upcase, upcase_len);
|
||||
}
|
||||
|
||||
/**
|
||||
* ntfs_file_values_compare - Which of two filenames should be listed first
|
||||
* @file_name_attr1:
|
||||
* @file_name_attr2:
|
||||
* @err_val:
|
||||
* @ic:
|
||||
* @upcase:
|
||||
* @upcase_len:
|
||||
*
|
||||
* Description...
|
||||
*
|
||||
* Returns:
|
||||
*/
|
||||
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)
|
||||
{
|
||||
return ntfs_names_collate((ntfschar*)&file_name_attr1->file_name,
|
||||
file_name_attr1->file_name_length,
|
||||
(ntfschar*)&file_name_attr2->file_name,
|
||||
file_name_attr2->file_name_length,
|
||||
err_val, ic, upcase, upcase_len);
|
||||
}
|
||||
|
||||
/*
|
||||
NTFS uses Unicode (UTF-16LE [NTFS-3G uses UCS-2LE, which is enough
|
||||
for now]) for path names, but the Unicode code points need to be
|
||||
@ -481,9 +520,16 @@ fail:
|
||||
static int ntfs_utf16_to_utf8(const ntfschar *ins, const int ins_len,
|
||||
char **outs, int outs_len)
|
||||
{
|
||||
#if defined(__APPLE__) || defined(__DARWIN__)
|
||||
#ifdef ENABLE_NFCONV
|
||||
char *original_outs_value = *outs;
|
||||
int original_outs_len = outs_len;
|
||||
#endif /* ENABLE_NFCONV */
|
||||
#endif /* defined(__APPLE__) || defined(__DARWIN__) */
|
||||
|
||||
char *t;
|
||||
int i, size, ret = -1;
|
||||
ntfschar halfpair;
|
||||
int halfpair;
|
||||
|
||||
halfpair = 0;
|
||||
if (!*outs)
|
||||
@ -536,6 +582,36 @@ static int ntfs_utf16_to_utf8(const ntfschar *ins, const int ins_len,
|
||||
}
|
||||
}
|
||||
*t = '\0';
|
||||
|
||||
#if defined(__APPLE__) || defined(__DARWIN__)
|
||||
#ifdef ENABLE_NFCONV
|
||||
if(nfconvert_utf8 && (t - *outs) > 0) {
|
||||
char *new_outs = NULL;
|
||||
int new_outs_len = ntfs_macosx_normalize_utf8(*outs, &new_outs, 0); // Normalize to decomposed form
|
||||
if(new_outs_len >= 0 && new_outs != NULL) {
|
||||
if(original_outs_value != *outs) {
|
||||
// We have allocated outs ourselves.
|
||||
free(*outs);
|
||||
*outs = new_outs;
|
||||
t = *outs + new_outs_len;
|
||||
}
|
||||
else {
|
||||
// We need to copy new_outs into the fixed outs buffer.
|
||||
memset(*outs, 0, original_outs_len);
|
||||
strncpy(*outs, new_outs, original_outs_len-1);
|
||||
t = *outs + original_outs_len;
|
||||
free(new_outs);
|
||||
}
|
||||
}
|
||||
else {
|
||||
ntfs_log_error("Failed to normalize NTFS string to UTF-8 NFD: %s\n", *outs);
|
||||
ntfs_log_error(" new_outs=0x%p\n", new_outs);
|
||||
ntfs_log_error(" new_outs_len=%d\n", new_outs_len);
|
||||
}
|
||||
}
|
||||
#endif /* ENABLE_NFCONV */
|
||||
#endif /* defined(__APPLE__) || defined(__DARWIN__) */
|
||||
|
||||
ret = t - *outs;
|
||||
out:
|
||||
return ret;
|
||||
@ -562,24 +638,26 @@ static int utf8_to_utf16_size(const char *s)
|
||||
while ((byte = *((const unsigned char *)s++))) {
|
||||
if (++count >= PATH_MAX)
|
||||
goto fail;
|
||||
if (byte >= 0xF5) {
|
||||
errno = EILSEQ;
|
||||
goto out;
|
||||
}
|
||||
if (!*s)
|
||||
break;
|
||||
if (byte >= 0xC0)
|
||||
s++;
|
||||
if (!*s)
|
||||
break;
|
||||
if (byte >= 0xE0)
|
||||
s++;
|
||||
if (!*s)
|
||||
break;
|
||||
if (byte >= 0xF0) {
|
||||
s++;
|
||||
if (++count >= PATH_MAX)
|
||||
goto fail;
|
||||
if (byte >= 0xc0) {
|
||||
if (byte >= 0xF5) {
|
||||
errno = EILSEQ;
|
||||
goto out;
|
||||
}
|
||||
if (!*s)
|
||||
break;
|
||||
if (byte >= 0xC0)
|
||||
s++;
|
||||
if (!*s)
|
||||
break;
|
||||
if (byte >= 0xE0)
|
||||
s++;
|
||||
if (!*s)
|
||||
break;
|
||||
if (byte >= 0xF0) {
|
||||
s++;
|
||||
if (++count >= PATH_MAX)
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
}
|
||||
ret = count;
|
||||
@ -611,8 +689,6 @@ static int utf8_to_unicode(u32 *wc, const char *s)
|
||||
} else if (byte < 0xc2) {
|
||||
goto fail;
|
||||
} else if (byte < 0xE0) {
|
||||
if (strlen(s) < 2)
|
||||
goto fail;
|
||||
if ((s[1] & 0xC0) == 0x80) {
|
||||
*wc = ((u32)(byte & 0x1F) << 6)
|
||||
| ((u32)(s[1] & 0x3F));
|
||||
@ -621,8 +697,6 @@ static int utf8_to_unicode(u32 *wc, const char *s)
|
||||
goto fail;
|
||||
/* three-byte */
|
||||
} else if (byte < 0xF0) {
|
||||
if (strlen(s) < 3)
|
||||
goto fail;
|
||||
if (((s[1] & 0xC0) == 0x80) && ((s[2] & 0xC0) == 0x80)) {
|
||||
*wc = ((u32)(byte & 0x0F) << 12)
|
||||
| ((u32)(s[1] & 0x3F) << 6)
|
||||
@ -641,8 +715,6 @@ static int utf8_to_unicode(u32 *wc, const char *s)
|
||||
goto fail;
|
||||
/* four-byte */
|
||||
} else if (byte < 0xF5) {
|
||||
if (strlen(s) < 4)
|
||||
goto fail;
|
||||
if (((s[1] & 0xC0) == 0x80) && ((s[2] & 0xC0) == 0x80)
|
||||
&& ((s[3] & 0xC0) == 0x80)) {
|
||||
*wc = ((u32)(byte & 0x07) << 18)
|
||||
@ -670,8 +742,22 @@ fail:
|
||||
*/
|
||||
static int ntfs_utf8_to_utf16(const char *ins, ntfschar **outs)
|
||||
{
|
||||
#if defined(__APPLE__) || defined(__DARWIN__)
|
||||
#ifdef ENABLE_NFCONV
|
||||
char *new_ins = NULL;
|
||||
if(nfconvert_utf8) {
|
||||
int new_ins_len;
|
||||
new_ins_len = ntfs_macosx_normalize_utf8(ins, &new_ins, 1); // Normalize to composed form
|
||||
if(new_ins_len >= 0)
|
||||
ins = new_ins;
|
||||
else
|
||||
ntfs_log_error("Failed to normalize NTFS string to UTF-8 NFC: %s\n", ins);
|
||||
}
|
||||
#endif /* ENABLE_NFCONV */
|
||||
#endif /* defined(__APPLE__) || defined(__DARWIN__) */
|
||||
const char *t = ins;
|
||||
u32 wc;
|
||||
BOOL allocated;
|
||||
ntfschar *outpos;
|
||||
int shorts, ret = -1;
|
||||
|
||||
@ -679,18 +765,30 @@ static int ntfs_utf8_to_utf16(const char *ins, ntfschar **outs)
|
||||
if (shorts < 0)
|
||||
goto fail;
|
||||
|
||||
allocated = FALSE;
|
||||
if (!*outs) {
|
||||
*outs = ntfs_malloc((shorts + 1) * sizeof(ntfschar));
|
||||
if (!*outs)
|
||||
goto fail;
|
||||
allocated = TRUE;
|
||||
}
|
||||
|
||||
outpos = *outs;
|
||||
|
||||
while(1) {
|
||||
int m = utf8_to_unicode(&wc, t);
|
||||
if (m < 0)
|
||||
goto fail;
|
||||
if (m <= 0) {
|
||||
if (m < 0) {
|
||||
/* do not leave space allocated if failed */
|
||||
if (allocated) {
|
||||
free(*outs);
|
||||
*outs = (ntfschar*)NULL;
|
||||
}
|
||||
goto fail;
|
||||
}
|
||||
*outpos++ = const_cpu_to_le16(0);
|
||||
break;
|
||||
}
|
||||
if (wc < 0x10000)
|
||||
*outpos++ = cpu_to_le16(wc);
|
||||
else {
|
||||
@ -698,13 +796,17 @@ static int ntfs_utf8_to_utf16(const char *ins, ntfschar **outs)
|
||||
*outpos++ = cpu_to_le16((wc >> 10) + 0xd800);
|
||||
*outpos++ = cpu_to_le16((wc & 0x3ff) + 0xdc00);
|
||||
}
|
||||
if (m == 0)
|
||||
break;
|
||||
t += m;
|
||||
}
|
||||
|
||||
ret = --outpos - *outs;
|
||||
fail:
|
||||
#if defined(__APPLE__) || defined(__DARWIN__)
|
||||
#ifdef ENABLE_NFCONV
|
||||
if(new_ins != NULL)
|
||||
free(new_ins);
|
||||
#endif /* ENABLE_NFCONV */
|
||||
#endif /* defined(__APPLE__) || defined(__DARWIN__) */
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -737,12 +839,15 @@ int ntfs_ucstombs(const ntfschar *ins, const int ins_len, char **outs,
|
||||
int outs_len)
|
||||
{
|
||||
char *mbs;
|
||||
int mbs_len;
|
||||
#ifdef MB_CUR_MAX
|
||||
wchar_t wc;
|
||||
int i, o, mbs_len;
|
||||
int i, o;
|
||||
int cnt = 0;
|
||||
#ifdef HAVE_MBSINIT
|
||||
mbstate_t mbstate;
|
||||
#endif
|
||||
#endif /* MB_CUR_MAX */
|
||||
|
||||
if (!ins || !outs) {
|
||||
errno = EINVAL;
|
||||
@ -756,6 +861,7 @@ int ntfs_ucstombs(const ntfschar *ins, const int ins_len, char **outs,
|
||||
}
|
||||
if (use_utf8)
|
||||
return ntfs_utf16_to_utf8(ins, ins_len, outs, outs_len);
|
||||
#ifdef MB_CUR_MAX
|
||||
if (!mbs) {
|
||||
mbs_len = (ins_len + 1) * MB_CUR_MAX;
|
||||
mbs = ntfs_malloc(mbs_len);
|
||||
@ -821,6 +927,9 @@ err_out:
|
||||
free(mbs);
|
||||
errno = eo;
|
||||
}
|
||||
#else /* MB_CUR_MAX */
|
||||
errno = EILSEQ;
|
||||
#endif /* MB_CUR_MAX */
|
||||
return -1;
|
||||
}
|
||||
|
||||
@ -849,6 +958,7 @@ err_out:
|
||||
*/
|
||||
int ntfs_mbstoucs(const char *ins, ntfschar **outs)
|
||||
{
|
||||
#ifdef MB_CUR_MAX
|
||||
ntfschar *ucs;
|
||||
const char *s;
|
||||
wchar_t wc;
|
||||
@ -856,6 +966,7 @@ int ntfs_mbstoucs(const char *ins, ntfschar **outs)
|
||||
#ifdef HAVE_MBSINIT
|
||||
mbstate_t mbstate;
|
||||
#endif
|
||||
#endif /* MB_CUR_MAX */
|
||||
|
||||
if (!ins || !outs) {
|
||||
errno = EINVAL;
|
||||
@ -865,6 +976,7 @@ int ntfs_mbstoucs(const char *ins, ntfschar **outs)
|
||||
if (use_utf8)
|
||||
return ntfs_utf8_to_utf16(ins, outs);
|
||||
|
||||
#ifdef MB_CUR_MAX
|
||||
/* Determine the size of the multi-byte string in bytes. */
|
||||
ins_size = strlen(ins);
|
||||
/* Determine the length of the multi-byte string. */
|
||||
@ -954,9 +1066,67 @@ int ntfs_mbstoucs(const char *ins, ntfschar **outs)
|
||||
return o;
|
||||
err_out:
|
||||
free(ucs);
|
||||
#else /* MB_CUR_MAX */
|
||||
errno = EILSEQ;
|
||||
#endif /* MB_CUR_MAX */
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Turn a UTF8 name uppercase
|
||||
*
|
||||
* Returns an allocated uppercase name which has to be freed by caller
|
||||
* or NULL if there is an error (described by errno)
|
||||
*/
|
||||
|
||||
char *ntfs_uppercase_mbs(const char *low,
|
||||
const ntfschar *upcase, u32 upcase_size)
|
||||
{
|
||||
int size;
|
||||
char *upp;
|
||||
u32 wc;
|
||||
int n;
|
||||
const char *s;
|
||||
char *t;
|
||||
|
||||
size = strlen(low);
|
||||
upp = (char*)ntfs_malloc(3*size + 1);
|
||||
if (upp) {
|
||||
s = low;
|
||||
t = upp;
|
||||
do {
|
||||
n = utf8_to_unicode(&wc, s);
|
||||
if (n > 0) {
|
||||
if (wc < upcase_size)
|
||||
wc = le16_to_cpu(upcase[wc]);
|
||||
if (wc < 0x80)
|
||||
*t++ = wc;
|
||||
else if (wc < 0x800) {
|
||||
*t++ = (0xc0 | ((wc >> 6) & 0x3f));
|
||||
*t++ = 0x80 | (wc & 0x3f);
|
||||
} else if (wc < 0x10000) {
|
||||
*t++ = 0xe0 | (wc >> 12);
|
||||
*t++ = 0x80 | ((wc >> 6) & 0x3f);
|
||||
*t++ = 0x80 | (wc & 0x3f);
|
||||
} else {
|
||||
*t++ = 0xf0 | ((wc >> 18) & 7);
|
||||
*t++ = 0x80 | ((wc >> 12) & 63);
|
||||
*t++ = 0x80 | ((wc >> 6) & 0x3f);
|
||||
*t++ = 0x80 | (wc & 0x3f);
|
||||
}
|
||||
s += n;
|
||||
}
|
||||
} while (n > 0);
|
||||
if (n < 0) {
|
||||
free(upp);
|
||||
upp = (char*)NULL;
|
||||
errno = EILSEQ;
|
||||
}
|
||||
*t = 0;
|
||||
}
|
||||
return (upp);
|
||||
}
|
||||
|
||||
/**
|
||||
* ntfs_upcase_table_build - build the default upcase table for NTFS
|
||||
* @uc: destination buffer where to store the built table
|
||||
@ -1006,21 +1176,58 @@ void ntfs_upcase_table_build(ntfschar *uc, u32 uc_len)
|
||||
{0}
|
||||
};
|
||||
int i, r;
|
||||
int k, off;
|
||||
|
||||
memset((char*)uc, 0, uc_len);
|
||||
uc_len >>= 1;
|
||||
if (uc_len > 65536)
|
||||
uc_len = 65536;
|
||||
for (i = 0; (u32)i < uc_len; i++)
|
||||
uc[i] = i;
|
||||
for (r = 0; uc_run_table[r][0]; r++)
|
||||
uc[i] = cpu_to_le16(i);
|
||||
for (r = 0; uc_run_table[r][0]; r++) {
|
||||
off = uc_run_table[r][2];
|
||||
for (i = uc_run_table[r][0]; i < uc_run_table[r][1]; i++)
|
||||
uc[i] += uc_run_table[r][2];
|
||||
uc[i] = cpu_to_le16(i + off);
|
||||
}
|
||||
for (r = 0; uc_dup_table[r][0]; r++)
|
||||
for (i = uc_dup_table[r][0]; i < uc_dup_table[r][1]; i += 2)
|
||||
uc[i + 1]--;
|
||||
for (r = 0; uc_byte_table[r][0]; r++)
|
||||
uc[uc_byte_table[r][0]] = uc_byte_table[r][1];
|
||||
uc[i + 1] = cpu_to_le16(i);
|
||||
for (r = 0; uc_byte_table[r][0]; r++) {
|
||||
k = uc_byte_table[r][1];
|
||||
uc[uc_byte_table[r][0]] = cpu_to_le16(k);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Build a table for converting to lower case
|
||||
*
|
||||
* This is only meaningful when there is a single lower case
|
||||
* character leading to an upper case one, and currently the
|
||||
* only exception is the greek letter sigma which has a single
|
||||
* upper case glyph (code U+03A3), but two lower case glyphs
|
||||
* (code U+03C3 and U+03C2, the latter to be used at the end
|
||||
* of a word). In the following implementation the upper case
|
||||
* sigma will be lowercased as U+03C3.
|
||||
*/
|
||||
|
||||
ntfschar *ntfs_locase_table_build(const ntfschar *uc, u32 uc_cnt)
|
||||
{
|
||||
ntfschar *lc;
|
||||
u32 upp;
|
||||
u32 i;
|
||||
|
||||
lc = (ntfschar*)ntfs_malloc(uc_cnt*sizeof(ntfschar));
|
||||
if (lc) {
|
||||
for (i=0; i<uc_cnt; i++)
|
||||
lc[i] = cpu_to_le16(i);
|
||||
for (i=0; i<uc_cnt; i++) {
|
||||
upp = le16_to_cpu(uc[i]);
|
||||
if ((upp != i) && (upp < uc_cnt))
|
||||
lc[upp] = cpu_to_le16(i);
|
||||
}
|
||||
} else
|
||||
ntfs_log_error("Could not build the locase table\n");
|
||||
return (lc);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1074,10 +1281,76 @@ void ntfs_ucsfree(ntfschar *ucs)
|
||||
free(ucs);
|
||||
}
|
||||
|
||||
/*
|
||||
* Check whether a name contains no chars forbidden
|
||||
* for DOS or Win32 use
|
||||
*
|
||||
* If there is a bad char, errno is set to EINVAL
|
||||
*/
|
||||
|
||||
BOOL ntfs_forbidden_chars(const ntfschar *name, int len)
|
||||
{
|
||||
BOOL forbidden;
|
||||
int ch;
|
||||
int i;
|
||||
u32 mainset = (1L << ('\"' - 0x20))
|
||||
| (1L << ('*' - 0x20))
|
||||
| (1L << ('/' - 0x20))
|
||||
| (1L << (':' - 0x20))
|
||||
| (1L << ('<' - 0x20))
|
||||
| (1L << ('>' - 0x20))
|
||||
| (1L << ('?' - 0x20));
|
||||
|
||||
forbidden = (len == 0)
|
||||
|| (le16_to_cpu(name[len-1]) == ' ')
|
||||
|| (le16_to_cpu(name[len-1]) == '.');
|
||||
for (i=0; i<len; i++) {
|
||||
ch = le16_to_cpu(name[i]);
|
||||
if ((ch < 0x20)
|
||||
|| ((ch < 0x40)
|
||||
&& ((1L << (ch - 0x20)) & mainset))
|
||||
|| (ch == '\\')
|
||||
|| (ch == '|'))
|
||||
forbidden = TRUE;
|
||||
}
|
||||
if (forbidden)
|
||||
errno = EINVAL;
|
||||
return (forbidden);
|
||||
}
|
||||
|
||||
/*
|
||||
* Check whether the same name can be used as a DOS and
|
||||
* a Win32 name
|
||||
*
|
||||
* The names must be the same, or the short name the uppercase
|
||||
* variant of the long name
|
||||
*/
|
||||
|
||||
BOOL ntfs_collapsible_chars(ntfs_volume *vol,
|
||||
const ntfschar *shortname, int shortlen,
|
||||
const ntfschar *longname, int longlen)
|
||||
{
|
||||
BOOL collapsible;
|
||||
unsigned int ch;
|
||||
int i;
|
||||
|
||||
collapsible = shortlen == longlen;
|
||||
if (collapsible)
|
||||
for (i=0; i<shortlen; i++) {
|
||||
ch = le16_to_cpu(longname[i]);
|
||||
if ((ch >= vol->upcase_len)
|
||||
|| ((shortname[i] != longname[i])
|
||||
&& (shortname[i] != vol->upcase[ch])))
|
||||
collapsible = FALSE;
|
||||
}
|
||||
return (collapsible);
|
||||
}
|
||||
|
||||
/*
|
||||
* Define the character encoding to be used.
|
||||
* Use UTF-8 unless specified otherwise.
|
||||
*/
|
||||
|
||||
int ntfs_set_char_encoding(const char *locale)
|
||||
{
|
||||
use_utf8 = 0;
|
||||
@ -1093,3 +1366,82 @@ int ntfs_set_char_encoding(const char *locale)
|
||||
}
|
||||
return 0; /* always successful */
|
||||
}
|
||||
|
||||
#if defined(__APPLE__) || defined(__DARWIN__)
|
||||
|
||||
int ntfs_macosx_normalize_filenames(int normalize) {
|
||||
#ifdef ENABLE_NFCONV
|
||||
if(normalize == 0 || normalize == 1) {
|
||||
nfconvert_utf8 = normalize;
|
||||
return 0;
|
||||
}
|
||||
else
|
||||
return -1;
|
||||
#else
|
||||
return -1;
|
||||
#endif /* ENABLE_NFCONV */
|
||||
}
|
||||
|
||||
int ntfs_macosx_normalize_utf8(const char *utf8_string, char **target,
|
||||
int composed) {
|
||||
#ifdef ENABLE_NFCONV
|
||||
/* For this code to compile, the CoreFoundation framework must be fed to the linker. */
|
||||
CFStringRef cfSourceString;
|
||||
CFMutableStringRef cfMutableString;
|
||||
CFRange rangeToProcess;
|
||||
CFIndex requiredBufferLength;
|
||||
char *result = NULL;
|
||||
int resultLength = -1;
|
||||
|
||||
/* Convert the UTF-8 string to a CFString. */
|
||||
cfSourceString = CFStringCreateWithCString(kCFAllocatorDefault, utf8_string, kCFStringEncodingUTF8);
|
||||
if(cfSourceString == NULL) {
|
||||
ntfs_log_error("CFStringCreateWithCString failed!\n");
|
||||
return -2;
|
||||
}
|
||||
|
||||
/* Create a mutable string from cfSourceString that we are free to modify. */
|
||||
cfMutableString = CFStringCreateMutableCopy(kCFAllocatorDefault, 0, cfSourceString);
|
||||
CFRelease(cfSourceString); /* End-of-life. */
|
||||
if(cfMutableString == NULL) {
|
||||
ntfs_log_error("CFStringCreateMutableCopy failed!\n");
|
||||
return -3;
|
||||
}
|
||||
|
||||
/* Normalize the mutable string to the desired normalization form. */
|
||||
CFStringNormalize(cfMutableString, (composed != 0 ? kCFStringNormalizationFormC : kCFStringNormalizationFormD));
|
||||
|
||||
/* Store the resulting string in a '\0'-terminated UTF-8 encoded char* buffer. */
|
||||
rangeToProcess = CFRangeMake(0, CFStringGetLength(cfMutableString));
|
||||
if(CFStringGetBytes(cfMutableString, rangeToProcess, kCFStringEncodingUTF8, 0, false, NULL, 0, &requiredBufferLength) > 0) {
|
||||
resultLength = sizeof(char)*(requiredBufferLength + 1);
|
||||
result = ntfs_calloc(resultLength);
|
||||
|
||||
if(result != NULL) {
|
||||
if(CFStringGetBytes(cfMutableString, rangeToProcess, kCFStringEncodingUTF8,
|
||||
0, false, (UInt8*)result, resultLength-1, &requiredBufferLength) <= 0) {
|
||||
ntfs_log_error("Could not perform UTF-8 conversion of normalized CFMutableString.\n");
|
||||
free(result);
|
||||
result = NULL;
|
||||
}
|
||||
}
|
||||
else
|
||||
ntfs_log_error("Could not perform a ntfs_calloc of %d bytes for char *result.\n", resultLength);
|
||||
}
|
||||
else
|
||||
ntfs_log_error("Could not perform check for required length of UTF-8 conversion of normalized CFMutableString.\n");
|
||||
|
||||
|
||||
CFRelease(cfMutableString);
|
||||
|
||||
if(result != NULL) {
|
||||
*target = result;
|
||||
return resultLength - 1;
|
||||
}
|
||||
else
|
||||
return -1;
|
||||
#else
|
||||
return -1;
|
||||
#endif /* ENABLE_NFCONV */
|
||||
}
|
||||
#endif /* defined(__APPLE__) || defined(__DARWIN__) */
|
||||
|
@ -30,9 +30,9 @@ 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,
|
||||
extern int ntfs_names_full_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 IGNORE_CASE_BOOL ic,
|
||||
const ntfschar *upcase, const u32 upcase_len);
|
||||
|
||||
extern int ntfs_ucsncmp(const ntfschar *s1, const ntfschar *s2, size_t n);
|
||||
@ -47,25 +47,72 @@ 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 void ntfs_name_locase(ntfschar *name, u32 name_len,
|
||||
const ntfschar *locase, const u32 locase_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,
|
||||
extern void ntfs_file_value_upcase(FILE_NAME_ATTR *file_name_attr,
|
||||
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 char *ntfs_uppercase_mbs(const char *low,
|
||||
const ntfschar *upcase, u32 upcase_len);
|
||||
|
||||
extern void ntfs_upcase_table_build(ntfschar *uc, u32 uc_len);
|
||||
extern ntfschar *ntfs_locase_table_build(const ntfschar *uc, u32 uc_cnt);
|
||||
|
||||
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 */
|
||||
|
||||
|
@ -68,6 +68,40 @@
|
||||
# define O_EXCL 0
|
||||
#endif
|
||||
|
||||
/**
|
||||
* fsync replacement which makes every effort to try to get the data down to
|
||||
* disk, using different means for different operating systems. Specifically,
|
||||
* it issues the proper fcntl for Mac OS X or does fsync where it is available
|
||||
* or as a last resort calls the fsync function. Information on this problem
|
||||
* was retrieved from:
|
||||
* http://mirror.linux.org.au/pub/linux.conf.au/2007/video/talks/278.pdf
|
||||
*/
|
||||
static int ntfs_fsync(int fildes)
|
||||
{
|
||||
int ret = -1;
|
||||
#if defined(__APPLE__) || defined(__DARWIN__)
|
||||
# ifndef F_FULLFSYNC
|
||||
# error "Mac OS X: F_FULLFSYNC is not defined. Either you didn't include fcntl.h or you're using an older, unsupported version of Mac OS X (pre-10.3)."
|
||||
# endif
|
||||
/*
|
||||
* Apple has disabled fsync() for internal disk drives in OS X.
|
||||
* To force a synchronization of disk contents, we use a Mac OS X
|
||||
* specific fcntl, F_FULLFSYNC.
|
||||
*/
|
||||
ret = fcntl(fildes, F_FULLFSYNC, NULL);
|
||||
if (ret) {
|
||||
/*
|
||||
* If we are not on a file system that supports this,
|
||||
* then fall back to a plain fsync.
|
||||
*/
|
||||
ret = fsync(fildes);
|
||||
}
|
||||
#else
|
||||
ret = fsync(fildes);
|
||||
#endif
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* ntfs_device_unix_io_open - Open a device and lock it exclusively
|
||||
* @dev:
|
||||
@ -155,7 +189,7 @@ static int ntfs_device_unix_io_close(struct ntfs_device *dev)
|
||||
return -1;
|
||||
}
|
||||
if (NDevDirty(dev))
|
||||
if (fsync(DEV_FD(dev))) {
|
||||
if (ntfs_fsync(DEV_FD(dev))) {
|
||||
ntfs_log_perror("Failed to fsync device %s", dev->d_name);
|
||||
return -1;
|
||||
}
|
||||
@ -281,7 +315,7 @@ static int ntfs_device_unix_io_sync(struct ntfs_device *dev)
|
||||
int res = 0;
|
||||
|
||||
if (!NDevReadOnly(dev)) {
|
||||
res = fsync(DEV_FD(dev));
|
||||
res = ntfs_fsync(DEV_FD(dev));
|
||||
if (res)
|
||||
ntfs_log_perror("Failed to sync device %s", dev->d_name);
|
||||
else
|
||||
|
@ -4,6 +4,7 @@
|
||||
* Copyright (c) 2000-2006 Anton Altaparmakov
|
||||
* Copyright (c) 2002-2009 Szabolcs Szakacsits
|
||||
* Copyright (c) 2004-2005 Richard Russon
|
||||
* Copyright (c) 2010 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
|
||||
@ -65,6 +66,7 @@
|
||||
#include "logfile.h"
|
||||
#include "dir.h"
|
||||
#include "logging.h"
|
||||
#include "cache.h"
|
||||
#include "misc.h"
|
||||
|
||||
const char *ntfs_home =
|
||||
@ -197,8 +199,10 @@ static int __ntfs_volume_release(ntfs_volume *v)
|
||||
ntfs_error_set(&err);
|
||||
}
|
||||
|
||||
ntfs_free_lru_caches(v);
|
||||
free(v->vol_name);
|
||||
free(v->upcase);
|
||||
if (v->locase) free(v->locase);
|
||||
free(v->attrdef);
|
||||
free(v);
|
||||
|
||||
@ -485,7 +489,14 @@ ntfs_volume *ntfs_volume_startup(struct ntfs_device *dev, unsigned long flags)
|
||||
|
||||
ntfs_upcase_table_build(vol->upcase,
|
||||
vol->upcase_len * sizeof(ntfschar));
|
||||
/* Default with no locase table and case sensitive file names */
|
||||
vol->locase = (ntfschar*)NULL;
|
||||
NVolSetCaseSensitive(vol);
|
||||
|
||||
/* by default, all files are shown and not marked hidden */
|
||||
NVolSetShowSysFiles(vol);
|
||||
NVolSetShowHidFiles(vol);
|
||||
NVolClearHideDotFiles(vol);
|
||||
if (flags & MS_RDONLY)
|
||||
NVolSetReadOnly(vol);
|
||||
|
||||
@ -525,6 +536,7 @@ ntfs_volume *ntfs_volume_startup(struct ntfs_device *dev, unsigned long flags)
|
||||
"%s\n", strerror(errno));
|
||||
|
||||
/* We now initialize the cluster allocator. */
|
||||
vol->full_zones = 0;
|
||||
mft_zone_size = vol->nr_clusters >> 3; /* 12.5% */
|
||||
|
||||
/* Setup the mft zone. */
|
||||
@ -758,6 +770,70 @@ out:
|
||||
return errno ? -1 : 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Make sure a LOGGED_UTILITY_STREAM attribute named "$TXF_DATA"
|
||||
* on the root directory is resident.
|
||||
* When it is non-resident, the partition cannot be mounted on Vista
|
||||
* (see http://support.microsoft.com/kb/974729)
|
||||
*
|
||||
* We take care to avoid this situation, however this can be a
|
||||
* consequence of having used an older version (including older
|
||||
* Windows version), so we had better fix it.
|
||||
*
|
||||
* Returns 0 if unneeded or successful
|
||||
* -1 if there was an error, explained by errno
|
||||
*/
|
||||
|
||||
static int fix_txf_data(ntfs_volume *vol)
|
||||
{
|
||||
void *txf_data;
|
||||
s64 txf_data_size;
|
||||
ntfs_inode *ni;
|
||||
ntfs_attr *na;
|
||||
int res;
|
||||
|
||||
res = 0;
|
||||
ntfs_log_debug("Loading root directory\n");
|
||||
ni = ntfs_inode_open(vol, FILE_root);
|
||||
if (!ni) {
|
||||
ntfs_log_perror("Failed to open root directory");
|
||||
res = -1;
|
||||
} else {
|
||||
/* Get the $TXF_DATA attribute */
|
||||
na = ntfs_attr_open(ni, AT_LOGGED_UTILITY_STREAM, TXF_DATA, 9);
|
||||
if (na) {
|
||||
if (NAttrNonResident(na)) {
|
||||
/*
|
||||
* Fix the attribute by truncating, then
|
||||
* rewriting it.
|
||||
*/
|
||||
ntfs_log_debug("Making $TXF_DATA resident\n");
|
||||
txf_data = ntfs_attr_readall(ni,
|
||||
AT_LOGGED_UTILITY_STREAM,
|
||||
TXF_DATA, 9, &txf_data_size);
|
||||
if (txf_data) {
|
||||
if (ntfs_attr_truncate(na, 0)
|
||||
|| (ntfs_attr_pwrite(na, 0,
|
||||
txf_data_size, txf_data)
|
||||
!= txf_data_size))
|
||||
res = -1;
|
||||
free(txf_data);
|
||||
}
|
||||
if (res)
|
||||
ntfs_log_error("Failed to make $TXF_DATA resident\n");
|
||||
else
|
||||
ntfs_log_error("$TXF_DATA made resident\n");
|
||||
}
|
||||
ntfs_attr_close(na);
|
||||
}
|
||||
if (ntfs_inode_close(ni)) {
|
||||
ntfs_log_perror("Failed to close root");
|
||||
res = -1;
|
||||
}
|
||||
}
|
||||
return (res);
|
||||
}
|
||||
|
||||
/**
|
||||
* ntfs_device_mount - open ntfs volume
|
||||
* @dev: device to open
|
||||
@ -1043,9 +1119,9 @@ ntfs_volume *ntfs_device_mount(struct ntfs_device *dev, unsigned long flags)
|
||||
goto error_exit;
|
||||
|
||||
for (j = 0; j < (s32)u; j++) {
|
||||
ntfschar uc = le16_to_cpu(vname[j]);
|
||||
u16 uc = le16_to_cpu(vname[j]);
|
||||
if (uc > 0xff)
|
||||
uc = (ntfschar)'_';
|
||||
uc = (u16)'_';
|
||||
vol->vol_name[j] = (char)uc;
|
||||
}
|
||||
vol->vol_name[u] = '\0';
|
||||
@ -1109,6 +1185,9 @@ ntfs_volume *ntfs_device_mount(struct ntfs_device *dev, unsigned long flags)
|
||||
goto error_exit;
|
||||
}
|
||||
}
|
||||
/* make $TXF_DATA resident if present on the root directory */
|
||||
if (!NVolReadOnly(vol) && fix_txf_data(vol))
|
||||
goto error_exit;
|
||||
|
||||
return vol;
|
||||
io_error_exit:
|
||||
@ -1124,6 +1203,58 @@ error_exit:
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Set appropriate flags for showing NTFS metafiles
|
||||
* or files marked as hidden.
|
||||
* Not set in ntfs_mount() to avoid breaking existing tools.
|
||||
*/
|
||||
|
||||
int ntfs_set_shown_files(ntfs_volume *vol,
|
||||
BOOL show_sys_files, BOOL show_hid_files,
|
||||
BOOL hide_dot_files)
|
||||
{
|
||||
int res;
|
||||
|
||||
res = -1;
|
||||
if (vol) {
|
||||
NVolClearShowSysFiles(vol);
|
||||
NVolClearShowHidFiles(vol);
|
||||
NVolClearHideDotFiles(vol);
|
||||
if (show_sys_files)
|
||||
NVolSetShowSysFiles(vol);
|
||||
if (show_hid_files)
|
||||
NVolSetShowHidFiles(vol);
|
||||
if (hide_dot_files)
|
||||
NVolSetHideDotFiles(vol);
|
||||
res = 0;
|
||||
}
|
||||
if (res)
|
||||
ntfs_log_error("Failed to set file visibility\n");
|
||||
return (res);
|
||||
}
|
||||
|
||||
/*
|
||||
* Set ignore case mode
|
||||
*/
|
||||
|
||||
int ntfs_set_ignore_case(ntfs_volume *vol)
|
||||
{
|
||||
int res;
|
||||
|
||||
res = -1;
|
||||
if (vol && vol->upcase) {
|
||||
vol->locase = ntfs_locase_table_build(vol->upcase,
|
||||
vol->upcase_len);
|
||||
if (vol->locase) {
|
||||
NVolClearCaseSensitive(vol);
|
||||
res = 0;
|
||||
}
|
||||
}
|
||||
if (res)
|
||||
ntfs_log_error("Failed to set ignore_case mode\n");
|
||||
return (res);
|
||||
}
|
||||
|
||||
/**
|
||||
* ntfs_mount - open ntfs volume
|
||||
* @name: name of device/file to open
|
||||
@ -1166,7 +1297,8 @@ ntfs_volume *ntfs_mount(const char *name __attribute__((unused)),
|
||||
int eo = errno;
|
||||
ntfs_device_free(dev);
|
||||
errno = eo;
|
||||
}
|
||||
} else
|
||||
ntfs_create_lru_caches(vol);
|
||||
return vol;
|
||||
#else
|
||||
/*
|
||||
@ -1429,7 +1561,7 @@ error_exit:
|
||||
*
|
||||
* Return 0 if successful and -1 if not with errno set to the error code.
|
||||
*/
|
||||
int ntfs_volume_write_flags(ntfs_volume *vol, const u16 flags)
|
||||
int ntfs_volume_write_flags(ntfs_volume *vol, const le16 flags)
|
||||
{
|
||||
ATTR_RECORD *a;
|
||||
VOLUME_INFORMATION *c;
|
||||
@ -1564,3 +1696,30 @@ int ntfs_set_locale(void)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Feed the counts of free clusters and free mft records
|
||||
*/
|
||||
|
||||
int ntfs_volume_get_free_space(ntfs_volume *vol)
|
||||
{
|
||||
ntfs_attr *na;
|
||||
int ret;
|
||||
|
||||
ret = -1; /* default return */
|
||||
vol->free_clusters = ntfs_attr_get_free_bits(vol->lcnbmp_na);
|
||||
if (vol->free_clusters < 0) {
|
||||
ntfs_log_perror("Failed to read NTFS $Bitmap");
|
||||
} else {
|
||||
na = vol->mftbmp_na;
|
||||
vol->free_mft_records = ntfs_attr_get_free_bits(na);
|
||||
|
||||
if (vol->free_mft_records >= 0)
|
||||
vol->free_mft_records += (na->allocated_size - na->data_size) << 3;
|
||||
|
||||
if (vol->free_mft_records < 0)
|
||||
ntfs_log_perror("Failed to calculate free MFT records");
|
||||
else
|
||||
ret = 0;
|
||||
}
|
||||
return (ret);
|
||||
}
|
||||
|
@ -61,11 +61,13 @@
|
||||
/* Forward declaration */
|
||||
typedef struct _ntfs_volume ntfs_volume;
|
||||
|
||||
#include "param.h"
|
||||
#include "types.h"
|
||||
#include "support.h"
|
||||
#include "device.h"
|
||||
#include "inode.h"
|
||||
#include "attrib.h"
|
||||
#include "index.h"
|
||||
|
||||
/**
|
||||
* enum ntfs_mount_flags -
|
||||
@ -105,6 +107,10 @@ typedef enum {
|
||||
NV_ReadOnly, /* 1: Volume is read-only. */
|
||||
NV_CaseSensitive, /* 1: Volume is mounted case-sensitive. */
|
||||
NV_LogFileEmpty, /* 1: $logFile journal is empty. */
|
||||
NV_ShowSysFiles, /* 1: Show NTFS metafiles. */
|
||||
NV_ShowHidFiles, /* 1: Show files marked hidden. */
|
||||
NV_HideDotFiles, /* 1: Set hidden flag on dot files */
|
||||
NV_Compression, /* 1: allow compression */
|
||||
} ntfs_volume_state_bits;
|
||||
|
||||
#define test_nvol_flag(nv, flag) test_bit(NV_##flag, (nv)->state)
|
||||
@ -123,6 +129,22 @@ typedef enum {
|
||||
#define NVolSetLogFileEmpty(nv) set_nvol_flag(nv, LogFileEmpty)
|
||||
#define NVolClearLogFileEmpty(nv) clear_nvol_flag(nv, LogFileEmpty)
|
||||
|
||||
#define NVolShowSysFiles(nv) test_nvol_flag(nv, ShowSysFiles)
|
||||
#define NVolSetShowSysFiles(nv) set_nvol_flag(nv, ShowSysFiles)
|
||||
#define NVolClearShowSysFiles(nv) clear_nvol_flag(nv, ShowSysFiles)
|
||||
|
||||
#define NVolShowHidFiles(nv) test_nvol_flag(nv, ShowHidFiles)
|
||||
#define NVolSetShowHidFiles(nv) set_nvol_flag(nv, ShowHidFiles)
|
||||
#define NVolClearShowHidFiles(nv) clear_nvol_flag(nv, ShowHidFiles)
|
||||
|
||||
#define NVolHideDotFiles(nv) test_nvol_flag(nv, HideDotFiles)
|
||||
#define NVolSetHideDotFiles(nv) set_nvol_flag(nv, HideDotFiles)
|
||||
#define NVolClearHideDotFiles(nv) clear_nvol_flag(nv, HideDotFiles)
|
||||
|
||||
#define NVolCompression(nv) test_nvol_flag(nv, Compression)
|
||||
#define NVolSetCompression(nv) set_nvol_flag(nv, Compression)
|
||||
#define NVolClearCompression(nv) clear_nvol_flag(nv, Compression)
|
||||
|
||||
/*
|
||||
* NTFS version 1.1 and 1.2 are used by Windows NT4.
|
||||
* NTFS version 2.x is used by Windows 2000 Beta
|
||||
@ -154,7 +176,7 @@ struct _ntfs_volume {
|
||||
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. */
|
||||
le16 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. */
|
||||
@ -167,6 +189,7 @@ struct _ntfs_volume {
|
||||
|
||||
/* 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. */
|
||||
@ -196,6 +219,12 @@ struct _ntfs_volume {
|
||||
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. */
|
||||
@ -208,6 +237,9 @@ struct _ntfs_volume {
|
||||
FILE_UpCase. */
|
||||
u32 upcase_len; /* Length in Unicode characters of the upcase
|
||||
table. */
|
||||
ntfschar *locase; /* Lower case equivalents of all 65536 2-byte
|
||||
Unicode characters. Only if option
|
||||
case_ignore is set. */
|
||||
|
||||
ATTR_DEF *attrdef; /* Attribute definitions. Obtained from
|
||||
FILE_AttrDef. */
|
||||
@ -217,6 +249,25 @@ struct _ntfs_volume {
|
||||
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_NIDATA_SIZE
|
||||
struct CACHE_HEADER *nidata_cache;
|
||||
#endif
|
||||
#if CACHE_LOOKUP_SIZE
|
||||
struct CACHE_HEADER *lookup_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;
|
||||
@ -236,12 +287,17 @@ 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_write_flags(ntfs_volume *vol, const le16 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_shown_files(ntfs_volume *vol,
|
||||
BOOL show_sys_files, BOOL show_hid_files, BOOL hide_dot_files);
|
||||
extern int ntfs_set_locale(void);
|
||||
extern int ntfs_set_ignore_case(ntfs_volume *vol);
|
||||
|
||||
#endif /* defined _NTFS_VOLUME_H */
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user