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:
Gerasim Troeglazov 2010-12-01 22:05:42 +00:00
parent 8ce987787a
commit 0d2c294fa1
43 changed files with 18780 additions and 787 deletions

View File

@ -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);

View File

@ -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
;

File diff suppressed because it is too large Load Diff

View 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

View File

@ -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);

View 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
}

View 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_ */

View File

@ -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);
}

View File

@ -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 */

View File

@ -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

View File

@ -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

View File

@ -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 */

View 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 */

View 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 */

View File

@ -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);
}

View File

@ -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 */

View File

@ -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 */

View File

@ -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 */

View File

@ -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)

View File

@ -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);

View File

@ -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

View File

@ -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

View File

@ -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;

View File

@ -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;
}

View File

@ -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_

View File

@ -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(&microseconds, (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 */

View 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 */

View 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 */

View 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 */

File diff suppressed because it is too large Load Diff

View 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 */

View File

@ -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;
}

View File

@ -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

View File

@ -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 */

View File

@ -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__) */

View File

@ -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 */

View File

@ -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

View File

@ -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);
}

View File

@ -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 */