/*
 *		 Display and audit security attributes in an NTFS volume
 *
 * Copyright (c) 2007-2012 Jean-Pierre Andre
 * 
 *	Options :
 *		-a auditing security data
 *		-b backing up NTFS ACLs
 *		-e set extra backed-up parameters (in conjunction with -s)
 *		-h displaying hexadecimal security descriptors within a file
 *		-r recursing in a directory
 *		-s setting backed-up NTFS ACLs
 *		-u getting a user mapping proposal
 *		-v verbose (very verbose if set twice)
 *	   also, if compile-time option is set
 *		-t run internal tests (with no access to storage)
 *
 *	On Linux (being root, with volume not mounted) :
 *		secaudit -h [file]
 *			display the security descriptors found in file
 *		secaudit -a[rv] volume
 *			audit the volume
 *		secaudit [-v] volume file
 *			display the security parameters of file
 *		secaudit -r[v] volume directory
 *			display the security parameters of files in directory
 *		secaudit -b[v] volume [directory]
 *			backup the security parameters of files in directory
 *		secaudit -s[ve] volume [backupfile]
 *			set the security parameters as indicated in backup
 *			with -e set extra parameters (Windows attrib)
 *		secaudit volume perms file
 *			set the security parameters of file to perms (mode or acl)
 *		secaudit -r[v] volume perms directory
 *			set the security parameters of files in directory to perms
 *          special case, does not require being root :
 *		secaudit [-v] mounted-file
 *			display the security parameters of mounted file
 *
 *
 *	On Windows (the volume being part of file name)
 *		secaudit -h [file]
 *			display the security descriptors found in file
 *		secaudit [-v] file
 *			display the security parameters of file
 *		secaudit -r[v] directory
 *			display the security parameters of files in directory
 *		secaudit -b[v] directory
 *			backup the security parameters of files in directory
 *		secaudit -s[v] [backupfile]
 *			set the security parameters as indicated in backup
 *			with -e set extra parameters (Windows attrib)
 *		secaudit perms file
 *			set the security parameters of file to perms (mode or acl)
 *		secaudit -r[v] perms directory
 *			set the security parameters of files in directory to perms
 */

/*	History
 *
 *  Nov 2007
 *     - first version, by concatenating miscellaneous utilities
 *
 *  Jan 2008, version 1.0.1
 *     - fixed mode displaying
 *     - added a global severe errors count
 *
 *  Feb 2008, version 1.0.2
 *     - implemented conversions for big-endian machines
 *
 *  Mar 2008, version 1.0.3
 *     - avoided consistency checks on $Secure when there is no such file
 *
 *  Mar 2008, version 1.0.4
 *     - changed ordering of ACE's
 *     - changed representation for special flags
 *     - defaulted to stdin for option -h
 *     - added self tests (compile time option)
 *     - fixed errors specific to big-endian computers
 *
 *  Apr 2008, version 1.1.0
 *     - developped Posix ACLs to NTFS ACLs conversions
 *     - developped NTFS ACLs backup and restore
 *
 *  Apr 2008, version 1.1.1
 *     - fixed an error specific to big-endian computers
 *     - checked hash value and fixed error report in restore
 *     - improved display in showhex() and restore()
 *
 *  Apr 2008, version 1.1.2
 *     - improved and fixed Posix ACLs to NTFS ACLs conversions
 *
 *  Apr 2008, version 1.1.3
 *     - reenabled recursion for setting a new mode or ACL
 *     - processed Unicode file names and displayed them as UTF-8
 *     - allocated dynamically memory for file names when recursing
 *
 *  May 2008, version 1.1.4
 *     - all Unicode/UTF-8 strings checked and processed
 *
 *  Jul 2008, version 1.1.5
 *     - made Windows owner consistent with Linux owner when changing mode
 *     - allowed owner change on Windows when it does not match Linux owner
 *     - skipped currently unused code
 *
 *  Aug 2008, version 1.2.0
 *     - processed \.NTFS-3G\UserMapping on Windows
 *     - made use of user mappings through the security API or direct access
 *     - fixed a bug in restore
 *     - fixed UTF-8 conversions
 *
 *  Sep 2008, version 1.3.0
 *     - split the code to have part of it shared with ntfs-3g library
 *     - fixed testing for end of SDS block
 *     - added samples checking in selftest for easier debugging
 *
 *  Oct 2008, version 1.3.1
 *     - fixed outputting long long data when testing on a Palm organizer
 *
 *  Dec 2008, version 1.3.2
 *     - fixed restoring ACLs
 *     - added optional logging of ACL hashes to facilitate restore checks
 *     - fixed collecting SACLs
 *     - fixed setting special control flags
 *     - fixed clearing existing SACLs (Linux only) and DACLs
 *     - changed the sequencing of items when quering a security descriptor
 *     - avoided recursing on junctions and symlinks on Windows
 *
 *  Jan 2009, version 1.3.3
 *     - save/restore Windows attributes (code from Faisal)
 *
 *  Mar 2009, version 1.3.4
 *     - enabled displaying attributes of a mounted file over Linux
 *
 *  Apr 2009, version 1.3.5
 *     - fixed initialisation of stand-alone user mapping
 *     - fixed POSIXACL redefinition when included in the ntfs-3g package
 *     - fixed displaying of options
 *     - fixed a dependency on the shared library version used
 *
 *  May 2009, version 1.3.6
 *     - added new samples for self testing
 *
 *  Jun 2009, version 1.3.7
 *     - fixed displaying owner and group of a mounted file over Linux
 *
 *  Jul 2009, version 1.3.8
 *     - fixed again displaying owner and group of a mounted file over Linux
 *     - cleaned some code to avoid warnings
 *
 *  Nov 2009, version 1.3.9
 *     - allowed security descriptors up to 64K
 *
 *  Nov 2009, version 1.3.10
 *     - applied patches for MacOSX from Erik Larsson
 *
 *  Nov 2009, version 1.3.11
 *     - replace <attr/xattr.h> by <sys/xattr.h> (provided by glibc)
 *
 *  Dec 2009, version 1.3.12
 *     - worked around "const" possibly redefined in config.h
 *
 *  Dec 2009, version 1.3.13
 *     - fixed the return code of dorestore()
 *
 *  Dec 2009, version 1.3.14
 *     - adapted to opensolaris
 *
 *  Jan 2010, version 1.3.15
 *     - more adaptations to opensolaris
 *     - removed the fix for return code of dorestore()
 *
 *  Jan 2010, version 1.3.16
 *     - repeated the fix for return code of dorestore()
 *
 *  Mar 2010, version 1.3.17
 *     - adapted to new default user mapping
 *     - fixed #ifdef'd code for selftest
 *
 *  May 2010, version 1.3.18
 *     - redefined early error logging
 *
 *  Mar 2011, version 1.3.19
 *     - fixed interface to ntfs_initialize_file_security()
 *
 *  Apr 2011, version 1.3.20
 *     - fixed false memory leak detection
 *
 *  Jun 2011, version 1.3.21
 *     - cleaned a few unneeded variables
 *
 *  Nov 2011, version 1.3.22
 *     - added a distinctive prefix to owner and group SID
 *     - fixed a false memory leak detection
 *
 *  Jun 2012, version 1.3.23
 *     - added support for SACL (nickgarvey)
 *
 *  Jul 2012, version 1.3.24
 *     - added self-tests for authenticated users
 *     - added display of ace-inherited flag
 *     - made runnable on OpenIndiana
 *
 *  Aug 2012, version 1.4.0
 *     - added an option for user mapping proposal
 */

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

/*
 *		General parameters which may have to be adapted to needs
 */

#define AUDT_VERSION "1.4.0"

#define GET_FILE_SECURITY "ntfs_get_file_security"
#define SET_FILE_SECURITY "ntfs_set_file_security"
#define GET_FILE_ATTRIBUTES "ntfs_get_file_attributes"
#define SET_FILE_ATTRIBUTES "ntfs_set_file_attributes"
#define READ_DIRECTORY "ntfs_read_directory"
#define READ_SDS "ntfs_read_sds"
#define READ_SII "ntfs_read_sii"
#define READ_SDH "ntfs_read_sdh"
#define GET_USER "ntfs_get_user"
#define GET_GROUP "ntfs_get_group"
#define GET_USID "ntfs_get_usid"
#define GET_GSID "ntfs_get_gsid"
#define INIT_FILE_SECURITY "ntfs_initialize_file_security"
#define LEAVE_FILE_SECURITY "ntfs_leave_file_security"

/*
 *			External declarations
 */

#include <stdio.h>
#include <time.h>
#include <string.h>
#include <stdlib.h>
#include <sys/types.h>
#include <fcntl.h>
#include <errno.h>
#include <stdarg.h>

				/*
				 * integration into secaudit, check whether Win32,
				 * may have to be adapted to compiler or something else
				 */

#ifndef WIN32
#if defined(__WIN32) | defined(__WIN32__) | defined(WNSC)
#define WIN32 1
#endif
#endif

				/*
				 * integration into secaudit/Win32
				 */
#ifdef WIN32
#include <windows.h>
#define __LITTLE_ENDIAN 1234
#define __BYTE_ORDER __LITTLE_ENDIAN
#else
				/*
				 * integration into secaudit/STSC
				 */
#ifdef STSC
#include <stat.h>
#undef __BYTE_ORDER
#define __BYTE_ORDER __BIG_ENDIAN
#else
				/*
				 * integration into secaudit/Linux
				 */

#include <sys/stat.h>
#ifdef HAVE_ENDIAN_H
#include <endian.h>
#endif
#ifdef HAVE_MACHINE_ENDIAN_H
#include <machine/endian.h>
#endif
#include <unistd.h>
#include <dlfcn.h>
#endif /* STSC */
#endif /* WIN32 */
#include "secaudit.h"

#ifndef WIN32

#ifndef STSC

#if !defined(HAVE_CONFIG_H) && POSIXACLS && !defined(__SVR4)
      /* require <sys/xattr.h> if not integrated into ntfs-3g package */
#define HAVE_SETXATTR 1
#endif

#ifdef HAVE_CONFIG_H
#ifdef _FILE_OFFSET_BITS
#undef _FILE_OFFSET_BITS  /* work around "_FILE_OFFSET_BITS" possibly already defined */
#endif
      /* <sys/xattr.h> according to config.h if integrated into ntfs-3g package */
#include "config.h"
#ifdef const /* work around "const" possibly redefined in config.h */
#undef const
#endif
#ifndef POSIXACLS
#define POSIXACLS 0
#endif
#endif /* HAVE_CONFIG_H */

#ifdef HAVE_SETXATTR
#include <sys/xattr.h>
#else
#warning "The extended attribute package is not available"
#endif /* HAVE_SETXATTR */

#endif /* STSC */

#define MS_NONE 0    /* no flag for mounting the device */
#define MS_RDONLY 1  /* flag for mounting the device read-only */

struct CALLBACK;

typedef int (*dircallback)(struct CALLBACK *context, char *ntfsname,
	int length, int type, long long pos, u64 mft_ref,
	unsigned int dt_type);

#ifndef HAVE_SYSLOG_H
void ntfs_log_early_error(const char *format, ...)
			__attribute__((format(printf, 1, 2)));
#endif

#if USESTUBS | defined(STSC)

int ntfs_get_file_security(void *scapi,
		const char *path, DWORD selection,
		char *buf, DWORD buflen, LPDWORD psize);
BOOL ntfs_set_file_security(void *scapi,
		const char *path, DWORD selection, const char *attr);
int ntfs_get_file_attributes(void *scapi,
		const char *path);
BOOL ntfs_set_file_attributes(void *scapi,
		const char *path, DWORD attrib);
BOOL ntfs_read_directory(void *scapi,
		const char *path, dircallback callback, void *context);
int ntfs_read_sds(void *scapi,
		char *buf, DWORD buflen, DWORD offset);
void *ntfs_read_sii(void *scapi, void *entry);
void *ntfs_read_sdh(void *scapi, void *entry);

int ntfs_get_usid(void *scapi, uid_t uid, char *buf);
int ntfs_get_gsid(void *scapi, gid_t gid, char *buf);
int ntfs_get_user(void *scapi, const char *usid);
int ntfs_get_group(void *scapi, const char *gsid);

void *ntfs_initialize_file_security(const char *device, unsigned long flags);
BOOL ntfs_leave_file_security(void *scapi);

#else

typedef int (*type_get_file_security)(void *scapi,
		const char *path, DWORD selection,
		char *buf, DWORD buflen, LPDWORD psize);
typedef BOOL (*type_set_file_security)(void *scapi,
		const char *path, DWORD selection, const char *attr);
typedef int (*type_get_file_attributes)(void *scapi,
		const char *path);
typedef BOOL (*type_set_file_attributes)(void *scapi,
		const char *path, DWORD attrib);
typedef BOOL (*type_read_directory)(void *scapi,
		const char *path, dircallback callback, void *context);
typedef int (*type_read_sds)(void *scapi,
		char *buf, DWORD buflen, DWORD offset);
typedef void *(*type_read_sii)(void *scapi, void *entry);
typedef void *(*type_read_sdh)(void *scapi, void *entry);

typedef int (*type_get_usid)(void *scapi, uid_t uid, char *buf);
typedef int (*type_get_gsid)(void *scapi, gid_t gid, char *buf);
typedef int (*type_get_user)(void *scapi, const char *usid);
typedef int (*type_get_group)(void *scapi, const char *gsid);

typedef void *(*type_initialize_file_security)(const char *device,
				unsigned long flags);
typedef BOOL (*type_leave_file_security)(void *scapi);

type_get_file_security ntfs_get_file_security;
type_set_file_security ntfs_set_file_security;
type_get_file_attributes ntfs_get_file_attributes;
type_set_file_attributes ntfs_set_file_attributes;
type_read_directory ntfs_read_directory;
type_read_sds ntfs_read_sds;
type_read_sii ntfs_read_sii;
type_read_sdh ntfs_read_sdh;

type_get_usid ntfs_get_usid;
type_get_gsid ntfs_get_gsid;
type_get_user ntfs_get_user;
type_get_group ntfs_get_group;

type_initialize_file_security ntfs_initialize_file_security;
type_leave_file_security ntfs_leave_file_security;


#endif /* USESTUBS | defined(STSC) */
#endif /* WIN32 */

/*
 *		Prototypes for local functions
 */

BOOL open_security_api(void);
BOOL close_security_api(void);
#ifndef WIN32
BOOL open_volume(const char*, unsigned long flags);
BOOL close_volume(const char*);
#endif
unsigned int get2l(const char*, int);
unsigned long get4l(const char*, int);
u64 get6l(const char*, int);
u64 get6h(const char*, int);
u64 get8l(const char*, int);
void set2l(char*, unsigned int);
void set4l(char*, unsigned long);
void hexdump(const char*, int, int);
u32 hash(const le32*, int);
unsigned int utf8size(const char*, int);
unsigned int makeutf8(char*, const char*, int);
unsigned int utf16size(const char*);
unsigned int makeutf16(char*, const char*);
unsigned int utf16len(const char*);
void printname(FILE*, const char*);
void printerror(FILE*);
BOOL guess_dir(const char*);
void showsid(const char*, int, const char*, int);
void showusid(const char*, int);
void showgsid(const char*, int);
void showheader(const char*, int);
void showace(const char*, int, int, int);
void showacl(const char*, int, int, int);
void showdacl(const char*, int, int);
void showsacl(const char*, int, int);
void showall(const char*, int);
void showposix(const struct POSIX_SECURITY*);
int linux_permissions(const char*, BOOL);
uid_t linux_owner(const char*);
gid_t linux_group(const char*);
int basicread(void*, char*, size_t, off_t);
int dummyread(void*, char*, size_t, off_t);
int local_build_mapping(struct MAPPING *[], const char*);
void newblock(s32);
void freeblocks(void);
u32 getmsbhex(const char*);
u32 getlsbhex(const char*);
BOOL ishexdump(const char*, int, int);
void showhex(FILE*);
void showfull(const char*, BOOL);
BOOL applyattr(const char*, const char*, BOOL, int, s32);
BOOL restore(FILE*);
BOOL dorestore(const char*, FILE*);
u32 merge_rights(const struct POSIX_SECURITY*, BOOL);
void tryposix(struct POSIX_SECURITY*);
void check_samples(void);
void basictest(int, BOOL, const SID*, const SID*);
void posixtest(int, BOOL, const SID*, const SID*);
void selftests(void);
unsigned int getfull(char*, const char*);
BOOL updatefull(const char *name, DWORD flags, char *attr);
BOOL setfull(const char*, int, BOOL);
BOOL singleshow(const char*);
BOOL proposal(const char*, const char*);
BOOL showmounted(const char*);
BOOL processmounted(const char*);
BOOL recurseshow(const char*);
BOOL singleset(const char*, int);
BOOL recurseset(const char*, int);
#ifdef WIN32
BOOL backup(const char*);
BOOL listfiles(const char*);
#if POSIXACLS
BOOL iterate(RECURSE, const char*, const struct POSIX_SECURITY*);
#else
BOOL iterate(RECURSE, const char*, mode_t);
#endif
#else
BOOL backup(const char*, const char*);
BOOL listfiles(const char*, const char*);
BOOL mapproposal(const char*, const char*);
#endif
#if POSIXACLS
BOOL setfull_posix(const char *, const struct POSIX_SECURITY*, BOOL);
struct POSIX_SECURITY *linux_permissions_posix(const char*, BOOL);
BOOL recurseset_posix(const char*, const struct POSIX_SECURITY*);
BOOL singleset_posix(const char*, const struct POSIX_SECURITY*);
struct POSIX_SECURITY *encode_posix_acl(const char*);
#endif
static void *stdmalloc(size_t);
static void stdfree(void*);

BOOL valid_sds(const char*, unsigned int, unsigned int,
		unsigned int, u32, BOOL);
BOOL valid_sii(const char*, u32);
BOOL valid_sdh(const char*, u32, u32);
int consist_sds(const char*, unsigned int, unsigned int, BOOL);
int consist_sii(const char*);
int consist_sdh(const char*);
int audit_sds(BOOL);
int audit_sii(void);
int audit_sdh(void);
void audit_summary(void);
BOOL audit(const char*);
int getoptions(int, char*[]);

#ifndef WIN32

/*
 *		Structures for collecting directory contents (Linux only)
 */

struct LINK {
	struct LINK *next;
	char name[1];
} ;

struct CALLBACK {
	struct LINK *head;
	const char *dir;
} ;

int callback(struct CALLBACK *context, char *ntfsname,
	int length, int type, long long pos, u64 mft_ref,
	unsigned int dt_type);
#endif

/*
 *		  Global constants
 */

#define BANNER "secaudit " AUDT_VERSION " : NTFS security data auditing"

#if SELFTESTS & !USESTUBS

/*
 *		Dummy mapping file (self tests only)
 */

#define DUMMYAUTH "S-1-5-21-3141592653-589793238-462843383-"
char dummymapping[] =	"500::" DUMMYAUTH "1000\n"
			"501::" DUMMYAUTH "1001\n"
			"502::" DUMMYAUTH "1002\n"
			"503::" DUMMYAUTH "1003\n"
			"516::" DUMMYAUTH "1016\n"
			":500:" DUMMYAUTH "513\r\n"
			":511:S-1-5-21-1607551490-981732888-1819828000-513\n"
			":516:" DUMMYAUTH "1012\r\n"
			"::"	DUMMYAUTH "10000\n";

/*
 *		SID for world  (S-1-1-0)
 */

static const char worldsidbytes[] = {
		1,		/* revision */
		1,		/* auth count */
		0, 0, 0, 0, 0, 1,	/* base */
		0, 0, 0, 0	/* 1st level */
} ;
static const SID *worldsid = (const SID*)worldsidbytes;

/*	        
 *		SID for authenticated user (S-1-5-11)
 */
	        
static const char authsidbytes[] = {
		1,		/* revision */ 
		1,		/* auth count */
		0, 0, 0, 0, 0, 5,	/* base */
		11, 0, 0, 0	/* 1st level */
};
  
static const SID *authsid = (const SID*)authsidbytes;

/*
 *		SID for administrator (S-1-5-32-544)
 */

static const char adminsidbytes[] = {
		1,		/* revision */
		2,		/* auth count */
		0, 0, 0, 0, 0, 5,	/* base */
		32, 0, 0, 0,	/* 1st level */
		32, 2, 0, 0	/* 2nd level */
};

static const SID *adminsid = (const SID*)adminsidbytes;

/*
 *		SID for local users (S-1-5-32-545)
 */

static const char localsidbytes[] = {
		1,		/* revision */
		2,		/* auth count */
		0, 0, 0, 0, 0, 5,	/* base */
		32, 0, 0, 0,	/* 1st level */
		33, 2, 0, 0	/* 2nd level */
};

static const SID *localsid = (const SID*)localsidbytes;

/*	        
 *		SID for system (S-1-5-18)
 */
	        
static const char systemsidbytes[] = {
		1,		/* revision */ 
		1,		/* auth count */
		0, 0, 0, 0, 0, 5,	/* base */
		18, 0, 0, 0	/* 1st level */
	};
  
static const SID *systemsid = (const SID*)systemsidbytes;

#endif

/*
 *		  Global variables
 */

BOOL opt_a;	/* audit security data */
BOOL opt_b;	/* backup NTFS ACLs */
BOOL opt_e;	/* restore extra (currently windows attribs) */
BOOL opt_h;	/* display an hexadecimal descriptor in a file */
BOOL opt_r;	/* recursively apply to subdirectories */
BOOL opt_s;	/* restore NTFS ACLs */
BOOL opt_u;	/* user mapping proposal */
#if SELFTESTS & !USESTUBS
BOOL opt_t;	/* run self-tests */
#endif
int opt_v;  /* verbose or very verbose*/
struct SECURITY_DATA *securdata[(MAXSECURID + (1 << SECBLKSZ) - 1)/(1 << SECBLKSZ)];
#if FORCEMASK
BOOL opt_m;	/* force a mask - dangerous */
u32 forcemsk /* mask to force */
#endif
unsigned int errors; /* number of severe errors */
unsigned int warnings; /* number of non-severe errors */

struct CHKALLOC *firstalloc;
struct SECURITY_CONTEXT context;
MAPTYPE mappingtype;

#ifdef STSC
#define static
#endif

#ifndef WIN32

void *ntfs_handle;
void *ntfs_context = (void*)NULL;

/*
 *		Open and close the security API (platform dependent)
 */

BOOL open_security_api(void)
{
#if USESTUBS | defined(STSC)
	return (TRUE);
#else
	char *error;
	BOOL err;
	const char *libfile;

	err = TRUE;
	libfile = getenv(ENVNTFS3G);
	if (!libfile)
		libfile = (sizeof(char*) == 8 ? LIBFILE64 : LIBFILE);
#ifdef __SVR4
		/* do not override library functions by caller ones */
	ntfs_handle = dlopen(libfile,RTLD_LAZY | RTLD_GROUP);
#else
	ntfs_handle = dlopen(libfile,RTLD_LAZY);
#endif
	if (ntfs_handle) {
		ntfs_initialize_file_security = (type_initialize_file_security)
				dlsym(ntfs_handle,INIT_FILE_SECURITY);
		error = dlerror();
		if (error)
			fprintf(stderr," %s\n",error);
		else {
			ntfs_leave_file_security = (type_leave_file_security)
					dlsym(ntfs_handle,LEAVE_FILE_SECURITY);
			ntfs_get_file_security = (type_get_file_security)
					dlsym(ntfs_handle,GET_FILE_SECURITY);
			ntfs_set_file_security = (type_set_file_security)
					dlsym(ntfs_handle,SET_FILE_SECURITY);
			ntfs_get_file_attributes = (type_get_file_attributes)
					dlsym(ntfs_handle,GET_FILE_ATTRIBUTES);
			ntfs_set_file_attributes = (type_set_file_attributes)
					dlsym(ntfs_handle,SET_FILE_ATTRIBUTES);
			ntfs_read_directory = (type_read_directory)
					dlsym(ntfs_handle,READ_DIRECTORY);
			ntfs_read_sds = (type_read_sds)
					dlsym(ntfs_handle,READ_SDS);
			ntfs_read_sii = (type_read_sii)
					dlsym(ntfs_handle,READ_SII);
			ntfs_read_sdh = (type_read_sdh)
					dlsym(ntfs_handle,READ_SDH);
			ntfs_get_user = (type_get_user)
					dlsym(ntfs_handle,GET_USER);
			ntfs_get_group = (type_get_group)
					dlsym(ntfs_handle,GET_GROUP);
			ntfs_get_usid = (type_get_usid)
					dlsym(ntfs_handle,GET_USID);
			ntfs_get_gsid = (type_get_gsid)
					dlsym(ntfs_handle,GET_GSID);
			err = !ntfs_initialize_file_security
				|| !ntfs_leave_file_security
				|| !ntfs_get_file_security
				|| !ntfs_set_file_security
				|| !ntfs_get_file_attributes
				|| !ntfs_set_file_attributes
				|| !ntfs_read_directory
				|| !ntfs_read_sds
				|| !ntfs_read_sii
				|| !ntfs_read_sdh
				|| !ntfs_get_user
				|| !ntfs_get_group
				|| !ntfs_get_usid
				|| !ntfs_get_gsid;

			if (error)
				fprintf(stderr,"ntfs-3g API not available\n");
		}
	} else {
		fprintf(stderr,"Could not open ntfs-3g library\n");
		fprintf(stderr,"\nPlease set environment variable \"" ENVNTFS3G "\"\n");
		fprintf(stderr,"to appropriate path and retry\n");
	}
	return (!err);
#endif /* USESTUBS | defined(STSC) */
}

BOOL close_security_api(void)
{
#if USESTUBS | defined(STSC)
	return (0);
#else
	return (!dlclose(ntfs_handle));
#endif /* USESTUBS | defined(STSC) */
}

/*
 *		Open and close a volume (platform dependent)
 *	Assumes a single volume is opened
 */

BOOL open_volume(const char *volume, unsigned long flags)
{
	BOOL ok;

	ok = FALSE;
	if (!ntfs_context) {
		ntfs_context = ntfs_initialize_file_security(volume,flags);
		if (ntfs_context) {
			if (*(u32*)ntfs_context != MAGIC_API) {
				fprintf(stderr,"Versions of ntfs-3g and secaudit"
						" are not compatible\n");
			} else {
				fprintf(stderr,"\"%s\" opened\n",volume);
				mappingtype = MAPEXTERN;
				ok = TRUE;
			}
		} else {
			fprintf(stderr,"Could not open \"%s\"\n",volume);
			fprintf(stderr,"Make sure \"%s\" is not mounted\n",volume);
		}
	} else
		fprintf(stderr,"A volume is already open\n");
	return (ok);
}

BOOL close_volume(const char *volume)
{
	BOOL r;

	r = ntfs_leave_file_security(ntfs_context);
	if (r)
		fprintf(stderr,"\"%s\" closed\n",volume);
	else
		fprintf(stderr,"Could not close \"%s\"\n",volume);
	ntfs_context = (void*)NULL;
	return (r);
}

#endif /* WIN32 */

/*
 *		Extract small or big endian data from an array of bytes
 */

unsigned int get2l(const char *attr, int p)
{
	int i;
	unsigned int v;

	v = 0;
	for (i=0; i<2; i++)
		v += (attr[p+i] & 255) << (8*i);
	return (v);
}

unsigned long get4l(const char *attr, int p)
{
	int i;
	unsigned long v;

	v = 0;
	for (i=0; i<4; i++)
		v += ((long)(attr[p+i] & 255)) << (8*i);
	return (v);
}

u64 get6l(const char *attr, int p)
{
	int i;
	u64 v;

	v = 0;
	for (i=0; i<6; i++)
		v += ((long long)(attr[p+i] & 255)) << (8*i);
	return (v);
}

u64 get6h(const char *attr, int p)
{
	int i;
	u64 v;

	v = 0;
	for (i=0; i<6; i++)
		v = (v << 8) + (attr[p+i] & 255);
	return (v);
}

u64 get8l(const char *attr, int p)
{
	int i;
	u64 v;

	v = 0;
	for (i=0; i<8; i++)
		v += ((long long)(attr[p+i] & 255)) << (8*i);
	return (v);
}

/*
 *		Set small or big endian data into an array of bytes
 */

void set2l(char *p, unsigned int v)
{
	int i;

	for (i=0; i<2; i++)
		p[i] = ((v >> 8*i) & 255);
}

void set4l(char *p, unsigned long v)
{
	int i;

	for (i=0; i<4; i++)
		p[i] = ((v >> 8*i) & 255);
}


/*
 *		hexadecimal dump of an array of bytes
 */

void hexdump(const char *attr, int size, int level)
{
	int i,j;

	for (i=0; i<size; i+=16) {
		if (level)
			printf("%*c",level,' ');
		printf("%06x ",i);
		for (j=i; (j<(i+16)) && (j<size); j++)
			printf((j & 3 ? "%02x" : " %02x"),attr[j] & 255);
		printf("\n");
	}
}

u32 hash(const le32 *buf, int size /* bytes */)
{
	u32 h;
	int i;

	h = 0;
	for (i=0; 4*i<size; i++)
		h = le32_to_cpu(buf[i]) + (h << 3) + ((h >> 29) & 7);
	return (h);
}

/*
 *		Evaluate the size of UTS-8 conversion of a UTF-16LE text
 *	trailing '\0' not accounted for
 *	Returns 0 for invalid input
 */

unsigned int utf8size(const char *utf16, int length)
{
	int i;
	unsigned int size;
	enum { BASE, SURR, ERR } state;

	size = 0;
	state = BASE;
	for (i=0; i<2*length; i+=2) {
		switch (state) {
		case BASE :
			if (utf16[i+1] & 0xf8) {
				if ((utf16[i+1] & 0xf8) == 0xd8)
					state = (utf16[i+1] & 4 ? ERR : SURR);
				else
#if NOREVBOM
					if (((utf16[i+1] & 0xff) == 0xff)
					  && ((utf16[i] & 0xfe) == 0xfe))
						state = ERR;
					else
						size += 3;
#else
					size += 3;
#endif
			} else
				if ((utf16[i] & 0x80) || utf16[i+1])
					size += 2;
				else
					size++;
			break;
		case SURR :
			if ((utf16[i+1] & 0xfc) == 0xdc) {
				state = BASE;
				size += 4;
			} else
				state = ERR;
			break;
		case ERR :
			break;
		}
	}
	if (state != BASE)
		size = 0;
	return (size);
}

/*
 *		Convert a UTF-16LE text to UTF-8
 *	Note : wcstombs() not used because on Linux it fails for characters
 *	not present in current locale
 *	Returns size or zero for invalid input
 */

unsigned int makeutf8(char *utf8, const char *utf16, int length)
{
	int i;
	unsigned int size;
	unsigned int rem;
	enum { BASE, SURR, ERR } state;

	size = 0;
	rem = 0;
	state = BASE;
	for (i=0; i<2*length; i+=2) {
		switch (state) {
		case BASE :
			if (utf16[i+1] & 0xf8) {
				if ((utf16[i+1] & 0xf8) == 0xd8) {
					if (utf16[i+1] & 4)
						state = ERR;
					else {
						utf8[size++] = 0xf0 + (utf16[i+1] & 7)
                                                                    + ((utf16[i] & 0xc0) == 0xc0);
						utf8[size++] = 0x80 + (((utf16[i] + 64) >> 2) & 63);
						rem = utf16[i] & 3;
						state = SURR;
					}
				} else {
#if NOREVBOM
					if (((utf16[i+1] & 0xff) == 0xff)
					  && ((utf16[i] & 0xfe) == 0xfe))
						state = ERR;
					else {
						utf8[size++] = 0xe0 + ((utf16[i+1] >> 4) & 15);
						utf8[size++] = 0x80
							+ ((utf16[i+1] & 15) << 2)
							+ ((utf16[i] >> 6) & 3);
						utf8[size++] = 0x80 + (utf16[i] & 63);
					}
#else
					utf8[size++] = 0xe0 + ((utf16[i+1] >> 4) & 15);
					utf8[size++] = 0x80
						+ ((utf16[i+1] & 15) << 2)
						+ ((utf16[i] >> 6) & 3);
					utf8[size++] = 0x80 + (utf16[i] & 63);
#endif
				}
			} else
				if ((utf16[i] & 0x80) || utf16[i+1]) {
					utf8[size++] = 0xc0
						+ ((utf16[i+1] & 15) << 2)
						+ ((utf16[i] >> 6) & 3);
					utf8[size++] = 0x80 + (utf16[i] & 63);
				} else
					utf8[size++] = utf16[i];
			break;
		case SURR :
			if ((utf16[i+1] & 0xfc) == 0xdc) {
				utf8[size++] = 0x80 + (rem << 4)
						 + ((utf16[i+1] & 3) << 2)
						 + ((utf16[i] >> 6) & 3);
				utf8[size++] = 0x80 + (utf16[i] & 63);
				state = BASE;
			} else
				state = ERR;
			break;
		case ERR :
			break;
		}
	}
	utf8[size] = 0;
	if (state != BASE)
		state = ERR;
	return (state == ERR ? 0 : size);
}

#ifdef WIN32

/*
 *		Evaluate the size of UTF-16LE conversion of a UTF-8 text
 *	(basic conversions only)
 *	trailing '\0' not accounted for
 */

unsigned int utf16size(const char *utf8)
{
	unsigned int size;
	const char *p;
	int c;
	unsigned int code;
	enum { BASE, TWO, THREE, THREE2, THREE3, FOUR, ERR } state;

	p = utf8;
	size = 0;
	state = BASE;
	while (*p) {
		c = *p++ & 255;
		switch (state) {
		case BASE :
			if (!(c & 0x80))
				size++;
			else
				if (c < 0xc2)
					state = ERR;
				else
					if (c < 0xe0)
						state = TWO;
					else
						if (c < 0xf0) {
							if (c == 0xe0)
								state = THREE2;
							else
								if (c == 0xed)
									state = THREE3;
								else
									state = THREE;
						} else
							if (c < 0xf8) {
								state = FOUR;
								code = c & 7;
							} else
								state = ERR;
			break;
		case TWO :
			if ((c & 0xc0) != 0x80)
				state = ERR;
			else {
				size++;
				state = BASE;
			}
			break;
		case THREE :
			if ((c & 0xc0) != 0x80)
				state = ERR;
			else
				state = TWO;
			break;
		case THREE2 :
			if ((c & 0xe0) != 0xa0)
				state = ERR;
			else
				state = TWO;
			break;
		case THREE3 :
			if ((c & 0xe0) != 0x80)
				state = ERR;
			else
				state = TWO;
			break;
		case FOUR :
			if ((((code << 6) + (c & 63)) > 0x10f)
			  || (((code << 6) + (c & 63)) < 0x10))
				state = ERR;
			else {
				size++;
				state = THREE;
			}
			break;
		case ERR :
			break;
		}
	}
	if (state != BASE) size = 0;
	return (size);
}

/*
 *		Convert a UTF8 text to UTF-16LE
 *	(basic conversions only)
 *	Note : mbstowcs() not used because on Linux it fails for characters
 *	not present in current locale
 */

unsigned int makeutf16(char *target, const char *utf8)
{
	unsigned int size;
	unsigned int code;
	const char *p;
	int c;
	enum { BASE, TWO, THREE, THREE2, THREE3, FOUR, FOUR2, FOUR3, ERR } state;

	p = utf8;
	size = 0;
	c = 0;
	state = BASE;
	while (*p) {
		c = *p++ & 255;
		switch (state) {
		case BASE :
			if (!(c & 0x80)) {
				target[2*size] = c;
				target[2*size + 1] = 0;
				size++;
			} else {
				if (c < 0xc2)
					state = ERR;
				else
					if (c < 0xe0) {
						code = c & 31;
						state = TWO;
					} else
						if (c < 0xf0) {
							code = c & 15;
							if (c == 0xe0)
								state = THREE2;
							else
								if (c == 0xed)
									state = THREE3;
								else
									state = THREE;
						} else
							if (c < 0xf8) {
								code = c & 7;
								state = FOUR;
							} else
								state = ERR;
			}
			break;
		case TWO :
#if NOREVBOM
			if (((c & 0xc0) != 0x80)
			  || ((code == 0x3ff) && (c >= 0xbe)))
#else
			if ((c & 0xc0) != 0x80)
#endif
				state = ERR;
			else {
				target[2*size] = ((code & 3) << 6) + (c & 63);
				target[2*size + 1] = ((code >> 2) & 255);
				size++;
				state = BASE;
			}
			break;
		case THREE :
			if ((c & 0xc0) != 0x80)
				state = ERR;
			else {
				code = ((code & 15) << 6) + (c & 63);
				state = TWO;
			}
			break;
		case THREE2 :
			if ((c & 0xe0) != 0xa0)
				state = ERR;
			else {
				code = ((code & 15) << 6) + (c & 63);
				state = TWO;
			}
			break;
		case THREE3 :
			if ((c & 0xe0) != 0x80)
				state = ERR;
			else {
				code = ((code & 15) << 6) + (c & 63);
				state = TWO;
			}
			break;
		case FOUR :
			if ((c & 0xc0) != 0x80)
				state = ERR;
			else {
				code = (code << 6) + (c & 63);
				state = FOUR2;
			}
			break;
		case FOUR2 :
			if ((c & 0xc0) != 0x80)
				state = ERR;
			else {
				code = (code << 6) + (c & 63);
				state = FOUR3;
			}
			break;
		case FOUR3 :
			if ((code > 0x43ff)
			 || (code < 0x400)
			 || ((c & 0xc0) != 0x80))
				state = ERR;
			else {
				target[2*size] = ((code - 0x400) >> 4) & 255;
				target[2*size+1] = 0xd8 + (((code - 0x400) >> 12) & 3);
				target[2*size+2] = ((code & 3) << 6) + (c & 63);
				target[2*size+3] = 0xdc + ((code >> 2) & 3);
				size += 2;
				state = BASE;
			}
			break;
		case ERR :
			break;
		}
	}
	if (state != BASE)
		size = 0;
	target[2*size] = 0;
	target[2*size + 1] = 0;
	return (size);
}

unsigned int utf16len(const char *str)
{
	unsigned int len;

	len = 0;
	while (str[2*len] || str[2*len+1]) len++;
	return (len);
}

#endif

/*
 *		Print a file name
 *	on Windows it prints UTF-16LE names as UTF-8
 */

void printname(FILE *file, const char *name)
{
#ifdef WIN32
	char utf8name[MAXFILENAME];

	makeutf8(utf8name,name,utf16len(name));
	fprintf(file,"%s",utf8name);	
#else
	fprintf(file,"%s",name);
#endif
}

/*
 *		Print the last error code
 */

void printerror(FILE *file)
{
#ifdef WIN32
	int err;
	const char *txt;

	err = GetLastError();
	switch (err) {
	case 5 :
		txt = "Access to security descriptor was denied";
		break;
	case 1307 :
		txt = "This SID may not be assigned as the owner of this object";
		break;
	case 1308 :
		txt = "This SID may not be assigned as the group of this object";
		break;
	case 1314 :
		txt = "You do not have the privilege to change this SID";
		break;
	default :
		txt = (const char*)NULL;
		break;
	}
	if (txt)
		fprintf(file,"Error %d : %s\n",err,txt);
	else
		fprintf(file,"Error %d\n",err);
#else
#ifdef STSC
	if (errno) fprintf(file,"Error code %d\n",errno);
#else
	if (errno) fprintf(file,"Error code %d : %s\n",errno,strerror(errno));
#endif
#endif
}

#ifndef HAVE_SYSLOG_H

/*
 *		Redefine early error messages in stand-alone situations
 */

void ntfs_log_early_error(const char *format, ...)
{
	va_list args;

	va_start(args, format);
	vfprintf(stderr,format,args);
	va_end(args);
}

#endif

/*
 *	Guess whether a security attribute is intended for a directory
 *	based on the presence of inheritable ACE
 *	(not 100% reliable)
 */

BOOL guess_dir(const char *attr)
{
	int off;
	int isdir;
	int cnt;
	int i;
	int x;

	isdir = 0;
	off = get4l(attr,16);
	if (off) {
		cnt = get2l(attr,off+4);
		x = 8;
		for (i=0; i<cnt; i++) {
			if (attr[off + x + 1] & 3)
				isdir = 1;
			x += get2l(attr,off + x + 2);
		}
	}
	return (isdir);
}

/*
 *		   Display a SID
 *   See http://msdn2.microsoft.com/en-us/library/aa379649.aspx
 */

void showsid(const char *attr, int off, const char *prefix, int level)
{
	int cnt;
	int i;
	BOOL known;
	u64 auth;
	unsigned long first;
	unsigned long second;
	unsigned long last;
	char marker;

	if (opt_b)
		marker = '#';
	else
		marker = ' ';
	cnt = attr[off+1] & 255;
	auth = get6h(attr,off+2);
	first = get4l(attr,off+8);
	known = FALSE;
	if ((attr[off] == 1) /* revision */
	     && (auth < 100))
		switch (cnt) {
		case 0 : /* no level (error) */
			break;
		case 1 : /* single level */
			switch (auth) {
			case 0 :
				if (first == 0) {
					known = TRUE;
					printf("%*cNull SID\n",-level,marker);
				}
				break;
			case 1 :
				if (first == 0) {
					known = TRUE;
					printf("%*cWorld SID\n",-level,marker);
				}
				break;
			case 3 :
				switch (first) {
				case 0 :
					known = TRUE;
					printf("%*cCreator owner SID\n",-level,marker);
					break;
				case 1 :
					known = TRUE;
					printf("%*cCreator group SID\n",-level,marker);
					break;
				}
				break;
			case 5 :
				switch (first) {
				case 7 :
					known = TRUE;
					printf("%*cAnonymous logon SID\n",-level,marker);
					break;
				case 11 :
					known = TRUE;
					printf("%*cAuthenticated user SID\n",-level,marker);
					break;
				case 13 :
					known = TRUE;
					printf("%*cLocal service SID\n",-level,marker);
					break;
				case 14 :
					known = TRUE;
					printf("%*cNetwork service SID\n",-level,marker);
					break;
				case 18 :
					known = TRUE;
					printf("%*cNT System SID\n",-level,marker);
					break;
				}
				break;
			}
			break;
		case 2 : /* double level */
			second = get4l(attr,off+12);
			switch (auth) {
			case 5 :
				if (first == 32) {
					known = TRUE;
					switch (second) {
					case 544 :
						printf("%*cLocal admins SID\n",-level,marker);
						break;
					case 545 :
						printf("%*cLocal users SID\n",-level,marker);
						break;
					case 546 :
						printf("%*cLocal guests SID\n",-level,marker);
						break;
					default :
						printf("%*cSome domain SID\n",-level,marker);
						break;
					}
				}
				break;
			}
		default : /* three levels or more */
			second = get4l(attr,off+12);
			last = get4l(attr,off+4+4*cnt);
			switch (auth) {
			case 5 :
				if (first == 21) {
					known = TRUE;
					switch (last)
						{
						case 512 :
							printf("%*cLocal admins SID\n",-level,marker);
							break;
						case 513 :
							printf("%*cLocal users SID\n",-level,marker);
							break;
						case 514 :
							printf("%*cLocal guests SID\n",-level,marker);
							break;
						default :
							printf("%*cLocal user-%lu SID\n",-level,marker,last);
							break;
					}
				}
				break;
			}
		}
	if (!known)
		printf("%*cUnknown SID\n",-level,marker);
	printf("%*c%shex S-%d-",-level,marker,prefix,attr[off] & 255);
	printf("%llx",auth);
	for (i=0; i<cnt; i++)
		printf("-%lx",get4l(attr,off+8+4*i));
	printf("\n");
	printf("%*c%sdec S-%d-",-level,marker,prefix,attr[off] & 255);
	printf("%llu",auth);
	for (i=0; i<cnt; i++)
		printf("-%lu",get4l(attr,off+8+4*i));
	printf("\n");
}

void showusid(const char *attr, int level)
{
	int off;
	char marker;

	if (opt_b)
		marker = '#';
	else
		marker = ' ';
	if (level)
		printf("%*c",-level,marker);
	printf("Owner SID\n");
	off = get4l(attr,4);
	showsid(attr,off,"O:",level+4);
}

void showgsid(const char *attr, int level)
{
	int off;
	char marker;

	if (opt_b)
		marker = '#';
	else
		marker = ' ';
	if (level)
		printf("%*c",-level,marker);
	printf("Group SID\n");
	off = get4l(attr,8);
	showsid(attr,off,"G:",level+4);
}

void showheader(const char *attr, int level)
{
	int flags;
	char marker;

	if (opt_b)
		marker = '#';
	else
		marker = ' ';
	if (level)
		printf("%*c",-level,marker);
	printf("Global header\n");
	printf("%*crevision %d\n",-level-4,marker,attr[0]);
	flags = get2l(attr,2);
	printf("%*cflags    0x%x\n",-level-4,marker,flags);
	if (flags & 1)
		printf("%*c    owner is defaulted\n",-level-4,marker);
	if (flags & 2)
		printf("%*c    group is defaulted\n",-level-4,marker);
	if (flags & 4)
		printf("%*c    DACL present\n",-level-4,marker);
	if (flags & 8)
		printf("%*c    DACL is defaulted\n",-level-4,marker);
	if (flags & 0x10)
		printf("%*c    SACL present\n",-level-4,marker);
	if (flags & 0x20)
		printf("%*c    SACL is defaulted\n",-level-4,marker);
	if (flags & 0x100)
		printf("%*c    DACL inheritance is requested\n",-level-4,marker);
	if (flags & 0x200)
		printf("%*c    SACL inheritance is requested\n",-level-4,marker);
	if (flags & 0x400)
		printf("%*c    DACL was inherited automatically\n",-level-4,marker);
	if (flags & 0x800)
		printf("%*c    SACL was inherited automatically\n",-level-4,marker);
	if (flags & 0x1000)
		printf("%*c    DACL cannot be modified by inheritable ACEs\n",-level-4,marker);
	if (flags & 0x2000)
		printf("%*c    SACL cannot be modified by inheritable ACEs\n",-level-4,marker);
	if (flags & 0x8000)
		printf("%*c    self relative descriptor\n",-level-4,marker);
	if (flags & 0x43eb)
		printf("%*c    unknown flags 0x%x present\n",-level-4,marker,
				flags & 0x43eb);
	printf("%*cOff USID 0x%x\n",-level-4,marker,(int)get4l(attr,4));
	printf("%*cOff GSID 0x%x\n",-level-4,marker,(int)get4l(attr,8));
	printf("%*cOff SACL 0x%x\n",-level-4,marker,(int)get4l(attr,12));
	printf("%*cOff DACL 0x%x\n",-level-4,marker,(int)get4l(attr,16));
}

void showace(const char *attr, int off, int isdir, int level)
{
	int flags;
	u32 rights;
	char marker;

	if (opt_b)
		marker = '#';
	else
		marker = ' ';
	printf("%*ctype     %d\n",-level,marker,attr[off]);
	switch (attr[off]) {
	case 0 :
		printf("%*cAccess allowed\n",-level-4,marker);
		break;
	case 1 :
		printf("%*cAccess denied\n",-level-4,marker);
		break;
	case 2 :
		printf("%*cSystem audit\n",-level-4,marker);
		break;
	default :
		printf("%*cunknown\n",-level-4,marker);
		break;
	}
	flags = attr[off+1] & 255;
	printf("%*cflags    0x%x\n",-level,marker,flags);
	if (flags & 1)
		printf("%*cObject inherits ACE\n",-level-4,marker);
	if (flags & 2)
		printf("%*cContainer inherits ACE\n",-level-4,marker);
	if (flags & 4)
		printf("%*cDon\'t propagate inherits ACE\n",-level-4,marker);
	if (flags & 8)
		printf("%*cInherit only ACE\n",-level-4,marker);
	if (flags & 0x10)
		printf("%*cACE was inherited\n",-level-4,marker);
	if (flags & 0x40)
		printf("%*cAudit on success\n",-level-4,marker);
	if (flags & 0x80)
		printf("%*cAudit on failure\n",-level-4,marker);

	printf("%*cSize     0x%x\n",-level,marker,get2l(attr,off+2));

	rights = get4l(attr,off+4);
	printf("%*cAcc rgts 0x%lx\n",-level,marker,(long)rights);
	printf("%*cObj specific acc rgts 0x%lx\n",-level-4,marker,(long)rights & 65535);
	if (isdir) /* a directory */ {
		if (rights & 0x01)
			printf("%*cList directory\n",-level-8,marker);
		if (rights & 0x02)
			printf("%*cAdd file\n",-level-8,marker);
		if (rights & 0x04)
			printf("%*cAdd subdirectory\n",-level-8,marker);
		if (rights & 0x08)
			printf("%*cRead EA\n",-level-8,marker);
		if (rights & 0x10)
			printf("%*cWrite EA\n",-level-8,marker);
		if (rights & 0x20)
			printf("%*cTraverse\n",-level-8,marker);
		if (rights & 0x40)
			printf("%*cDelete child\n",-level-8,marker);
		if (rights & 0x80)
			printf("%*cRead attributes\n",-level-8,marker);
		if (rights & 0x100)
			printf("%*cWrite attributes\n",-level-8,marker);
	}
	else {
	     /* see FILE_READ_DATA etc in winnt.h */
		if (rights & 0x01)
			printf("%*cRead data\n",-level-8,marker);
		if (rights & 0x02)
			printf("%*cWrite data\n",-level-8,marker);
		if (rights & 0x04)
			printf("%*cAppend data\n",-level-8,marker);
		if (rights & 0x08)
			printf("%*cRead EA\n",-level-8,marker);
		if (rights & 0x10)
			printf("%*cWrite EA\n",-level-8,marker);
		if (rights & 0x20)
			printf("%*cExecute\n",-level-8,marker);
		if (rights & 0x80)
			printf("%*cRead attributes\n",-level-8,marker);
		if (rights & 0x100)
			printf("%*cWrite attributes\n",-level-8,marker);
	}
	printf("%*cstandard acc rgts 0x%lx\n",-level-4,marker,(long)(rights >> 16) & 127);
	if (rights & 0x10000)
		printf("%*cDelete\n",-level-8,marker);
	if (rights & 0x20000)
		printf("%*cRead control\n",-level-8,marker);
	if (rights & 0x40000)
		printf("%*cWrite DAC\n",-level-8,marker);
	if (rights & 0x80000)
		printf("%*cWrite owner\n",-level-8,marker);
	if (rights & 0x100000)
		printf("%*cSynchronize\n",-level-8,marker);
	if (rights & 0x800000)
		printf("%*cCan access security ACL\n",-level-4,marker);
	if (rights & 0x10000000)
		printf("%*cGeneric all\n",-level-4,marker);
	if (rights & 0x20000000)
		printf("%*cGeneric execute\n",-level-4,marker);
	if (rights & 0x40000000)
		printf("%*cGeneric write\n",-level-4,marker);
	if (rights & 0x80000000)
		printf("%*cGeneric read\n",-level-4,marker);

	printf("%*cSID at 0x%x\n",-level,marker,off+8);
	showsid(attr,off+8,"",level+4);
	printf("%*cSummary :",-level,marker);
	if (attr[off] == 0)
		printf(" grant");
	if (attr[off] == 1)
		printf(" deny");
	if (rights & le32_to_cpu(FILE_GREAD | FILE_GWRITE | FILE_GEXEC)) {
		printf(" ");
		if (rights & le32_to_cpu(FILE_GREAD))
			printf("r");
		if (rights & le32_to_cpu(FILE_GWRITE))
			printf("w");
		if (rights & le32_to_cpu(FILE_GEXEC))
			printf("x");
	} else
		printf(" none");
	if (flags & 11)
		printf(" inherited");
	if (!(flags & 8)) {
		int sz;

		printf(" applied");
		sz = attr[off+9]*4 + 8;
		if (!memcmp(&attr[off+8],&attr[get4l(attr,4)],sz))
			printf(" to owner");
		if (!memcmp(&attr[off+8],&attr[get4l(attr,8)],sz))
			printf(" to group");
	}
	printf("\n");

}

void showacl(const char *attr, int off, int isdir, int level)
{
	int i;
	int cnt;
	int size;
	int x;
	char marker;

	if (opt_b)
		marker = '#';
	else
		marker = ' ';
	size = get2l(attr,off+2);
	printf("%*crevision %d\n",-level,marker,attr[off]);
	printf("%*cACL size %d\n",-level,marker,size);
	cnt = get2l(attr,off+4);
	printf("%*cACE cnt  %d\n",-level,marker,cnt);
	x = 8;
	for (i=0; (i<cnt) && (x < size); i++) {
		printf("%*cACE %d at 0x%x\n",-level,marker,i + 1,off+x);
		showace(attr,off + x,isdir,level+4);
		x += get2l(attr,off + x + 2);
	}
}

void showdacl(const char *attr, int isdir, int level)
{
	int off;
	char marker;

	if (opt_b)
		marker = '#';
	else
		marker = ' ';
	off = get4l(attr,16);
	if (off) {
		if (level)
			printf("%*c",-level,marker);
		printf("DACL\n");
		showacl(attr,off,isdir,level+4);
	} else {
		if (level)
			printf("%*c",-level,marker);
		printf("No DACL\n");
	}
}

void showsacl(const char *attr, int isdir, int level)
{
	int off;
	char marker;

	if (opt_b)
		marker = '#';
	else
		marker = ' ';
	off = get4l(attr,12);
	if (off) {
		if (level)
			printf("%*c",-level,marker);
		printf("SACL\n");
		showacl(attr,off,isdir,level+4);
	}
	else {
		if (level)
			printf("%*c",-level,marker);
		printf("No SACL\n");
	}
}

void showall(const char *attr, int level)
{
	BOOL isdir;

	isdir = guess_dir(attr);
	showheader(attr,level);
	showusid(attr,level);
	showgsid(attr,level);
	showdacl(attr,isdir,level);
	showsacl(attr,isdir,level);
}

#if POSIXACLS
/*
 *		Display a Posix descriptor
 */

void showposix(const struct POSIX_SECURITY *pxdesc)
{
	char txperm[4];
	const char *txtag;
	const char *txtype;
	const struct POSIX_ACL *acl;
	const struct POSIX_ACE *pxace;
	int acccnt;
	int defcnt;
	int firstdef;
	int perms;
	u16 tag;
	s32 id;
	int k,l;

	if (pxdesc) {
		acccnt = pxdesc->acccnt;
		defcnt = pxdesc->defcnt;
		firstdef = pxdesc->firstdef;
		acl = &pxdesc->acl;
		printf("Posix descriptor :\n");
		printf("    acccnt %d\n",acccnt);
		printf("    defcnt %d\n",defcnt);
		printf("    firstdef %d\n",firstdef);
		printf("    mode : 0%03o\n",(int)pxdesc->mode);
		printf("    tagsset : 0x%02x\n",(int)pxdesc->tagsset);
		printf("Posix ACL :\n");
		printf("    version %d\n",(int)acl->version);
		printf("    flags 0x%02x\n",(int)acl->flags);
		for (k=0; k<(acccnt + defcnt); k++) {
			if (k < acccnt)
				l = k;
			else
				l = firstdef + k - acccnt;
			pxace = &acl->ace[l];
			tag = pxace->tag;
			perms = pxace->perms;
			if (tag == POSIX_ACL_SPECIAL) {
				txperm[0] = (perms & S_ISVTX ? 's' : '-');
				txperm[1] = (perms & S_ISUID ? 'u' : '-');
				txperm[2] = (perms & S_ISGID ? 'g' : '-');
			} else {
				txperm[0] = (perms & 4 ? 'r' : '-');
				txperm[1] = (perms & 2 ? 'w' : '-');
				txperm[2] = (perms & 1 ? 'x' : '-');
			}
			txperm[3] = 0;
			if (k >= acccnt)
				txtype = "default";
			else
				txtype = "access ";
			switch (tag) {
			case POSIX_ACL_USER :
				txtag = "USER ";
				break;
			case POSIX_ACL_USER_OBJ :
				txtag = "USR-O";
				break;
			case POSIX_ACL_GROUP :
				txtag = "GROUP";
				break;
			case POSIX_ACL_GROUP_OBJ :
				txtag = "GRP-O";
				break;
			case POSIX_ACL_MASK :
				txtag = "MASK ";
				break;
			case POSIX_ACL_OTHER :
				txtag = "OTHER";
				break;
			case POSIX_ACL_SPECIAL :
				txtag = "SPECL";
				break;
			default :
				txtag = "UNKWN";
				break;
			}
			id = pxace->id;
			printf("ace %d : %s %s %4ld perms 0%03o %s\n",
				l,txtype,txtag,(long)id,
				perms,txperm);
		}
	} else
		printf("** NULL ACL\n");
}

#endif /* POSIXACLS */

#if defined(WIN32) | defined(STSC)

#else

/*
 *		Relay to get usid as defined during mounting
 */

const SID *relay_find_usid(const struct MAPPING *usermapping __attribute__((unused)),
		uid_t uid, SID *defusid)
{
	return (ntfs_get_usid(ntfs_context,uid,(char*)defusid) ?
			defusid : (SID*)NULL);
}

/*
 *		Relay to get gsid as defined during mounting
 */

const SID *relay_find_gsid(const struct MAPPING *groupmapping __attribute__((unused)),
		uid_t gid, SID *defgsid)
{
	return (ntfs_get_usid(ntfs_context,gid,(char*)defgsid) ?
			defgsid : (SID*)NULL);
}

/*
 *		Relay to get uid as defined during mounting
 */

uid_t relay_find_user(const struct MAPPING *mapping __attribute__((unused)),
			const SID *usid)
{
	int uid;

	uid = ntfs_get_user(ntfs_context,(const char*)usid);
	return (uid < 0 ? 0 : uid);
}

/*
 *		Relay to get gid as defined during mounting
 */

gid_t relay_find_group(const struct MAPPING *mapping __attribute__((unused)),
			const SID *gsid)
{
	int gid;

	gid = ntfs_get_group(ntfs_context,(const char*)gsid);
	return (gid < 0 ? 0 : gid);
}

#endif

#if defined(WIN32) | defined(STSC)

/*
 *		Dummy get uid from user name, out of a Linux environment
 */

struct passwd *getpwnam(const char *user)
{
	ntfs_log_error("Cannot interpret id \"%s\"", user);
	ntfs_log_error("please use numeric uids in UserMapping file\n");
	return ((struct passwd*)NULL);
}

/*
 *		Dummy get gid from group name, out of a Linux environment
 */

struct group *getgrnam(const char *group)
{
	ntfs_log_error("Cannot interpret id \"%s\"", group);
	ntfs_log_error("please use numeric gids in UserMapping file\n");
	return ((struct group*)NULL);
}

#endif /* defined(WIN32) | defined(STSC) */

#if POSIXACLS

struct POSIX_SECURITY *linux_permissions_posix(const char *attr, BOOL isdir)
{
	const SECURITY_DESCRIPTOR_RELATIVE *phead;
#if OWNERFROMACL
	const SID *osid;
#endif
	const SID *usid;
	const SID *gsid;
	struct POSIX_SECURITY *posix_desc;

	phead = (const SECURITY_DESCRIPTOR_RELATIVE*)attr;
	gsid = (const SID*)&attr[le32_to_cpu(phead->group)];
#if OWNERFROMACL
	osid = (const SID*)&attr[le32_to_cpu(phead->owner)];
	usid = ntfs_acl_owner((const char*)attr);
#if SELFTESTS & !USESTUBS
	if (!opt_t && !ntfs_same_sid(usid,osid))
		printf("== Linux owner is different from Windows owner\n"); 
#else
	if (!ntfs_same_sid(usid,osid))
		printf("== Linux owner is different from Windows owner\n"); 
#endif
#else
	usid = (const SID*)&attr[le32_to_cpu(phead->owner)];
#endif
	posix_desc = ntfs_build_permissions_posix(context.mapping,
			(const char*)attr, usid, gsid, isdir);
	if (!posix_desc) {
		printf("** Failed to build a Posix descriptor\n");
		errors++;
	}
	return (posix_desc);
}

#endif /* POSIXACLS */

int linux_permissions(const char *attr, BOOL isdir)
{
	const SECURITY_DESCRIPTOR_RELATIVE *phead;
#if OWNERFROMACL
	const SID *osid;
#endif
	const SID *usid;
	const SID *gsid;
	int perm;

	phead = (const SECURITY_DESCRIPTOR_RELATIVE*)attr;
	gsid = (const SID*)&attr[le32_to_cpu(phead->group)];
#if OWNERFROMACL
	osid = (const SID*)&attr[le32_to_cpu(phead->owner)];
	usid = ntfs_acl_owner((const char*)attr);
#if SELFTESTS & !USESTUBS
	if (!opt_t && !ntfs_same_sid(usid,osid))
		printf("== Linux owner is different from Windows owner\n"); 
#else
	if (!ntfs_same_sid(usid,osid))
		printf("== Linux owner is different from Windows owner\n"); 
#endif
#else
	usid = (const SID*)&attr[le32_to_cpu(phead->owner)];
#endif
	perm = ntfs_build_permissions((const char*)attr, usid, gsid, isdir);
	if (perm < 0) {
		printf("** Failed to build permissions\n");
		errors++;
	}
	return (perm);
}

uid_t linux_owner(const char *attr)
{
	const SID *usid;
	uid_t uid;

#if OWNERFROMACL
	usid = ntfs_acl_owner((const char*)attr);
#else
	const SECURITY_DESCRIPTOR_RELATIVE *phead;

	phead = (const SECURITY_DESCRIPTOR_RELATIVE*)attr;
	usid = (const SID*)&attr[le32_to_cpu(phead->owner)];
#endif
#if defined(WIN32) | defined(STSC)
	uid = ntfs_find_user(context.mapping[MAPUSERS],usid);
#else
	if (mappingtype == MAPEXTERN)
		uid = relay_find_user(context.mapping[MAPUSERS],usid);
	else
		uid = ntfs_find_user(context.mapping[MAPUSERS],usid);
#endif
	return (uid);
}

gid_t linux_group(const char *attr)
{
	const SECURITY_DESCRIPTOR_RELATIVE *phead;
	const SID *gsid;
	gid_t gid;

	phead = (const SECURITY_DESCRIPTOR_RELATIVE*)attr;
	gsid = (const SID*)&attr[le32_to_cpu(phead->group)];
#if defined(WIN32) | defined(STSC)
	gid = ntfs_find_group(context.mapping[MAPGROUPS],gsid);
#else
	if (mappingtype == MAPEXTERN)
		gid = relay_find_group(context.mapping[MAPGROUPS],gsid);
	else
		gid = ntfs_find_group(context.mapping[MAPGROUPS],gsid);
#endif
	return (gid);
}

void newblock(s32 key)
{
	struct SECURITY_DATA *psecurdata;
	int i;

	if ((key > 0) && (key < MAXSECURID) && !securdata[key >> SECBLKSZ]) {
		securdata[key >> SECBLKSZ] =
			(struct SECURITY_DATA*)malloc((1 << SECBLKSZ)*sizeof(struct SECURITY_DATA));
		if (securdata[key >> SECBLKSZ])
			for (i=0; i<(1 << SECBLKSZ); i++) {
				psecurdata = &securdata[key >> SECBLKSZ][i];
				psecurdata->filecount = 0;
				psecurdata->mode = 0;
				psecurdata->flags = 0;
				psecurdata->attr = (char*)NULL;
			}
	}
}

void freeblocks(void)
{
	int i,j;
	struct SECURITY_DATA *psecurdata;

	for (i=0; i<(MAXSECURID + (1 << SECBLKSZ) - 1)/(1 << SECBLKSZ); i++)
		if (securdata[i]) {
			for (j=0; j<(1 << SECBLKSZ); j++) {
				psecurdata = &securdata[i][j];
				if (psecurdata->attr)
					free(psecurdata->attr);
			}
			free(securdata[i]);
		}
}

/*
 *		Basic read from a user mapping file (Win32)
 */

int basicread(void *fileid, char *buf, size_t size,
		off_t pos __attribute__((unused)))
{
	return (read(*(int*)fileid, buf, size));
}

#if SELFTESTS & !USESTUBS

/*
 *		Read a dummy mapping file for tests
 */

int dummyread(void *fileid  __attribute__((unused)),
		char *buf, size_t size, off_t pos)
{
	size_t sz;

	if (pos >= (off_t)(sizeof(dummymapping) - 1))
		sz = 0;
	else
		if ((size + pos) >= (sizeof(dummymapping) - 1))
			sz = sizeof(dummymapping) - 1 - pos;
		else
			sz = size;
	if (sz > 0)
		memcpy(buf,&dummymapping[pos],sz);
	return (sz);
}

#endif /* POSIXACLS & SELFTESTS & !USESTUBS */

/*
 *		Apply default single user mapping
 *	returns zero if successful
 */

static int do_default_mapping(struct MAPPING *mapping[],
			 const SID *usid)
{
	struct MAPPING *usermapping;
	struct MAPPING *groupmapping;
	SID *sid;
	int sidsz;
	int res;

	res = -1;
	sidsz = ntfs_sid_size(usid);
#if USESTUBS
	sid = (SID*)stdmalloc(sidsz); /* will be freed within the library */
#else
	sid = (SID*)ntfs_malloc(sidsz);
#endif
	if (sid) {
		memcpy(sid,usid,sidsz);
#if USESTUBS
		usermapping = (struct MAPPING*)stdmalloc(sizeof(struct MAPPING));
#else
		usermapping = (struct MAPPING*)ntfs_malloc(sizeof(struct MAPPING));
#endif
		if (usermapping) {
#if USESTUBS
			groupmapping = (struct MAPPING*)stdmalloc(sizeof(struct MAPPING));
#else
			groupmapping = (struct MAPPING*)ntfs_malloc(sizeof(struct MAPPING));
#endif
			if (groupmapping) {
				usermapping->sid = sid;
				usermapping->xid = 0;
				usermapping->next = (struct MAPPING*)NULL;
				groupmapping->sid = sid;
				groupmapping->xid = 0;
				groupmapping->next = (struct MAPPING*)NULL;
				mapping[MAPUSERS] = usermapping;
				mapping[MAPGROUPS] = groupmapping;
				res = 0;
			}
		}
	}
	return (res);
}

/*
 *		Build the user mapping
 *	- according to a mapping file if defined (or default present),
 *	- or try default single user mapping if possible
 *
 *	The mapping is specific to a mounted device
 *	No locking done, mounting assumed non multithreaded
 *
 *	returns zero if mapping is successful
 *	(failure should not be interpreted as an error)
 */

int local_build_mapping(struct MAPPING *mapping[], const char *usermap_path)
{
#ifdef WIN32
	char mapfile[sizeof(MAPDIR) + sizeof(MAPFILE) + 6];
	char currpath[261];
#else
	char *mapfile;
	char *p;
#endif
	int fd;
	struct MAPLIST *item;
	struct MAPLIST *firstitem = (struct MAPLIST*)NULL;
	struct MAPPING *usermapping;
	struct MAPPING *groupmapping;
	static struct {
		u8 revision;
		u8 levels;
		be16 highbase;
		be32 lowbase;
		le32 level1;
		le32 level2;
		le32 level3;
		le32 level4;
		le32 level5;
	} defmap = {
		1, 5, const_cpu_to_be16(0), const_cpu_to_be32(5),
		const_cpu_to_le32(21),
		const_cpu_to_le32(DEFSECAUTH1), const_cpu_to_le32(DEFSECAUTH2),
		const_cpu_to_le32(DEFSECAUTH3), const_cpu_to_le32(DEFSECBASE)
	} ;

	/* be sure not to map anything until done */
	mapping[MAPUSERS] = (struct MAPPING*)NULL;
	mapping[MAPGROUPS] = (struct MAPPING*)NULL;

	if (usermap_path) {
#ifdef WIN32
/* TODO : check whether the device can store acls */
		strcpy(mapfile,"x:\\" MAPDIR "\\" MAPFILE);
		if (((le16*)usermap_path)[1] == ':')
  			mapfile[0] = usermap_path[0];
		else {
			GetModuleFileName(NULL, currpath, 261);
			mapfile[0] = currpath[0];
		}
		fd = open(mapfile,O_RDONLY);
#else
		fd = 0;
		mapfile = (char*)malloc(MAXFILENAME);
		if (mapfile) {
			/* build a full path to locate the mapping file */
			if ((usermap_path[0] != '/')
			   && getcwd(mapfile,MAXFILENAME)) {
				strcat(mapfile,"/");
				strcat(mapfile,usermap_path);
			} else
				strcpy(mapfile,usermap_path);
			p = strrchr(mapfile,'/');
			if (p)
				do {
					strcpy(p,"/" MAPDIR "/" MAPFILE);
					fd = open(mapfile,O_RDONLY);
					if (fd <= 0) {
						*p = 0;
						p = strrchr(mapfile,'/');
						if (p == mapfile)
							p = (char*)NULL;
					}
				} while ((fd <= 0) && p);
			free(mapfile);
			if (!p) {
				printf("** Could not find the user mapping file\n");
				if (usermap_path[0] != '/')
					printf("   Retry with full path of file\n");
				errors++;
			}
		}
#endif
		if (fd > 0) {
			firstitem = ntfs_read_mapping(basicread, (void*)&fd);
			close(fd);
		}
	} else {
#if SELFTESTS & !USESTUBS
		firstitem = ntfs_read_mapping(dummyread, (void*)NULL);
#endif
	}

	if (firstitem) {
		usermapping = ntfs_do_user_mapping(firstitem);
		groupmapping = ntfs_do_group_mapping(firstitem);
		if (usermapping && groupmapping) {
			mapping[MAPUSERS] = usermapping;
			mapping[MAPGROUPS] = groupmapping;
		} else
			ntfs_log_error("There were no valid user or no valid group\n");
		/* now we can free the memory copy of input text */
		/* and rely on internal representation */
		while (firstitem) {
			item = firstitem->next;
#if USESTUBS
			stdfree(firstitem); /* allocated within library */
#else
			free(firstitem);
#endif
			firstitem = item;
		}
	} else {
		do_default_mapping(mapping,(const SID*)&defmap);
	}
	if (mapping[MAPUSERS])
		mappingtype = MAPLOCAL;
	return (!mapping[MAPUSERS]);
}

/*
 *		Get an hexadecimal number (source with MSB first)
 */

u32 getmsbhex(const char *text)
{
	u32 v;
	int b;
	BOOL ok;

	v = 0;
	ok = TRUE;
	do {
		b = *text++;
		if ((b >= '0') && (b <= '9'))
			v = (v << 4) + b - '0';
		else
			if ((b >= 'a') && (b <= 'f'))
				v = (v << 4) + b - 'a' + 10;
			else
				if ((b >= 'A') && (b <= 'F'))
					v = (v << 4) + b - 'A' + 10;
				else ok = FALSE;
	} while (ok);
	return (v);
}


/*
 *		Get an hexadecimal number (source with LSB first)
 *	An odd number of digits might yield a strange result
 */

u32 getlsbhex(const char *text)
{
	u32 v;
	int b;
	BOOL ok;
	int pos;

	v = 0;
	ok = TRUE;
	pos = 0;
	do {
		b = *text++;
		if ((b >= '0') && (b <= '9'))
			v |= (u32)(b - '0') << (pos ^ 4);
		else
			if ((b >= 'a') && (b <= 'f'))
				v |= (u32)(b - 'a' + 10) << (pos ^ 4);
			else
				if ((b >= 'A') && (b <= 'F'))
					v |= (u32)(b - 'A' + 10) << (pos ^ 4);
				else ok = FALSE;
		pos += 4;
	} while (ok);
	return (v);
}


/*
 *		Check whether a line looks like an hex dump
 */

BOOL ishexdump(const char *line, int first, int lth)
{
	BOOL ok;
	int i;
	int b;

	ok = (first >= 0) && (lth >= (first + 16));
	for (i=0; ((first+i)<lth) && ok; i++) {
		b = line[first + i];
		if ((i == 6)
		    || (i == 7)
		    || (i == 16)
		    || (i == 25)
		    || (i == 34)
		    || (i == 43))
			ok = (b == ' ') || (b == '\n');
		else
			ok = ((b >= '0') && (b <= '9'))
			    || ((b >= 'a') && (b <= 'f'))
			    || ((b >= 'A') && (b <= 'F'));
	}
	return (ok);
}


/*
 *		Display security descriptors from a file
 *	This is typically to convert a verbose output to a very verbose one
 */

void showhex(FILE *fd)
{
	static char attr[MAXATTRSZ];
	char line[MAXLINE+1];
#if POSIXACLS
	struct POSIX_SECURITY *pxdesc;
#endif
	int lth;
	int first;
	unsigned int pos;
	u32 v;
	int c;
	int isdir;
	int mode;
	unsigned int off;
	int i;
	BOOL isdump;
	BOOL done;

	pos = 0;
	off = 0;
	done = FALSE;
	do {
			/* input a (partial) line without displaying */
		lth = 0;
		first = -1;
		do {
			c = getc(fd);
			if ((c != ' ') && (first < 0))
				first = lth;
			if (c == EOF)
				done = TRUE;
			else
				if (c != '\r')
					line[lth++] = c;
		} while (!done && (c != '\n') && (lth < MAXLINE));
			/* check whether this looks like an hexadecimal dump */
		isdump = ishexdump(line, first, lth);
		if (isdump) off = getmsbhex(&line[first]);
			/* line is not an hexadecimal dump */
			/* display what we have in store */
		if ((!isdump || !off) && pos && ntfs_valid_descr((char*)attr,pos)) {
			printf("	Computed hash : 0x%08lx\n",
				    (unsigned long)hash((le32*)attr,
				    ntfs_attr_size(attr)));
			isdir = guess_dir(attr);
			printf("    Estimated type : %s\n",(isdir ? "directory" : "file"));
			showheader(attr,4);
			showusid(attr,4);
			showgsid(attr,4);
			showdacl(attr,isdir,4);
			showsacl(attr,isdir,4);
			mode = linux_permissions(attr,isdir);
			printf("Interpreted Unix mode 0%03o\n",mode);
#if POSIXACLS
				/*
				 * Posix display not possible when user
				 * mapping is not available (option -h)
				 */
			if (mappingtype != MAPNONE) {
				pxdesc = linux_permissions_posix(attr,isdir);
				if (pxdesc) {
					showposix(pxdesc);
					free(pxdesc);
				}
			}
#endif
			pos = 0;
		}
		if (isdump && !off)
			pos = off;
			/* line looks like an hexadecimal dump */
			/* decode it into attribute */
		if (isdump && (off == pos)) {
			for (i=first+8; i<lth; i+=9) {
				v = getlsbhex(&line[i]);
				*(le32*)&attr[pos] = cpu_to_le32(v);
				pos += 4;
			}
		}
			/* display (full) current line */
		if (lth) printf("! ");
		for (i=0; i<lth; i++) {
			c = line[i];
			putchar(c);
		}
		while (!done && (c != '\n')) {
			c = getc(fd);
			if (c == EOF)
				done = TRUE;
			else
				putchar(c);
		}
	} while (!done);
}

BOOL applyattr(const char *fullname, const char *attr,
			BOOL withattr, int attrib, s32 key)
{
	struct SECURITY_DATA *psecurdata;
	const char *curattr;
	char *newattr;
	int selection;
	BOOL bad;
	BOOL badattrib;
	BOOL err;
#ifdef WIN32
	HANDLE htoken;
	TOKEN_PRIVILEGES tkp;
#endif

	err = FALSE;
	psecurdata = (struct SECURITY_DATA*)NULL;
	curattr = (const char*)NULL;
	newattr = (char*)NULL;
	if ((key > 0) && (key < MAXSECURID)) {
		if (!securdata[key >> SECBLKSZ])
			newblock(key);
		if (securdata[key >> SECBLKSZ]) {
			psecurdata = &securdata[key >> SECBLKSZ]
					[key & ((1 << SECBLKSZ) - 1)];
		}
	}

			/* If we have a usable attrib value. Try applying */
	badattrib = FALSE;
	if (opt_e && (attrib != INVALID_FILE_ATTRIBUTES)) {
#ifdef WIN32
		badattrib = !SetFileAttributesW((LPCWSTR)fullname, attrib);
#else
		badattrib = !ntfs_set_file_attributes(ntfs_context, fullname, attrib);
#endif
		if (badattrib) {
			printf("** Could not set Windows attrib of ");
			printname(stdout,fullname);
			printf(" to 0x%x\n", attrib);
			printerror(stdout);
			warnings++;
		}
	}

	if (withattr) {
		if (psecurdata) {
			newattr = (char*)malloc(ntfs_attr_size(attr));
			if (newattr) {
				memcpy(newattr,attr,ntfs_attr_size(attr));
				psecurdata->attr = newattr;
			}
		}
		curattr = attr;
	} else
		/*
		 * No explicit attr in backup, use attr defined
		 * previously for the same id
		 */
		if (psecurdata)
			curattr = psecurdata->attr;
       

	if (curattr) {
#ifdef WIN32
		selection = OWNER_SECURITY_INFORMATION
			| GROUP_SECURITY_INFORMATION
			| DACL_SECURITY_INFORMATION;
		if (OpenProcessToken(GetCurrentProcess(), 
				TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &htoken)) {
			if (LookupPrivilegeValue(NULL, SE_SECURITY_NAME,
					&tkp.Privileges[0].Luid)) {
				tkp.PrivilegeCount = 1;
				tkp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
				if (AdjustTokenPrivileges(htoken, FALSE, &tkp, 0, NULL, 0)) {
					selection |= SACL_SECURITY_INFORMATION;
				}
			}
		}
		bad = !SetFileSecurityW((LPCWSTR)fullname,
			selection, (char*)curattr);
		if (bad)
			switch (GetLastError()) {
			case 1307 :
			case 1314 :
				printf("** Could not set owner or SACL of ");
				printname(stdout,fullname);
				printf(", retrying with no owner or SACL setting\n");
				warnings++;
				bad = !SetFileSecurityW((LPCWSTR)fullname,
					selection & ~OWNER_SECURITY_INFORMATION
					& ~SACL_SECURITY_INFORMATION, (char*)curattr);
				break;
			default :
				break;
			}
		/* Release privileges once we are done*/
		if (selection ^ SACL_SECURITY_INFORMATION) {
			tkp.Privileges[0].Attributes = 0;
			AdjustTokenPrivileges(htoken, FALSE, &tkp, 0, NULL, 0);
		}
#else
		selection = OWNER_SECURITY_INFORMATION
			| GROUP_SECURITY_INFORMATION
			| DACL_SECURITY_INFORMATION
			| SACL_SECURITY_INFORMATION;
		bad = !ntfs_set_file_security(ntfs_context,fullname,
			selection, (const char*)curattr);
#endif
		if (bad) {
			printf("** Could not set the ACL of ");
			printname(stdout,fullname);
			printf("\n");
			printerror(stdout);
			err = TRUE;
		} else
			if (opt_v) {
				if (opt_e && !badattrib)
					printf("ACL and attrib have been applied to ");
				else
					printf("ACL has been applied to ");
				printname(stdout,fullname);
				printf("\n");

			}
	} else {
		printf("** There was no valid ACL for ");
		printname(stdout,fullname);
		printf("\n");
		err = TRUE;
	}
	return (!err);
}

/*
 *		Restore security descriptors from a file
 */

BOOL restore(FILE *fd)
{
	static char attr[MAXATTRSZ];
	char line[MAXFILENAME+25];
	char fullname[MAXFILENAME+25];
	SECURITY_DESCRIPTOR_RELATIVE *phead;
	int lth;
	int first;
	unsigned int pos;
	int c;
	int isdir;
	int mode;
	s32 key;
	BOOL isdump;
	unsigned int off;
	u32 v;
	u32 oldhash;
	int i;
	int count;
	int attrib;
	BOOL withattr;
	BOOL done;

	pos = 0;
	off = 0;
	done = FALSE;
	withattr = FALSE;
	oldhash = 0;
	key = 0;
	errors = 0;
	count = 0;
	fullname[0] = 0;
	attrib = INVALID_FILE_ATTRIBUTES;
	do {
			/* input a (partial) line without processing */
		lth = 0;
		first = -1;
		do {
			c = getc(fd);
			if ((c != ' ') && (first < 0))
				first = lth;
			if (c == EOF)
				done = TRUE;
			else
				if (c != '\r')
					line[lth++] = c;
		} while (!done && (c != '\n') && (lth < (MAXFILENAME + 24)));
			/* check whether this looks like an hexadecimal dump */
		isdump = ishexdump(line, first, lth);
		if (isdump) off = getmsbhex(&line[first]);
			/* line is not an hexadecimal dump */
			/* apply what we have in store */
		if ((!isdump || !off) && pos && ntfs_valid_descr((char*)attr,pos)) {
			withattr = TRUE;
			if (opt_v >= 2) {
				printf("	Computed hash : 0x%08lx\n",
					    (unsigned long)hash((le32*)attr,
					    ntfs_attr_size(attr)));
				isdir = guess_dir(attr);
				printf("    Estimated type : %s\n",(isdir ? "directory" : "file"));
				showheader(attr,4);
				showusid(attr,4);
				showgsid(attr,4);
				showdacl(attr,isdir,4);
				showsacl(attr,isdir,4);
				mode = linux_permissions(attr,isdir);
				printf("Interpreted Unix mode 0%03o\n",mode);
			}
			pos = 0;
		}
		if (isdump && !off)
			pos = off;
			/* line looks like an hexadecimal dump */
			/* decode it into attribute */
		if (isdump && (off == pos)) {
			for (i=first+8; i<lth; i+=9) {
				v = getlsbhex(&line[i]);
				*(le32*)&attr[pos] = cpu_to_le32(v);
				pos += 4;
			}
		}
			/* display (full) current line unless dump or verbose */
		if (!isdump || opt_v) {
			if(lth) printf("! ");
			for (i=0; i<lth; i++) {
				c = line[i];
				putchar(c);
			}
		}
		while (!done && (c != '\n')) {
			c = getc(fd);
			if (c == EOF)
				done = TRUE;
			else
				if (!isdump || opt_v)
					putchar(c);
		}

		line[lth] = 0;
		while ((lth > 0)
		    && ((line[lth-1] == '\n') || (line[lth-1] == '\r')))
			line[--lth] = 0;
		if (!strncmp(line,"Computed hash : 0x",18))
			oldhash = getmsbhex(&line[18]);
		if (!strncmp(line,"Security key : 0x",17))
			key = getmsbhex(&line[17]);
		if (!strncmp(line,"Windows attrib : 0x",19))
			attrib = getmsbhex(&line[19]);
		if (done
		    || !strncmp(line,"File ",5)
		    || !strncmp(line,"Directory ",10)) {
			/*
			 *  New file or directory (or end of file) :
			 *  apply attribute just collected
			 *  or apply attribute defined from current key
			 */

			if (withattr
			    && oldhash
			    && (hash((const le32*)attr,ntfs_attr_size(attr)) != oldhash)) {
				printf("** ACL rejected, its hash is not as expected\n");
				errors++;
			} else
				if (fullname[0]) {
					phead = (SECURITY_DESCRIPTOR_RELATIVE*)attr;
					/* set the request for auto-inheritance */
					if (phead->control & SE_DACL_AUTO_INHERITED)
						phead->control |= SE_DACL_AUTO_INHERIT_REQ;
					if (!applyattr(fullname,attr,withattr,
							attrib,key))
						errors++;
					else
						count++;
				}
			/* save current file or directory name */
			withattr = FALSE;
			key = 0;
			oldhash = 0;
			attrib = INVALID_FILE_ATTRIBUTES;
			if (!done) {
#ifdef WIN32
				if (!strncmp(line,"File ",5))
					makeutf16(fullname,&line[5]);
				else
					makeutf16(fullname,&line[10]);
#else
				if (!strncmp(line,"File ",5))
					strcpy(fullname,&line[5]);
				else
					strcpy(fullname,&line[10]);
#endif
			}
		}
	} while (!done);
	printf("%d ACLs have been applied\n",count);
	return (FALSE);
}

/*
 *		Open the security API in rw mode for an ACL restoration
 */

#ifdef WIN32
#else
BOOL dorestore(const char *volume, FILE *fd)
{
	BOOL err;

	err = FALSE;
	if (!getuid()) {
 		if (open_security_api()) {
			if (open_volume(volume,MS_NONE)) {
				if (restore(fd)) err = TRUE;
				close_volume(volume);
			} else {
				fprintf(stderr,"Could not open volume %s\n",volume);
				printerror(stderr);
				err = TRUE;
			}
			close_security_api();
		} else {
			fprintf(stderr,"Could not open security API\n");
			printerror(stderr);
			err = TRUE;
		}
	} else {
		fprintf(stderr,"Restore is only possible as root\n");
		err = TRUE;
	}
	return (err);
}
#endif /* WIN32 */

#if POSIXACLS & SELFTESTS & !USESTUBS

/*
 *		Merge Posix ACL rights into an u32 (self test only)
 *
 *	Result format : -----rwxrwxrwxrwxrwx---rwxrwxrwx
 *                           U1 U2 G1 G2  M     o  g  w
 *
 *	Only two users (U1, U2) and two groups (G1, G2) taken into account
 */
u32 merge_rights(const struct POSIX_SECURITY *pxdesc, BOOL def)
{
	const struct POSIX_ACE *pxace;
	int i;
	int users;
	int groups;
	int first;
	int last;
	u32 rights;

	rights = 0;
	users = 0;
	groups = 0;
	if (def) {
		first = pxdesc->firstdef;
		last = pxdesc->firstdef + pxdesc->defcnt - 1;
	} else {
		first = 0;
		last = pxdesc->acccnt - 1;
	}
	pxace = pxdesc->acl.ace;
	for (i=first; i<=last; i++) {
		switch (pxace[i].tag) {
		case POSIX_ACL_USER_OBJ :
			rights |= (pxace[i].perms & 7) << 6;
			break;
		case POSIX_ACL_USER :
			if (users < 2)
				rights |= ((u32)pxace[i].perms & 7) << (24 - 3*users);
			users++;
			break;
		case POSIX_ACL_GROUP_OBJ :
			rights |= (pxace[i].perms & 7) << 3;
			break;
		case POSIX_ACL_GROUP :
			if (groups < 2)
				rights |= ((u32)pxace[i].perms & 7) << (18 - 3*groups);
			groups++;
			break;
		case POSIX_ACL_MASK :
			rights |= ((u32)pxace[i].perms & 7) << 12;
			break;
		case POSIX_ACL_OTHER :
			rights |= (pxace[i].perms & 7);
			break;
		default :
			break;
		}
	}
	return (rights);
}

void tryposix(struct POSIX_SECURITY *pxdesc)
{
	le32 owner_sid[] = /* S-1-5-21-3141592653-589793238-462843383-1016 */
		{
		cpu_to_le32(0x501), cpu_to_le32(0x05000000), cpu_to_le32(21),
		cpu_to_le32(DEFSECAUTH1), cpu_to_le32(DEFSECAUTH2),
		cpu_to_le32(DEFSECAUTH3), cpu_to_le32(1016)
		} ;
	le32 group_sid[] = /* S-1-5-21-3141592653-589793238-462843383-513 */
		{
		cpu_to_le32(0x501), cpu_to_le32(0x05000000), cpu_to_le32(21),
		cpu_to_le32(DEFSECAUTH1), cpu_to_le32(DEFSECAUTH2),
		cpu_to_le32(DEFSECAUTH3), cpu_to_le32(513)
		} ;

	char *attr;
	BOOL isdir;
	mode_t mode;
	struct POSIX_SECURITY *newpxdesc;
	struct POSIX_SECURITY *oldpxdesc;
	static char *oldattr = (char*)NULL;

	isdir = FALSE;
	if (oldattr) {
		oldpxdesc = linux_permissions_posix(oldattr, isdir);
		newpxdesc = ntfs_merge_descr_posix(pxdesc, oldpxdesc);
		if (!newpxdesc)
			newpxdesc = pxdesc;
		free(oldpxdesc);
		if (opt_v) {
			printf("merged descriptors :\n");
			showposix(newpxdesc);
		}
	} else
		newpxdesc = pxdesc;
	attr = ntfs_build_descr_posix(context.mapping,newpxdesc,
			isdir,(SID*)owner_sid,(SID*)group_sid);
	if (attr && ntfs_valid_descr(attr, ntfs_attr_size(attr))) {
		if (opt_v)
			hexdump(attr,ntfs_attr_size(attr),8);
		if (opt_v >= 2) {
			showheader(attr,4);
			showusid(attr,4);
			showgsid(attr,4);
			showdacl(attr,isdir,4);
			showsacl(attr,isdir,4);
			mode = linux_permissions(attr,isdir);
			printf("Interpreted Unix mode 0%03o\n",mode);
			printf("Interpreted back Posix descriptor :\n");
			newpxdesc = linux_permissions_posix(attr,isdir);
			showposix(newpxdesc);
			free(newpxdesc);
		}
		if (oldattr) free(oldattr);
		oldattr = attr;
	}
}

static BOOL same_posix(struct POSIX_SECURITY *pxdesc1,
			struct POSIX_SECURITY *pxdesc2)
{
	BOOL same;
	int i;

	same = pxdesc1
		&& pxdesc2
		&& (pxdesc1->mode == pxdesc2->mode)
		&& (pxdesc1->acccnt == pxdesc2->acccnt)
		&& (pxdesc1->defcnt == pxdesc2->defcnt)
		&& (pxdesc1->firstdef == pxdesc2->firstdef)
		&& (pxdesc1->tagsset == pxdesc2->tagsset)
		&& (pxdesc1->acl.version == pxdesc2->acl.version)
		&& (pxdesc1->acl.flags == pxdesc2->acl.flags);
	i = 0;
	while (same && (i < pxdesc1->acccnt)) {
		same = (pxdesc1->acl.ace[i].tag == pxdesc2->acl.ace[i].tag)
		   && (pxdesc1->acl.ace[i].perms == pxdesc2->acl.ace[i].perms)
		   && (pxdesc1->acl.ace[i].id == pxdesc2->acl.ace[i].id);
		i++;
	}
	i = pxdesc1->firstdef;
	while (same && (i < pxdesc1->firstdef + pxdesc1->defcnt)) {
		same = (pxdesc1->acl.ace[i].tag == pxdesc2->acl.ace[i].tag)
		   && (pxdesc1->acl.ace[i].perms == pxdesc2->acl.ace[i].perms)
		   && (pxdesc1->acl.ace[i].id == pxdesc2->acl.ace[i].id);
		i++;
	}
	return (same);
}

#endif /* POSIXACLS & SELFTESTS & !USESTUBS */

#if SELFTESTS & !USESTUBS

/*
 *		Build a dummy security descriptor
 *	returns descriptor in allocated memory, must free() after use
 */

static char *build_dummy_descr(BOOL isdir __attribute__((unused)),
			const SID *usid, const SID *gsid,
			int cnt, 
			 /* seq of int allow, SID *sid, int flags, u32 mask */
			...)
{
	char *attr;
	int attrsz;
	SECURITY_DESCRIPTOR_RELATIVE *pnhead;
	ACL *pacl;
	ACCESS_ALLOWED_ACE *pace;
	va_list ap;
	const SID *sid;
	u32 umask;
	le32 mask;
	int flags;
	BOOL allow;
	int pos;
	int usidsz;
	int gsidsz;
	int sidsz;
	int aclsz;
	int i;

	if (usid)
		usidsz = ntfs_sid_size(usid);
	else
		usidsz = 0;
	if (gsid)
		gsidsz = ntfs_sid_size(gsid);
	else
		gsidsz = 0;


	/* allocate enough space for the new security attribute */
	attrsz = sizeof(SECURITY_DESCRIPTOR_RELATIVE)	/* header */
	    + usidsz + gsidsz	/* usid and gsid */
	    + sizeof(ACL)	/* acl header */
	    + cnt*40;

	attr = (char*)ntfs_malloc(attrsz);
	if (attr) {
		/* build the main header part */
		pnhead = (SECURITY_DESCRIPTOR_RELATIVE*) attr;
		pnhead->revision = SECURITY_DESCRIPTOR_REVISION;
		pnhead->alignment = 0;
			/*
			 * The flag SE_DACL_PROTECTED prevents the ACL
			 * to be changed in an inheritance after creation
			 */
		pnhead->control = SE_DACL_PRESENT | SE_DACL_PROTECTED
				    | SE_SELF_RELATIVE;
			/*
			 * Windows prefers ACL first, do the same to
			 * get the same hash value and avoid duplication
			 */
		/* build the ACL header */
		pos = sizeof(SECURITY_DESCRIPTOR_RELATIVE);
		pacl = (ACL*)&attr[pos];
		pacl->revision = ACL_REVISION;
		pacl->alignment1 = 0;
		pacl->size = cpu_to_le16(0); /* fixed later */
		pacl->ace_count = cpu_to_le16(cnt);
		pacl->alignment2 = cpu_to_le16(0);

		/* enter the ACEs */

		pos += sizeof(ACL);
		aclsz = sizeof(ACL);
		va_start(ap,cnt);
		for (i=0; i<cnt; i++) {
			pace = (ACCESS_ALLOWED_ACE*)&attr[pos];
			allow = va_arg(ap,int);
			sid = va_arg(ap,SID*);
			flags = va_arg(ap,int);
			umask = va_arg(ap,u32);
			mask = cpu_to_le32(umask);
			sidsz = ntfs_sid_size(sid);
			pace->type = (allow ? ACCESS_ALLOWED_ACE_TYPE : ACCESS_DENIED_ACE_TYPE);
			pace->flags = flags;
			pace->size = cpu_to_le16(sidsz + 8);
			pace->mask = mask;
			memcpy(&pace->sid,sid,sidsz);
			aclsz += sidsz + 8;
			pos += sidsz + 8;
		}
		va_end(ap);

		/* append usid and gsid if defined */
		/* positions of ACL, USID and GSID into header */
		pnhead->owner = cpu_to_le32(0);
		pnhead->group = cpu_to_le32(0);
		if (usid) {
			memcpy(&attr[pos], usid, usidsz);
			pnhead->owner = cpu_to_le32(pos);
		}
		if (gsid) {
			memcpy(&attr[pos + usidsz], gsid, gsidsz);
			pnhead->group = cpu_to_le32(pos + usidsz);
		}
		/* positions of DACL and SACL into header */
		pnhead->sacl = cpu_to_le32(0);
		if (cnt) {
			pacl->size = cpu_to_le16(aclsz);
			pnhead->dacl =
			    cpu_to_le32(sizeof(SECURITY_DESCRIPTOR_RELATIVE));
		} else
			pnhead->dacl = cpu_to_le32(0);
		if (!ntfs_valid_descr(attr,pos+usidsz+gsidsz)) {
			printf("** Bad sample descriptor\n");
			free(attr);
			attr = (char*)NULL;
			errors++;
		}
	} else
		errno = ENOMEM;
	return (attr);
}

/*
 *		Check a few samples with special conditions
 */

void check_samples()
{
	char *descr = (char*)NULL;
	BOOL isdir = FALSE;
	mode_t perms;
	mode_t expect = 0;
	int cnt;
	u32 expectacc;
	u32 expectdef;
#if POSIXACLS
	u32 accrights;
	u32 defrights;
	mode_t mixmode;
	struct POSIX_SECURITY *pxdesc;
	struct POSIX_SECURITY *pxsample;
	const char *txsample;
#endif
	le32 owner1[] = /* S-1-5-21-1833069642-4243175381-1340018762-1003 */
		{
		cpu_to_le32(0x501), cpu_to_le32(0x05000000), cpu_to_le32(21),
		cpu_to_le32(1833069642), cpu_to_le32(4243175381),
		cpu_to_le32(1340018762), cpu_to_le32(1003)
		} ;
	le32 group1[] = /* S-1-5-21-1833069642-4243175381-1340018762-513 */
		{
		cpu_to_le32(0x501), cpu_to_le32(0x05000000), cpu_to_le32(21),
		cpu_to_le32(1833069642), cpu_to_le32(4243175381),
		cpu_to_le32(1340018762), cpu_to_le32(513)
		} ;
	le32 group2[] = /* S-1-5-21-1607551490-981732888-1819828000-513 */
		{
		cpu_to_le32(0x501), cpu_to_le32(0x05000000), cpu_to_le32(21),
		cpu_to_le32(1607551490), cpu_to_le32(981732888),
		cpu_to_le32(1819828000), cpu_to_le32(513)
		} ;
	le32 owner3[] = /* S-1-5-21-3141592653-589793238-462843383-1016 */
		{
		cpu_to_le32(0x501), cpu_to_le32(0x05000000), cpu_to_le32(21),
		cpu_to_le32(DEFSECAUTH1), cpu_to_le32(DEFSECAUTH2),
		cpu_to_le32(DEFSECAUTH3), cpu_to_le32(1016)
		} ;
	le32 group3[] = /* S-1-5-21-3141592653-589793238-462843383-513 */
		{
		cpu_to_le32(0x501), cpu_to_le32(0x05000000), cpu_to_le32(21),
		cpu_to_le32(DEFSECAUTH1), cpu_to_le32(DEFSECAUTH2),
		cpu_to_le32(DEFSECAUTH3), cpu_to_le32(513)
		} ;

#if POSIXACLS
	struct {
		struct POSIX_SECURITY head;
		struct POSIX_ACE ace[4];
	} sampletry1 =
	{
		{ 0645, 4, 0, 4, 0x35,
			{ POSIX_VERSION, 0, 0 }
		},
		{
			{ 1, 6, -1 },
			{ 4, 5, -1 },
			{ 16, 4, -1 },
			{ 32, 5, -1 }
		}
	} ;

	struct {
		struct POSIX_SECURITY head;
		struct POSIX_ACE ace[6];
	} sampletry3 =
	{
		{ 0100, 6, 0, 6, 0x3f,
			{ POSIX_VERSION, 0, 0 }
		},
		{
			{ 1, 1, -1 },
			{ 2, 3, 1000 },
			{ 4, 1, -1 },
			{ 8, 3, 1002 },
			{ 16, 0, -1 },
			{ 32, 0, -1 }
		}
	} ;

	struct {
		struct POSIX_SECURITY head;
		struct POSIX_ACE ace[8];
	} sampletry4 =
	{
		{ 0140, 8, 0, 8, 0x3f,
			{ POSIX_VERSION, 0, 0 }
		},
		{
			{ 1, 1, -1 },
			{ 2, 3, 516 },
			{ 2, 6, 1000 },
			{ 4, 1, -1 },
			{ 8, 6, 500 },
			{ 8, 3, 1002 },
			{ 16, 4, -1 },
			{ 32, 0, -1 }
		}
	} ;

	struct {
		struct POSIX_SECURITY head;
		struct POSIX_ACE ace[6];
	} sampletry5 =
	{
		{ 0454, 6, 0, 6, 0x3f,
			{ POSIX_VERSION, 0, 0 }
		},
		{
			{ 1, 4, -1 },
			{ 2, 5, 516 },
			{ 4, 4, -1 },
			{ 8, 6, 500 },
			{ 16, 5, -1 },
			{ 32, 4, -1 }
		}
	} ;

	struct {
		struct POSIX_SECURITY head;
		struct POSIX_ACE ace[8];
	} sampletry6 =
	{
		{ 0332, 8, 0, 8, 0x3f,
			{ POSIX_VERSION, 0, 0 }
		},
		{
			{ 1, 3, -1 },
			{ 2, 1,  0 },
			{ 2, 2,  1000 },
			{ 4, 6, -1 },
			{ 8, 4,  0 },
			{ 8, 5,  1002 },
			{ 16, 3, -1 },
			{ 32, 2, -1 }
		}
	} ;

	struct {
		struct POSIX_SECURITY head;
		struct POSIX_ACE ace[4];
	} sampletry8 =
	{
		{ 0677, 4, 0, 4, 0x35,
			{ POSIX_VERSION, 0, 0 }
		},
		{
			{ 1, 6, -1 },
			{ 4, 7, -1 },
			{ 16, 7, -1 },
			{ 32, 7, -1 }
		}
	} ;

#endif /* POSIXACLS */


#if POSIXACLS
	for (cnt=1; cnt<=8; cnt++) {
		switch (cnt) {
		case 1 :
			pxsample = &sampletry1.head;
			txsample = "sampletry1-a";
			isdir = FALSE;
			descr = ntfs_build_descr_posix(context.mapping,&sampletry1.head,
				isdir, (const SID*)owner3, (const SID*)group3);
			break;
		case 2 :
			pxsample = &sampletry1.head;
			txsample = "sampletry1-b";
			isdir = FALSE;
			descr = ntfs_build_descr_posix(context.mapping,&sampletry1.head,
				isdir, (const SID*)adminsid, (const SID*)group3);
			break;
		case 3 :
			isdir = FALSE;
			pxsample = &sampletry3.head;
			txsample = "sampletry3";
			descr = ntfs_build_descr_posix(context.mapping,pxsample,
				isdir, (const SID*)group3, (const SID*)group3);
			break;
		case 4 :
			isdir = FALSE;
			pxsample = &sampletry4.head;
			txsample = "sampletry4";
			descr = ntfs_build_descr_posix(context.mapping,pxsample,
				isdir, (const SID*)owner3, (const SID*)group3);
			break;
		case 5 :
			isdir = FALSE;
			pxsample = &sampletry5.head;
			txsample = "sampletry5";
			descr = ntfs_build_descr_posix(context.mapping,pxsample,
				isdir, (const SID*)owner3, (const SID*)group3);
			break;
		case 6 :
			isdir = FALSE;
			pxsample = &sampletry6.head;
			txsample = "sampletry6-a";
			descr = ntfs_build_descr_posix(context.mapping,pxsample,
				isdir, (const SID*)owner3, (const SID*)group3);
			break;
		case 7 :
			isdir = FALSE;
			pxsample = &sampletry6.head;
			txsample = "sampletry6-b";
			descr = ntfs_build_descr_posix(context.mapping,pxsample,
				isdir, (const SID*)adminsid, (const SID*)adminsid);
			break;
		case 8 :
			pxsample = &sampletry8.head;
			txsample = "sampletry8";
			isdir = FALSE;
			descr = ntfs_build_descr_posix(context.mapping,&sampletry8.head,
				isdir, (const SID*)owner3, (const SID*)group3);
			break;
		default :
			pxsample = (struct POSIX_SECURITY*)NULL;
			txsample = (const char*)NULL;
		}
				/* check we get original back */
		if (descr)
			pxdesc = linux_permissions_posix(descr, isdir);
		else
			pxdesc = (struct POSIX_SECURITY*)NULL;
		if (!descr || !pxdesc || !same_posix(pxsample,pxdesc)) {
			printf("** Error in %s\n",txsample);
			showposix(pxsample);
			showall(descr,0);
			showposix(pxdesc);
			errors++;
		}
		free(descr);
		free(pxdesc);
	}

#endif /* POSIXACLS */


		/*
		 *		Check a few samples built by Windows,
		 *	which cannot be generated by Linux
		 */

	for (cnt=1; cnt<=10; cnt++) {
		switch(cnt) {
		case 1 :  /* hp/tmp */
			isdir = TRUE;
			descr = build_dummy_descr(isdir,
				(const SID*)owner1, (const SID*)group1,
				1,
				(int)TRUE, worldsid, (int)0x3, (u32)0x1f01ff);
			expectacc = expect = 0777;
			expectdef = 0;
			break;
		case 2 :  /* swsetup */
			isdir = TRUE;
			descr = build_dummy_descr(isdir, adminsid, (const SID*)group2,
				2,
				(int)TRUE, worldsid, (int)0x0, (u32)0x1f01ff,
				(int)TRUE, worldsid, (int)0xb, (u32)0x1f01ff);
			expectacc = expect = 0777;
			expectdef = 0777;
			break;
		case 3 :  /* Dr Watson */
			isdir = TRUE;
			descr = build_dummy_descr(isdir, (const SID*)owner3, (const SID*)group3,
				0);
			expectacc = expect = 0700;
			expectdef = 0;
			break;
		case 4 :
			isdir = FALSE;
			descr = build_dummy_descr(isdir,
				(const SID*)owner3, (const SID*)group3,
				4,
				(int)TRUE, (const SID*)owner3, 0, 
					le32_to_cpu(FILE_READ_DATA | OWNER_RIGHTS),
				(int)TRUE, (const SID*)group3, 0,
					le32_to_cpu(FILE_WRITE_DATA),
				(int)TRUE, (const SID*)group2, 0,
					le32_to_cpu(FILE_WRITE_DATA | FILE_READ_DATA),
				(int)TRUE, (const SID*)worldsid, 0,
					le32_to_cpu(FILE_EXECUTE));
			expect = 0731;
			expectacc = 07070731;
			expectdef = 0;
			break;
		case 5 :  /* Vista/JP */
			isdir = TRUE;
			descr = build_dummy_descr(isdir, systemsid, systemsid,
				6,
				(int)TRUE, owner1, (int)0x0, (u32)0x1f01ff,
				(int)TRUE, systemsid, (int)0x0, (u32)0x1f01ff,
				(int)TRUE, adminsid, (int)0x0, (u32)0x1f01ff,
				(int)TRUE, owner1, (int)0xb, (u32)0x10000000,
				(int)TRUE, systemsid, (int)0xb, (u32)0x10000000,
				(int)TRUE, adminsid, (int)0xb, (u32)0x10000000);
			expectacc = expect = 0700;
			expectdef = 0700;
			break;
		case 6 :  /* Vista/JP2 */
			isdir = TRUE;
			descr = build_dummy_descr(isdir, systemsid, systemsid,
				7,
				(int)TRUE, owner1,    (int)0x0, (u32)0x1f01ff,
				(int)TRUE, systemsid, (int)0x0, (u32)0x1f01ff,
				(int)TRUE, adminsid,  (int)0x0, (u32)0x1f01ff,
				(int)TRUE, owner1,    (int)0xb, (u32)0x1f01ff,
				(int)TRUE, systemsid, (int)0xb, (u32)0x1f01ff,
				(int)TRUE, adminsid,  (int)0xb, (u32)0x1f01ff,
				(int)TRUE, owner3,    (int)0x3, (u32)0x1200a9);
			expectacc = 0500070700;
			expectdef = 0700;
			expect = 0700;
			break;
		case 7 :  /* WinXP/JP */
			isdir = TRUE;
			descr = build_dummy_descr(isdir, adminsid, systemsid,
				6,
				(int)TRUE, owner1, (int)0x0, (u32)0x1f01ff,
				(int)TRUE, systemsid, (int)0x0, (u32)0x1f01ff,
				(int)TRUE, adminsid, (int)0x0, (u32)0x1f01ff,
				(int)TRUE, owner1, (int)0xb, (u32)0x10000000,
				(int)TRUE, systemsid, (int)0xb, (u32)0x10000000,
				(int)TRUE, adminsid, (int)0xb, (u32)0x10000000);
			expectacc = expect = 0700;
			expectdef = 0700;
			break;
		case 8 :  /* WinXP/JP2 */
			isdir = TRUE;
			descr = build_dummy_descr(isdir, adminsid, systemsid,
				6,
				(int)TRUE, owner1,    (int)0x0, (u32)0x1f01ff,
				(int)TRUE, systemsid, (int)0x0, (u32)0x1f01ff,
				(int)TRUE, adminsid,  (int)0x0, (u32)0x1f01ff,
				(int)TRUE, owner1,    (int)0xb, (u32)0x10000000,
				(int)TRUE, systemsid, (int)0xb, (u32)0x10000000,
				(int)TRUE, adminsid,  (int)0xb, (u32)0x10000000);
			expectacc = expect = 0700;
			expectdef = 0700;
			break;
		case 9 :  /* Win8/bin */
			isdir = TRUE;
			descr = build_dummy_descr(isdir,
				(const SID*)owner3, (const SID*)owner3,
				6,
				(int)TRUE, authsid,   (int)0x3,  (u32)0x1f01ff,
				(int)TRUE, adminsid,  (int)0x13, (u32)0x1f01ff,
				(int)TRUE, systemsid, (int)0x13, (u32)0x1f01ff,
				(int)TRUE, localsid,  (int)0x13, (u32)0x1200a9,
				(int)TRUE, authsid,   (int)0x10, (u32)0x1301bf,
				(int)TRUE, authsid,   (int)0x1b, (u32)0xe0010000);
			expectacc = expect = 0777;
			expectdef = 0777;
			break;
		case 10 :  /* Win8/bin/linem.exe */
			isdir = FALSE;
			descr = build_dummy_descr(isdir,
				(const SID*)owner3, (const SID*)owner3,
				4,
				(int)TRUE, authsid,   (int)0x10, (u32)0x1f01ff,
				(int)TRUE, adminsid,  (int)0x10, (u32)0x1f01ff,
				(int)TRUE, systemsid, (int)0x10, (u32)0x1ff,
				(int)TRUE, localsid,  (int)0x10, (u32)0x1200a9);
			expectacc = expect = 0777;
			expectdef = 0;
			break;
		default :
			expectacc = expectdef = 0;
			break;
		}
		if (descr) {
			perms = linux_permissions(descr, isdir);
			if (perms != expect) {
				printf("** Error in sample %d, perms 0%03o expected 0%03o\n",
					cnt,perms,expect);
				showall(descr,0);
				errors++;
			} else {
#if POSIXACLS
				pxdesc = linux_permissions_posix(descr, isdir);
				if (pxdesc) {
					accrights = merge_rights(pxdesc,FALSE);
					defrights = merge_rights(pxdesc,TRUE);
					if (!(pxdesc->tagsset & ~(POSIX_ACL_USER_OBJ | POSIX_ACL_GROUP_OBJ | POSIX_ACL_OTHER)))
						mixmode = expect;
					else
						mixmode = (expect & 07707) | ((accrights >> 9) & 070);
					if ((pxdesc->mode != mixmode)
					  || (accrights != expectacc)
					  || (defrights != expectdef)) {
						printf("** Error in sample %d : mode %03o expected 0%03o\n",
							cnt,pxdesc->mode,mixmode);
						printf("     Posix access rights 0%03lo expected 0%03lo\n",
							(long)accrights,(long)expectacc);
						printf("          default rights 0%03lo expected 0%03lo\n",
							(long)defrights,(long)expectdef);
						showall(descr,0);
						showposix(pxdesc);
exit(1);
					}
					free(pxdesc);
				}
#endif
			}
		free(descr);
		}
	}
}


/*
 *		Check whether any basic permission setting is interpreted
 *	back exactly as set
 */

void basictest(int kind, BOOL isdir, const SID *owner, const SID *group)
{
	char *attr;
	mode_t perm;
	mode_t gotback;
	u32 count;
	u32 acecount;
	u32 globhash;
	const SECURITY_DESCRIPTOR_RELATIVE *phead;
	const ACL *pacl;
	enum { ERRNO,
		ERRMA, ERRPA, /* error converting mode or Posix ACL to NTFS */
		ERRAM, ERRAP, /* error converting NTFS to mode or Posix ACL */
	} err;
	u32 expectcnt[] = {
		27800, 31896,
		24064, 28160,
		24064, 28160,
		24064, 28160,
		25416, 29512
	} ;
	u32 expecthash[] = {
		0x8f80865b, 0x7bc7960,
		0x8fd9ecfe, 0xddd4db0,
		0xa8b07400, 0xa189c20,
		0xc5689a00, 0xb6c09000,
		0x94bfb419, 0xa4311791
	} ;
#if POSIXACLS
	struct POSIX_SECURITY *pxdesc;
	char *pxattr;
	u32 pxcount;
	u32 pxacecount;
	u32 pxglobhash;
#endif

	count = 0;
	acecount = 0;
	globhash = 0;
#if POSIXACLS
	pxcount = 0;
	pxacecount = 0;
	pxglobhash = 0;
#endif
	for (perm=0; (perm<=07777) && (errors < 10); perm++) {
		err = ERRNO;
		/* file owned by plain user and group */
		attr = ntfs_build_descr(perm,isdir,owner,(const SID*)group);
		if (attr && ntfs_valid_descr(attr, ntfs_attr_size(attr))) {
			phead = (const SECURITY_DESCRIPTOR_RELATIVE*)attr;
			pacl = (const ACL*)&attr[le32_to_cpu(phead->dacl)];
			acecount += le16_to_cpu(pacl->ace_count);
			globhash += hash((const le32*)attr,ntfs_attr_size(attr));
			count++;
#if POSIXACLS
			/*
			 * Build a NTFS ACL from a mode, and
			 * decode to a Posix ACL, expecting to
			 * get the original mode back.
			 */
			pxdesc = linux_permissions_posix(attr, isdir);
			if (!pxdesc || (pxdesc->mode != perm)) {
				err = ERRAP;
				if (pxdesc)
					gotback = pxdesc->mode;
				else
					gotback = 0;
			} else {
			/*
			 * Build a NTFS ACL from the Posix ACL, expecting to
			 * get exactly the same NTFS ACL, then decode to a
			 * mode, expecting to get the original mode back.
			 */
				pxattr = ntfs_build_descr_posix(context.mapping,
						pxdesc,isdir,owner,
						(const SID*)group);
				if (pxattr && !memcmp(pxattr,attr,
						 ntfs_attr_size(attr))) {
					phead = (const SECURITY_DESCRIPTOR_RELATIVE*)attr;
					pacl = (const ACL*)&attr[le32_to_cpu(phead->dacl)];
					pxacecount += le16_to_cpu(pacl->ace_count);
					pxglobhash += hash((const le32*)attr,ntfs_attr_size(attr));
					pxcount++;
					gotback = linux_permissions(pxattr, isdir);
					if (gotback != perm)
						err = ERRAM;
					else
						free(pxattr);
				} else
					err = ERRPA;
				free(attr);
			}
			free(pxdesc);
#else
			gotback = linux_permissions(attr, isdir);
			if (gotback != perm)
				err = ERRAM;
			else
				free(attr);
#endif /* POSIXACLS */
		} else
			err = ERRMA;

		switch (err) {
		case ERRMA :
			printf("** no or wrong permission settings "
				"for kind %d perm %03o\n",kind,perm);
			if (attr && opt_v)
				hexdump(attr,ntfs_attr_size(attr),8);
			if (attr && (opt_v >= 2)) {
				showheader(attr,4);
				showusid(attr,4);
				showgsid(attr,4);
				showdacl(attr,isdir,4);
				showsacl(attr,isdir,4);
			}
			errors++;
			break;
		case ERRPA :
			printf("** no or wrong permission settings from PX "
				"for kind %d perm %03o\n",kind,perm);
			errors++;
			break;
#if POSIXACLS
		case ERRAM :
			printf("** wrong permission settings, "
				"kind %d perm 0%03o, gotback %03o\n",
				kind, perm, gotback);
			if (opt_v)
				hexdump(pxattr,ntfs_attr_size(pxattr),8);
			if (opt_v >= 2) {
				showheader(pxattr,4);
				showusid(pxattr,4);
				showgsid(pxattr,4);
				showdacl(pxattr,isdir,4);
				showsacl(pxattr,isdir,4);
			}
			errors++;
			break;
		case ERRAP :
			/* continued */
#else
		case ERRAM :
		case ERRAP :
#endif /* POSIXACLS */
			printf("** wrong permission settings, "
				"kind %d perm 0%03o, gotback %03o\n",
				kind, perm, gotback);
			if (opt_v)
				hexdump(attr,ntfs_attr_size(attr),8);
			if (opt_v >= 2) {
				showheader(attr,4);
				showusid(attr,4);
				showgsid(attr,4);
				showdacl(attr,isdir,4);
				showsacl(attr,isdir,4);
			}
			errors++;
			free(attr);
			break;
		default :
			break;
		}
	}
	printf("%lu ACLs built from mode, %lu ACE built, mean count %lu.%02lu\n",
		(unsigned long)count,(unsigned long)acecount,
		(unsigned long)acecount/count,acecount*100L/count%100L);
	if (acecount != expectcnt[kind]) {
		printf("** Error : expected ACE count %lu\n",
			(unsigned long)expectcnt[kind]);
		errors++;
	}
	if (globhash != expecthash[kind]) {
		printf("** Error : wrong global hash 0x%lx instead of 0x%lx\n",
			(unsigned long)globhash, (unsigned long)expecthash[kind]);
		errors++;
	}
#if POSIXACLS
	printf("%lu ACLs built from Posix ACLs, %lu ACE built, mean count %lu.%02lu\n",
		(unsigned long)pxcount,(unsigned long)pxacecount,
		(unsigned long)pxacecount/pxcount,pxacecount*100L/pxcount%100L);
	if (pxacecount != expectcnt[kind]) {
		printf("** Error : expected ACE count %lu\n",
			(unsigned long)expectcnt[kind]);
		errors++;
	}
	if (pxglobhash != expecthash[kind]) {
		printf("** Error : wrong global hash 0x%lx instead of 0x%lx\n",
			(unsigned long)pxglobhash, (unsigned long)expecthash[kind]);
		errors++;
	}
#endif /* POSIXACLS */
}

#if POSIXACLS

/*
 *		Check whether Posix ACL settings are interpreted
 *	back exactly as set
 */

void posixtest(int kind, BOOL isdir,
			const SID *owner, const SID *group)
{
	struct POSIX_SECURITY *pxdesc;
	struct {
		struct POSIX_SECURITY pxdesc;
		struct POSIX_ACE aces[10];
	} desc;
	int ownobj;
	int grpobj;
	int usr;
	int grp;
	int wrld;
	int mask;
	int mindes, maxdes;
	int minmsk, maxmsk;
	char *pxattr;
	u32 count;
	u32 acecount;
	u32 globhash;
	const SECURITY_DESCRIPTOR_RELATIVE *phead;
	const ACL *pacl;
	struct POSIX_SECURITY *gotback;
	enum { ERRNO,
		ERRMA, ERRPA, /* error converting mode or Posix ACL to NTFS */
		ERRAM, ERRAP, /* error converting NTFS to mode or Posix ACL */
	} err;
	u32 expectcnt[] = {
#ifdef STSC
		32400, 34992,
		25920, 28512,
		25920, 28512,
		25920, 28512,
		26460, 29052,
		0, 0,
		0, 0,
		0, 0,
		24516, 27108,
		20736, 23328,
		20736, 23328,
		20736, 23328,
		21060, 23652,
#else
		252720, 273456,
		199584, 220320,
		199584, 220320,
		199584, 220320,
		203904, 224640,
		0, 0,
		0, 0,
		0, 0,
		196452, 217188,
		165888, 186624,
		165888, 186624,
		165888, 186624,
		168480, 189216,
#endif
		0, 0,
		0, 0,
		0, 0,
		16368, 18672,
		0, 0,
		13824, 0,
		0, 0,
		14640, 0
	} ;
	u32 expecthash[] = {
#ifdef STSC
		0xf9f82115, 0x40666647,
		0xde826d30, 0xa181b660,
		0x952d4500, 0x8ac49450,
		0xf80acee0, 0xbd9ec6c0,
		0xfe09b868, 0xde24e84d,
		0, 0,
		0, 0,
		0, 0,
		0x2381438d, 0x3ab42dc6,
		0x7cccf6f8, 0x108ad430,
		0x5e448840, 0x83ab6c40,
		0x9b037100, 0x8f7c3b40,
		0x04a359dc, 0xa4619609,
#else
		0x1808a6cd, 0xd82f7c60,
		0x5ad29e85, 0x518c7620,
		0x188ce270, 0x7e44e590,
		0x48a64800, 0x5bdf0030,
		0x1c64aec6, 0x8b0168fa,
		0, 0,
		0, 0,
		0, 0,
		0x169fb80e, 0x382d9a59,
		0xf9c28164, 0x1855d352,
		0xf9685700, 0x44d16700,
		0x587ebe90, 0xf7c51480,
		0x2cb1b518, 0x52408df6,
#endif
		0, 0,
		0, 0,
		0, 0,
		0x905f2e38, 0xd40c22f0,
		0, 0,
		0xdd76da00, 0,
		0, 0,
		0x718e34a0, 0
	};

	count = 0;
	acecount = 0;
	globhash = 0;
				/* fill headers */
	pxdesc = &desc.pxdesc;
	pxdesc->mode = 0;
	pxdesc->defcnt = 0;
	if (kind & 32) {
		pxdesc->acccnt = 4;
		pxdesc->firstdef = 4;
		pxdesc->tagsset = 0x35;
	} else {
		pxdesc->acccnt = 6;;
		pxdesc->firstdef = 6;
		pxdesc->tagsset = 0x3f;
	}
	pxdesc->acl.version = POSIX_VERSION;
	pxdesc->acl.flags = 0;
	pxdesc->acl.filler = 0;
				/* prefill aces */
	pxdesc->acl.ace[0].tag = POSIX_ACL_USER_OBJ;
	pxdesc->acl.ace[0].id = -1;
	if (kind & 32) {
		pxdesc->acl.ace[1].tag = POSIX_ACL_GROUP_OBJ;
		pxdesc->acl.ace[1].id = -1;
		pxdesc->acl.ace[2].tag = POSIX_ACL_MASK;
		pxdesc->acl.ace[2].id = -1;
		pxdesc->acl.ace[3].tag = POSIX_ACL_OTHER;
		pxdesc->acl.ace[3].id = -1;
	} else {
		pxdesc->acl.ace[1].tag = POSIX_ACL_USER;
		pxdesc->acl.ace[1].id = (kind & 16 ? 0 : 1000);
		pxdesc->acl.ace[2].tag = POSIX_ACL_GROUP_OBJ;
		pxdesc->acl.ace[2].id = -1;
		pxdesc->acl.ace[3].tag = POSIX_ACL_GROUP;
		pxdesc->acl.ace[3].id = (kind & 16 ? 0 : 1002);
		pxdesc->acl.ace[4].tag = POSIX_ACL_MASK;
		pxdesc->acl.ace[4].id = -1;
		pxdesc->acl.ace[5].tag = POSIX_ACL_OTHER;
		pxdesc->acl.ace[5].id = -1;
	}

	mindes = 3;
	maxdes = (kind & 32 ? mindes : 6);
#ifdef STSC
	minmsk = (kind & 32 ? 0 : 3);
	maxmsk = (kind & 32 ? 7 : 3);
#else
	minmsk = 0;
	maxmsk = 7;
#endif
	for (mask=minmsk; mask<=maxmsk; mask++)
	for (ownobj=1; ownobj<7; ownobj++)
	for (grpobj=1; grpobj<7; grpobj++)
	for (wrld=0; wrld<8; wrld++)
	for (usr=mindes; usr<=maxdes; usr++)
	if (usr != 4)
	for (grp=mindes; grp<=maxdes; grp++)
	if (grp != 4) {
		pxdesc->mode = (ownobj << 6) | (mask << 3) | wrld;

		pxdesc->acl.ace[0].perms = ownobj;
		if (kind & 32) {
			pxdesc->acl.ace[1].perms = grpobj;
			pxdesc->acl.ace[2].perms = mask;
			pxdesc->acl.ace[3].perms = wrld;
		} else {
			pxdesc->acl.ace[1].perms = usr;
			pxdesc->acl.ace[2].perms = grpobj;
			pxdesc->acl.ace[3].perms = grp;
			pxdesc->acl.ace[4].perms = mask;
			pxdesc->acl.ace[5].perms = wrld;
		}

		err = ERRNO;
		gotback = (struct POSIX_SECURITY*)NULL;
		pxattr = ntfs_build_descr_posix(context.mapping,
				pxdesc,isdir,owner,group);
		if (pxattr && ntfs_valid_descr(pxattr, ntfs_attr_size(pxattr))) {
			phead = (const SECURITY_DESCRIPTOR_RELATIVE*)pxattr;
			pacl = (const ACL*)&pxattr[le32_to_cpu(phead->dacl)];
			acecount += le16_to_cpu(pacl->ace_count);
			globhash += hash((const le32*)pxattr,ntfs_attr_size(pxattr));
			count++;
			gotback = linux_permissions_posix(pxattr, isdir);
			if (gotback) {
				if (ntfs_valid_posix(gotback)) {
					if (!same_posix(pxdesc,gotback)) {
						printf("Non matching got back Posix ACL\n");
						printf("input ACL\n");
						showposix(pxdesc);
						printf("NTFS owner\n");
						showusid(pxattr,4);
						printf("NTFS group\n");
						showgsid(pxattr,4);
						printf("NTFS DACL\n");
						showdacl(pxattr,isdir,4);
						printf("gotback ACL\n");
						showposix(gotback);
						errors++;
exit(1);
					}
				} else {
					printf("Got back an invalid Posix ACL\n");
					errors++;
				}
				free(gotback);
			} else {
				printf("Could not get Posix ACL back\n");
				errors++;
			}

		} else {
			printf("NTFS ACL incorrect or not build\n");
			printf("input ACL\n");
			showposix(pxdesc);
			printf("NTFS DACL\n");
			if (pxattr)
				showdacl(pxattr,isdir,4);
			else
				printf("   (none)\n");
			if (gotback) {
				printf("gotback ACL\n");
				showposix(gotback);
			} else
				printf("no gotback ACL\n");
			errors++;
		}
		if (pxattr)
			free(pxattr);
	}
	printf("%lu ACLs built from Posix ACLs, %lu ACE built, mean count %lu.%02lu\n",
		(unsigned long)count,(unsigned long)acecount,
		(unsigned long)acecount/count,acecount*100L/count%100L);
	if (acecount != expectcnt[kind]) {
		printf("** Error ! expected ACE count %lu\n",
			(unsigned long)expectcnt[kind]);
		errors++;
	}
	if (globhash != expecthash[kind]) {
		printf("** Error : wrong global hash 0x%lx instead of 0x%lx\n",
			(unsigned long)globhash, (unsigned long)expecthash[kind]);
		errors++;
	}
}

#endif /* POSIXACLS */

void selftests(void)
{
	le32 owner_sid[] = /* S-1-5-21-3141592653-589793238-462843383-1016 */
		{
		cpu_to_le32(0x501), cpu_to_le32(0x05000000), cpu_to_le32(21),
		cpu_to_le32(DEFSECAUTH1), cpu_to_le32(DEFSECAUTH2),
		cpu_to_le32(DEFSECAUTH3), cpu_to_le32(1016)
		} ;
	le32 group_sid[] = /* S-1-5-21-3141592653-589793238-462843383-513 */
		{
		cpu_to_le32(0x501), cpu_to_le32(0x05000000), cpu_to_le32(21),
		cpu_to_le32(DEFSECAUTH1), cpu_to_le32(DEFSECAUTH2),
		cpu_to_le32(DEFSECAUTH3), cpu_to_le32(513)
		} ;
#if POSIXACLS
#ifdef STSC
	unsigned char kindlist[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
			   16, 17, 18, 20, 22, 24,
			   32, 33, 36, 40 } ;
#else
	unsigned char kindlist[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
			   16, 17, 18, 20, 22, 24, 19, 21, 23, 25,
			   32, 33, 36, 40 } ;
#endif
	unsigned int k;
#endif /* POSIXACLS */
	int kind;
	const SID *owner;
	const SID *group;
	BOOL isdir;

#if POSIXACLS
	local_build_mapping(context.mapping, (const char*)NULL);
#endif
			/* first check samples */
	mappingtype = MAPDUMMY;
	check_samples();
if (errors) exit(1);
		/*
		 * kind is oring of :
		 *   1 : directory
		 *   2 : owner is root
		 *   4 : group is root
		 *   8 : group is owner
		 *  16 : root is designated user/group
		 *  32 : mask present with no designated user/group
		 */
	for (kind=0; (kind<10) && (errors<10); kind++) {
		isdir = kind & 1;
		if (kind & 8)
			owner = (const SID*)group_sid;
		else
			owner = (kind & 2 ? adminsid : (const SID*)owner_sid);
		group = (kind & 4 ? adminsid : (const SID*)group_sid);
		basictest(kind, isdir, owner, group);
	}
#if POSIXACLS
	for (k=0; (k<sizeof(kindlist)) && (errors<10); k++) {
		kind = kindlist[k];
		isdir = kind & 1;
		if (kind & 8)
			owner = (const SID*)group_sid;
		else
			owner = (kind & 2 ? adminsid : (const SID*)owner_sid);
		group = (kind & 4 ? adminsid : (const SID*)group_sid);
		posixtest(kind, isdir, owner, group);
	}
	ntfs_free_mapping(context.mapping);
#endif
	if (errors >= 10)
		printf("** too many errors, test aborted\n");
}
#endif /* SELFTESTS & !USESTUBS */

#ifdef WIN32

/*
 *		   Get the security descriptor of a file (Windows version)
 */

unsigned int getfull(char *attr, const char *fullname)
{
	static char part[MAXATTRSZ];
	BIGSID ownsid;
	int xowner;
	int ownersz;
	u16 ownerfl;
	ULONG attrsz;
	ULONG partsz;
	BOOL overflow;
	HANDLE htoken;
	TOKEN_PRIVILEGES tkp;
	BOOL saclsuccess;

	attrsz = 0;
	partsz = 0;
	overflow = FALSE;
	if (GetFileSecurityW((LPCWSTR)fullname,OWNER_SECURITY_INFORMATION,
				(char*)part,MAXATTRSZ,&partsz)) {
		xowner = get4l(part,4);
		if (xowner) {
			ownerfl = get2l(part,2);
			ownersz = ntfs_sid_size((SID*)&part[xowner]);
			if (ownersz <= (int)sizeof(BIGSID))
				memcpy(ownsid,&part[xowner],ownersz);
			else
				overflow = TRUE;
		} else {
			ownerfl = 0;
			ownersz = 0;
		}
			/*
			 *  SACL : just feed in or clean
			 *  This requires the SE_SECURITY_NAME privilege
			 */
		saclsuccess = FALSE;
		if (OpenProcessToken(GetCurrentProcess(), 
				TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &htoken)) {
			if (LookupPrivilegeValue(NULL, SE_SECURITY_NAME,
					&tkp.Privileges[0].Luid)) {
				tkp.PrivilegeCount = 1;
				tkp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
				if (AdjustTokenPrivileges(htoken, FALSE, &tkp, 0, NULL, 0)) {
					if (GetFileSecurityW((LPCWSTR)fullname,
							SACL_SECURITY_INFORMATION,
							(char*)attr,MAXATTRSZ,&attrsz)) {
						saclsuccess = TRUE;
					}
					tkp.Privileges[0].Attributes = 0;
					AdjustTokenPrivileges(htoken, FALSE, &tkp, 0, NULL, 0);
				}
			}
		}		
		if (!saclsuccess) {
			attrsz = 20;
			set4l(attr,0);
			attr[0] = SECURITY_DESCRIPTOR_REVISION;
			set4l(&attr[12],0);
			if (opt_v >= 2)
				printf("   No SACL\n");
		}
			/*
			 *  append DACL and merge its flags
			 */
		partsz = 0;
		set4l(&attr[16],0);
		if (GetFileSecurityW((LPCWSTR)fullname,DACL_SECURITY_INFORMATION,
			    (char*)part,MAXATTRSZ,&partsz)) {
			if ((attrsz + partsz - 20) <= MAXATTRSZ) {
				memcpy(&attr[attrsz],&part[20],partsz-20);
				set4l(&attr[16],(partsz > 20 ? attrsz : 0));
				set2l(&attr[2],get2l(attr,2) | (get2l(part,2)
					& const_le16_to_cpu(SE_DACL_PROTECTED
						   | SE_DACL_AUTO_INHERITED
						   | SE_DACL_PRESENT)));
				attrsz += partsz - 20;
			} else
				overflow = TRUE;
		} else
			if (partsz > MAXATTRSZ)
				overflow = TRUE;
			else {
				if (opt_b)
					printf("#   No discretionary access control list\n");
				else
					printf("   No discretionary access control list\n");
				warnings++;
			}

			/*
			 *  append owner and merge its flag
			 */
		if (xowner && !overflow) {
			memcpy(&attr[attrsz],ownsid,ownersz);
			set4l(&attr[4],attrsz);
			set2l(&attr[2],get2l(attr,2)
			   | (ownerfl & const_le16_to_cpu(SE_OWNER_DEFAULTED)));
			attrsz += ownersz;
		} else
			set4l(&attr[4],0);
			/*
			 * append group
			 */
		partsz = 0;
		set4l(&attr[8],0);
		if (GetFileSecurityW((LPCWSTR)fullname,GROUP_SECURITY_INFORMATION,
			    (char*)part,MAXATTRSZ,&partsz)) {
			if ((attrsz + partsz - 20) <= MAXATTRSZ) {
				memcpy(&attr[attrsz],&part[20],partsz-20);
				set4l(&attr[8],(partsz > 20 ? attrsz : 0));
				set2l(&attr[2],get2l(attr,2) | (get2l(part,2)
					& const_le16_to_cpu(SE_GROUP_DEFAULTED)));
				attrsz += partsz - 20;
			} else
				overflow = TRUE;
		} else
			if (partsz > MAXATTRSZ)
				overflow = TRUE;
			else {
				printf("**   No group SID\n");
				warnings++;
			}
		set2l(&attr[2],get2l(attr,2)
					| const_le16_to_cpu(SE_SELF_RELATIVE));
		if (overflow) {
			printf("** Descriptor was too long (> %d)\n",MAXATTRSZ);
			warnings++;
			attrsz = 0;
		} else
			if (!ntfs_valid_descr((char*)attr,attrsz)) {
				printf("** Descriptor for ");
				printname(stdout,fullname);
				printf(" is not valid\n");
				errors++;
				attrsz = 0;
			}

	} else {
		printf("** Could not get owner of ");
		printname(stdout,fullname);
		printf(", partsz %d\n",partsz);
		printerror(stdout);
		warnings++;
		attrsz = 0;		
	}
	return (attrsz);
}

/*
 *		Update a security descriptor (Windows version)
 */

BOOL updatefull(const char *name, DWORD flags, char *attr)
{
	BOOL bad;

	bad = !SetFileSecurityW((LPCWSTR)name, flags, attr);
	if (bad
	  && (flags & OWNER_SECURITY_INFORMATION)
	  && (GetLastError() == 1307)) {
		printf("** Could not set owner of ");
		printname(stdout,name);
		printf(", retrying with no owner setting\n");
		warnings++;
		bad = !SetFileSecurityW((LPCWSTR)name,
			flags & ~OWNER_SECURITY_INFORMATION, (char*)attr);
	}
	if (bad) {
		printf("** Could not change attributes of ");
		printname(stdout,name);
		printf("\n");
		printerror(stdout);
		errors++;
	}
	return (!bad);
}

#else

/*
 *		   Get the security descriptor of a file (Linux version)
 */

unsigned int getfull(char *attr, const char *fullname)
{
	static char part[MAXATTRSZ];
	BIGSID ownsid;
	int xowner;
	int ownersz;
	u16 ownerfl;
	u32 attrsz;
	u32 partsz;
	BOOL overflow;

	attrsz = 0;
	partsz = 0;
	overflow = FALSE;
	if (ntfs_get_file_security(ntfs_context,fullname,
				OWNER_SECURITY_INFORMATION,
				(char*)part,MAXATTRSZ,&partsz)) {
		xowner = get4l(part,4);
		if (xowner) {
			ownerfl = get2l(part,2);
			ownersz = ntfs_sid_size((SID*)&part[xowner]);
			if (ownersz <= (int)sizeof(BIGSID))
				memcpy(ownsid,&part[xowner],ownersz);
			else
				overflow = TRUE;
		} else {
			ownerfl = 0;
			ownersz = 0;
		}
			/*
			 *  SACL : just feed in or clean
			 */
		if (!ntfs_get_file_security(ntfs_context,fullname,
				SACL_SECURITY_INFORMATION,
				(char*)attr,MAXATTRSZ,&attrsz)) {
			attrsz = 20;
			set4l(attr,0);
			attr[0] = SECURITY_DESCRIPTOR_REVISION;
			set4l(&attr[12],0);
			if (opt_v >= 2)
				printf("   No SACL\n");
		}
			/*
			 *  append DACL and merge its flags
			 */
		partsz = 0;
		set4l(&attr[16],0);
		if (ntfs_get_file_security(ntfs_context,fullname,
		    DACL_SECURITY_INFORMATION,
		    (char*)part,MAXATTRSZ,&partsz)) {
			if ((attrsz + partsz - 20) <= MAXATTRSZ) {
				memcpy(&attr[attrsz],&part[20],partsz-20);
				set4l(&attr[16],(partsz > 20 ? attrsz : 0));
				set2l(&attr[2],get2l(attr,2) | (get2l(part,2)
					& const_le16_to_cpu(SE_DACL_PROTECTED
						   | SE_DACL_AUTO_INHERITED
						   | SE_DACL_PRESENT)));
				attrsz += partsz - 20;
			} else
				overflow = TRUE;
		} else
			if (partsz > MAXATTRSZ)
				overflow = TRUE;
			else {
				if (opt_b)
					printf("#   No discretionary access control list\n");
				else
					printf("   No discretionary access control list\n");
				warnings++;
			}

			/*
			 *  append owner and merge its flag
			 */
		if (xowner && !overflow) {
			memcpy(&attr[attrsz],ownsid,ownersz);
			set4l(&attr[4],attrsz);
			set2l(&attr[2],get2l(attr,2)
			   | (ownerfl & const_le16_to_cpu(SE_OWNER_DEFAULTED)));
			attrsz += ownersz;
		} else
			set4l(&attr[4],0);
			/*
			 * append group
			 */
		partsz = 0;
		set4l(&attr[8],0);
		if (ntfs_get_file_security(ntfs_context,fullname,
		    GROUP_SECURITY_INFORMATION,
		    (char*)part,MAXATTRSZ,&partsz)) {
			if ((attrsz + partsz - 20) <= MAXATTRSZ) {
				memcpy(&attr[attrsz],&part[20],partsz-20);
				set4l(&attr[8],(partsz > 20 ? attrsz : 0));
				set2l(&attr[2],get2l(attr,2) | (get2l(part,2)
					& const_le16_to_cpu(SE_GROUP_DEFAULTED)));
				attrsz += partsz - 20;
			} else
				overflow = TRUE;
		} else
			if (partsz > MAXATTRSZ)
				overflow = TRUE;
			else {
				printf("**   No group SID\n");
				warnings++;
			}
		if (overflow) {
			printf("** Descriptor was too long (> %d)\n",MAXATTRSZ);
			warnings++;
			attrsz = 0;
		} else
			if (!ntfs_valid_descr((char*)attr,attrsz)) {
				printf("** Descriptor for %s is not valid\n",fullname);
				errors++;
				attrsz = 0;
			}

	} else {
		printf("** Could not get owner of %s\n",fullname);
		warnings++;
		attrsz = 0;		
	}
	return (attrsz);
}

/*
 *		Update a security descriptor (Linux version)
 */

BOOL updatefull(const char *name, DWORD flags, char *attr)
{
	BOOL ok;

	ok = !ntfs_set_file_security(ntfs_context, name, flags, attr);
	if (ok) {
		printf("** Could not change attributes of %s\n",name);
		printerror(stdout);
		errors++;
	}
	return (ok);
}


#endif

#if POSIXACLS

/*
 *		   Set all the parameters associated to a file
 */

BOOL setfull_posix(const char *fullname, const struct POSIX_SECURITY *pxdesc,
			BOOL isdir)
{
	static char attr[MAXATTRSZ];
	struct POSIX_SECURITY *oldpxdesc;
	struct POSIX_SECURITY *newpxdesc;
	const SECURITY_DESCRIPTOR_RELATIVE *phead;
	char *newattr;
	int err;
	unsigned int attrsz;
	int newattrsz;
	const SID *usid;
	const SID *gsid;
#if OWNERFROMACL
	const SID *osid;
#endif

	printf("%s ",(isdir ? "Directory" : "File"));
	printname(stdout,fullname);
	if (pxdesc->acccnt)
		printf("\n");
	else
		printf(" mode 0%03o\n",pxdesc->mode);

	err = FALSE;
	attrsz = getfull(attr, fullname);
	if (attrsz) {
		oldpxdesc = linux_permissions_posix(attr, isdir);
		if (opt_v >= 2) {
			printf("Posix equivalent of old ACL :\n");
			showposix(oldpxdesc);
		}
		if (oldpxdesc) {
			if (!pxdesc->defcnt
			   && !(pxdesc->tagsset &
			     (POSIX_ACL_USER | POSIX_ACL_GROUP | POSIX_ACL_MASK))) {
				if (!ntfs_merge_mode_posix(oldpxdesc,pxdesc->mode))
					newpxdesc = oldpxdesc;
				else {
					newpxdesc = (struct POSIX_SECURITY*)NULL;
					free(oldpxdesc);
				}
			} else {
				newpxdesc = ntfs_merge_descr_posix(pxdesc, oldpxdesc);
				free(oldpxdesc);
			}
			if (opt_v) {
				printf("New Posix ACL :\n");
				showposix(newpxdesc);
			}
		} else
			newpxdesc = (struct POSIX_SECURITY*)NULL;
		if (newpxdesc) {
			phead = (const SECURITY_DESCRIPTOR_RELATIVE*)attr;
			gsid = (const SID*)&attr[le32_to_cpu(phead->group)];
#if OWNERFROMACL
			osid = (const SID*)&attr[le32_to_cpu(phead->owner)];
			usid = ntfs_acl_owner((const char*)attr);
			if (!ntfs_same_sid(usid,osid))
				printf("== Windows owner might change\n"); 
#else
			usid = (const SID*)&attr[le32_to_cpu(phead->owner)];
#endif
			newattr = ntfs_build_descr_posix(context.mapping,
				newpxdesc,isdir,usid,gsid);
			free(newpxdesc);
		} else
			newattr = (char*)NULL;
		if (newattr) {
			newattrsz = ntfs_attr_size(newattr);
			if (opt_v) {
				printf("New NTFS security descriptor\n");
				hexdump(newattr,newattrsz,4);
			}
			if (opt_v >= 2) {
				printf("Expected hash : 0x%08lx\n",
					(unsigned long)hash((le32*)newattr,ntfs_attr_size(newattr)));
				showheader(newattr,0);
				showusid(newattr,0);
				showgsid(newattr,0);
				showdacl(newattr,isdir,0);
				showsacl(newattr,isdir,0);
			}

#ifdef WIN32
			/*
			 * avoid getting a set owner error on Windows
			 * owner should not be changed anyway
			 */
			if (!updatefull(fullname,
				DACL_SECURITY_INFORMATION
				| GROUP_SECURITY_INFORMATION
				| OWNER_SECURITY_INFORMATION,
					newattr))
#else
			if (!updatefull(fullname,
				DACL_SECURITY_INFORMATION
				| GROUP_SECURITY_INFORMATION
				| OWNER_SECURITY_INFORMATION,
					newattr))
#endif
				err = TRUE;
/*
{
struct POSIX_SECURITY *interp;
printf("Reinterpreted new Posix :\n");
interp = linux_permissions_posix(newattr,isdir);
showposix(interp);
free(interp);
}
*/
			free(newattr);
		} else
			err = TRUE;
	} else
		err = TRUE;
	return (!err);
}

#endif

BOOL setfull(const char *fullname, int mode, BOOL isdir)
{
	static char attr[MAXATTRSZ];
	const SECURITY_DESCRIPTOR_RELATIVE *phead;
	char *newattr;
	int err;
	unsigned int attrsz;
	int newattrsz;
	const SID *usid;
	const SID *gsid;
#if OWNERFROMACL
	const SID *osid;
#endif

	printf("%s ",(isdir ? "Directory" : "File"));
	printname(stdout,fullname);
	printf(" mode 0%03o\n",mode);
	attrsz = getfull(attr, fullname);
	err = FALSE;
	if (attrsz) {
		phead = (const SECURITY_DESCRIPTOR_RELATIVE*)attr;
		gsid = (const SID*)&attr[le32_to_cpu(phead->group)];
#if OWNERFROMACL
		osid = (const SID*)&attr[le32_to_cpu(phead->owner)];
		usid = ntfs_acl_owner((const char*)attr);
		if (!ntfs_same_sid(usid,osid))
			printf("== Windows owner might change\n"); 
#else
		usid = (const SID*)&attr[le32_to_cpu(phead->owner)];
#endif
		newattr = ntfs_build_descr(mode,isdir,usid,gsid);
		if (newattr) {
			newattrsz = ntfs_attr_size(newattr);
			if (opt_v) {
				printf("Security descriptor\n");
				hexdump(newattr,newattrsz,4);
			}
			if (opt_v >= 2) {
				printf("Expected hash : 0x%08lx\n",
					(unsigned long)hash((le32*)newattr,ntfs_attr_size(newattr)));
				showheader(newattr,0);
				showusid(newattr,0);
				showgsid(newattr,0);
				showdacl(newattr,isdir,0);
				showsacl(newattr,isdir,0);
			}

#ifdef WIN32
			/*
			 * avoid getting a set owner error on Windows
			 * owner should not be changed anyway
			 */
			if (!updatefull(fullname,
				DACL_SECURITY_INFORMATION
				| GROUP_SECURITY_INFORMATION
				| OWNER_SECURITY_INFORMATION,
					newattr))
#else
			if (!updatefull(fullname,
				DACL_SECURITY_INFORMATION
				| GROUP_SECURITY_INFORMATION
				| OWNER_SECURITY_INFORMATION,
					newattr))
#endif
				err = TRUE;
			free(newattr);
		}
		
	} else
		err = TRUE;
	return (err);
}

BOOL proposal(const char *name, const char *attr)
{
	char fullname[MAXFILENAME];
	int uoff, goff;
	int i;
	u64 uauth, gauth;
	int ucnt, gcnt;
	int uid, gid;
	BOOL err;
#ifdef WIN32
	char driveletter;
#else
	struct stat st;
	char *p,*q;
#endif

	err = FALSE;
#ifdef WIN32
	uid = gid = 0;
#else
	uid = getuid();
	gid = getgid();
#endif
	uoff = get4l(attr,4);
	uauth = get6h(attr,uoff+2);
	ucnt = attr[uoff+1] & 255;
	goff = get4l(attr,8);
	gauth = get6h(attr,goff+2);
	gcnt = attr[goff+1] & 255;

	if ((ucnt == 5) && (gcnt == 5)
	    && (uauth == 5) && (gauth == 5)
	    && (get4l(attr,uoff+8) == 21) && (get4l(attr,goff+8) == 21)) {
		printf("# User mapping proposal :\n");
		printf("# -------------------- cut here -------------------\n");
		if (uid)
			printf("%d::",uid);
		else
			printf("user::");
		printf("S-%d-%llu",attr[uoff] & 255,uauth);
		for (i=0; i<ucnt; i++)
			printf("-%lu",get4l(attr,uoff+8+4*i));
		printf("\n");
		if (gid)
			printf(":%d:",gid);
		else
			printf(":group:");
		printf("S-%d-%llu",attr[goff] & 255,gauth);
		for (i=0; i<gcnt; i++)
			printf("-%lu",get4l(attr,goff+8+4*i));
		printf("\n");
			/* generic rule, based on group */
		printf("::S-%d-%llu",attr[goff] & 255,gauth);
		for (i=0; i<gcnt-1; i++)
			printf("-%lu",get4l(attr,goff+8+4*i));
		printf("-10000\n");
		printf("# -------------------- cut here -------------------\n");
		if (!uid || !gid) {
			printf("# Please replace \"user\" and \"group\" above by the uid\n");
			printf("# and gid of the Linux owner and group of ");
			printname(stdout,name);
			printf(", then\n");
			printf("# insert the modified lines into .NTFS-3G/Usermapping, with .NTFS-3G\n");
		} else
			printf("# Insert the above lines into .NTFS-3G/Usermapping, with .NTFS-3G\n");
#ifdef WIN32
		printf("# being a directory of the root of the NTFS file system.\n");

		/* Get the drive letter to the file system */
		driveletter = 0;
		if ((((name[0] >= 'a') && (name[0] <= 'z'))
			|| ((name[0] >= 'A') && (name[0] <= 'Z')))
		    && (name[1] == ':'))
			driveletter = name[0];
		else {
			if (GetCurrentDirectoryA(MAXFILENAME, fullname)
					&& (fullname[1] == ':'))
				driveletter = fullname[0];
		}
		if (driveletter) {
			printf("# Example : %c:\\.NTFS-3G\\UserMapping\n",
				driveletter);
		}
#else
		printf("# being a hidden subdirectory of the root of the NTFS file system.\n");

		/* Get the path to the root of the file system */
		if (name[0] != '/') {
			p = getcwd(fullname,MAXFILENAME);
			if (p) {
				strcat(fullname,"/");
				strcat(fullname,name);
			}
		} else {
			strcpy(fullname,name);
			p = fullname;
		}
		if (p) {
			/* go down the path to inode 5 (fails on symlinks) */
			do {
				lstat(fullname,&st);
				q = strrchr(p,'/');
				if (q && (st.st_ino != 5))
					*q = 0;
			} while (strchr(p,'/') && (st.st_ino != 5));
		}
		if (p && (st.st_ino == 5)) {
			printf("# Example : ");
			printname(stdout,p);
			printf("/.NTFS-3G/UserMapping\n");
		}
#endif
	} else {
		printf("** Not possible : ");
		printname(stdout,name);
		printf(" was not created by a Windows user\n");
		err = TRUE;
	}
	return (err);
}

#ifdef WIN32

/*
 *		Check whether a directory with reparse data is a junction
 *	or a symbolic link
 */

BOOL islink(const char *filename)
{
	WIN32_FIND_DATAW found;
	HANDLE search;
	BOOL link;

	link = FALSE;
	search = FindFirstFileW((LPCWSTR)filename, &found);
	if (search != INVALID_HANDLE_VALUE) {
		link = (found.dwReserved0 == IO_REPARSE_TAG_MOUNT_POINT)
		   || (found.dwReserved0 == IO_REPARSE_TAG_SYMLINK);
		FindClose(search);
	}
	return (link);
}

#if POSIXACLS
BOOL iterate(RECURSE call, const char *fullname, const struct POSIX_SECURITY *pxdesc)
#else
BOOL iterate(RECURSE call, const char *fullname, mode_t mode)
#endif
{
	WIN32_FIND_DATAW found;
	HANDLE search;
	BOOL err;
	unsigned int len;
	char *filter;
	char *inner;

	err = FALSE;
	filter = (char*)malloc(MAXFILENAME);
	inner = (char*)malloc(MAXFILENAME);
	if (filter && inner) {
		len = utf16len(fullname);
		memcpy(filter, fullname, 2*len);
		makeutf16(&filter[2*len],"\\*.*");
		search = FindFirstFileW((LPCWSTR)filter, &found);
		if (search != INVALID_HANDLE_VALUE) {
			do {
				if (found.cFileName[0] != UNICODE('.')) {
					memcpy(inner, fullname, 2*len);
					inner[2*len] = '\\';
					inner[2*len+1] = 0;
					memcpy(&inner[2*len+2],found.cFileName,
						2*utf16len((char*)found.cFileName)+2);
					if (opt_v)
						if (opt_b)
							printf("#\n#\n");
						else
							printf("\n\n");
					switch (call) {
					case RECSHOW :
						if (recurseshow(inner))
							err = TRUE;
						break;
#if POSIXACLS
					case RECSETPOSIX :
						if (recurseset_posix(inner,pxdesc))
							err = TRUE;
						break;
#else
					case RECSET :
						if (recurseset(inner,mode))
							err = TRUE;
						break;
#endif
					default :
						err = TRUE;
					}
				}
			} while (FindNextFileW(search, &found));
			FindClose(search);
		}
		free(filter);
		free(inner);
	} else {
		printf("** Cannot process deeper : not enough memory\n");
		errors++;
		err = TRUE;
	}
	return (err);
}



/*
 *		   Display all the parameters associated to a file (Windows version)
 */

void showfull(const char *fullname, BOOL isdir)
{
	static char attr[MAXATTRSZ];
#if POSIXACLS
	struct POSIX_SECURITY *pxdesc;
#endif
	ULONG attrsz;
	int mode;
	uid_t uid;
	gid_t gid;
	int attrib;
	int level;

	printf("%s ",(isdir ? "Directory" : "File"));
	printname(stdout,fullname);
	printf("\n");

       /* get individual parameters, as when trying to get them */
       /* all, and one (typically SACL) is missing, we get none, */
       /* and concatenate them, to be able to compute the hash code */

	attrsz = getfull(attr, fullname);
	if (attrsz) {
			if (opt_v || opt_b) {
				hexdump(attr,attrsz,8);
				printf("Computed hash : 0x%08lx\n",
					(unsigned long)hash((le32*)attr,attrsz));
			}
			if (opt_v && opt_b) {
				printf("# %s ",(isdir ? "Directory" : "File"));
				printname(stdout,fullname);
				printf(" hash 0x%lx\n",
					(unsigned long)hash((le32*)attr,attrsz));
			}
			attrib = GetFileAttributesW((LPCWSTR)fullname);
			if (attrib == INVALID_FILE_ATTRIBUTES) {
				printf("** Could not get file attrib\n");
				errors++;
			} else
				printf("Windows attrib : 0x%x\n",attrib);
			if (ntfs_valid_descr(attr,attrsz)) {
#if POSIXACLS
				pxdesc = linux_permissions_posix(attr,isdir);
				if (pxdesc)
					mode = pxdesc->mode;
				else
					mode = 0;
#else
				mode = linux_permissions(attr,isdir);
#endif
				if (opt_v >= 2) {
					level = (opt_b ? 4 : 0);
					showheader(attr,level);
					showusid(attr,level);
					showgsid(attr,level);
					showdacl(attr,isdir,level);
					showsacl(attr,isdir,level);
				}
				uid = linux_owner(attr);
				gid = linux_group(attr);
				if (opt_b) {
					printf("# Interpreted Unix owner %d, group %d, mode 0%03o\n",
						(int)uid,(int)gid,mode);
				} else {
					printf("Interpreted Unix owner %d, group %d, mode 0%03o\n",
						(int)uid,(int)gid,mode);
				}
#if POSIXACLS
				if (pxdesc) {
					if (!opt_b
					    && (pxdesc->defcnt
						|| (pxdesc->tagsset
						    & (POSIX_ACL_USER
							| POSIX_ACL_GROUP
							| POSIX_ACL_MASK))))
						showposix(pxdesc);
					free(pxdesc);
				}
#endif
			} else
				if (opt_b)
					printf("# Descriptor fails sanity check\n");
				else
					printf("Descriptor fails sanity check\n");
	}
}

BOOL recurseshow(const char *fullname)
{
	int attrib;
	int err;
	BOOL isdir;

	err = FALSE;
	attrib = GetFileAttributesW((LPCWSTR)fullname);
	if (attrib != INVALID_FILE_ATTRIBUTES) {
		isdir = (attrib & FILE_ATTRIBUTE_DIRECTORY) != 0;
		showfull(fullname,isdir);
		if (isdir
		   && !((attrib & FILE_ATTRIBUTE_REPARSE_POINT)
			&& islink(fullname))) {
#if POSIXACLS
			err = iterate(RECSHOW, fullname, (struct POSIX_SECURITY*)NULL);
#else
			err = iterate(RECSHOW, fullname, 0);
#endif
		}
	} else {
		printf("** Could not access ");
		printname(stdout,fullname);
		printf("\n");
		printerror(stdout);
		errors++;
		err = TRUE;
	}
	return (err);
}


BOOL singleshow(const char *fullname)
{
	int attrib;
	int err;
	BOOL isdir;

	err = FALSE;
	attrib = GetFileAttributesW((LPCWSTR)fullname);
	if (attrib != INVALID_FILE_ATTRIBUTES) {
		isdir = (attrib & FILE_ATTRIBUTE_DIRECTORY) != 0;
		showfull(fullname,isdir);
	} else { 
		printf("** Could not access ");
		printname(stdout,fullname);
		printf("\n");
		printerror(stdout);
		errors++;
		err = TRUE;
	}
	return (err);
}

BOOL mapproposal(const char *fullname)
{
	char attr[256];
	ULONG attrsz;
	int attrib;
	int err;

	err = FALSE;
	attrsz = 0;
	attrib = GetFileAttributesW((LPCWSTR)fullname);
	if ((attrib != INVALID_FILE_ATTRIBUTES)
	    && GetFileSecurityW((LPCWSTR)fullname,
				OWNER_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION,
				(char*)attr,256,&attrsz)) {
		err = proposal(fullname,attr);
	} else { 
		printf("** Could not access ");
		printname(stdout,fullname);
		printf("\n");
		printerror(stdout);
		err = TRUE;
	}
	return (err);
}

#if POSIXACLS

BOOL recurseset_posix(const char *fullname, const struct POSIX_SECURITY *pxdesc)
{
	int attrib;
	int err;
	BOOL isdir;

	err = FALSE;
	attrib = GetFileAttributesW((LPCWSTR)fullname);
	if (attrib != INVALID_FILE_ATTRIBUTES) {
		isdir = (attrib & FILE_ATTRIBUTE_DIRECTORY) != 0;
		err = !setfull_posix(fullname,pxdesc,isdir);
		if (err) {
			printf("** Failed to update ");
			printname(stdout,fullname);
			printf("\n");
			errors++;
		} else
			if (isdir
			   && !((attrib & FILE_ATTRIBUTE_REPARSE_POINT)
				&& islink(fullname)))
				iterate(RECSETPOSIX, fullname, pxdesc);
	} else { 
		err = GetLastError();
		printf("** Could not access ");
		printname(stdout,fullname);
		printf("\n");
		printerror(stdout);
		err = TRUE;
		errors++;
	}
	return (err);
}

#else

BOOL recurseset(const char *fullname, int mode)
{
	int attrib;
	int err;
	BOOL isdir;

	err = FALSE;
	attrib = GetFileAttributesW((LPCWSTR)fullname);
	if (attrib != INVALID_FILE_ATTRIBUTES) {
		isdir = (attrib & FILE_ATTRIBUTE_DIRECTORY) != 0;
		setfull(fullname,mode,isdir);
		if (isdir
		   && !((attrib & FILE_ATTRIBUTE_REPARSE_POINT)
			&& islink(fullname)))
			iterate(RECSETPOSIX, fullname, mode);
	} else { 
		err = GetLastError();
		printf("** Could not access ");
		printname(stdout,fullname);
		printf("\n");
		printerror(stdout);
		err = TRUE;
		errors++;
	}
	return (err);
}

#endif

#if POSIXACLS

BOOL singleset_posix(const char *path, const struct POSIX_SECURITY *pxdesc)
{
	BOOL isdir;
	BOOL err;
	int attrib;

	err = FALSE;
	attrib = GetFileAttributesW((LPCWSTR)path);
	if (attrib != INVALID_FILE_ATTRIBUTES) {
		isdir = (attrib & FILE_ATTRIBUTE_DIRECTORY);
		err = !setfull_posix(path,pxdesc,isdir);
		if (err) {
			printf("** Failed to update ");
			printname(stdout,path);
			printf("\n");
			errors++;
		}
	} else {
		printf("** Could not access ");
		printname(stdout,path);
		printf("\n");
		printerror(stdout);
		errors++;
		err = TRUE;
	}
	return (!err);
}

#endif

BOOL singleset(const char *path, int mode)
{
	BOOL isdir;
	BOOL err;
	int attrib;

	err = FALSE;
	attrib = GetFileAttributesW((LPCWSTR)path);
	if (attrib != INVALID_FILE_ATTRIBUTES) {
		isdir = (attrib & FILE_ATTRIBUTE_DIRECTORY);
		setfull(path,mode,isdir);
	} else {
		printf("** Could not access ");
		printname(stdout,path);
		printf("\n");
		printerror(stdout);
		errors++;
		err = TRUE;
	}
	return (!err);
}

#else

/*
 *		   Display all the parameters associated to a file (Linux version)
 */

void showfull(const char *fullname, BOOL isdir)
{
	static char attr[MAXATTRSZ];
	static char part[MAXATTRSZ];
#if POSIXACLS
	struct POSIX_SECURITY *pxdesc;
#endif
	struct SECURITY_DATA *psecurdata;
	char *newattr;
	int securindex;
	int mode;
	int level;
	int attrib;
	u32 attrsz;
	u32 partsz;
	uid_t uid;
	gid_t gid;

	if (opt_v || opt_b)
		printf("%s %s\n",(isdir ? "Directory" : "File"),fullname);

       /* get individual parameters, as when trying to get them */
       /* all, and one (typically SACL) is missing, we get none */
       /* and concatenate them, to be able to compute the checksum */

	partsz = 0;
	securindex = ntfs_get_file_security(ntfs_context,fullname,
				OWNER_SECURITY_INFORMATION,
				(char*)part,MAXATTRSZ,&partsz);

	attrib = ntfs_get_file_attributes(ntfs_context, fullname);
	if (attrib == INVALID_FILE_ATTRIBUTES) {
		printf("** Could not get file attrib\n");
		errors++;
	}
	if ((securindex < 0)
	    || (securindex >= MAXSECURID)
	    || ((securindex > 0)
		&& ((!opt_r && !opt_b)
		   || !securdata[securindex >> SECBLKSZ]
		   || !securdata[securindex >> SECBLKSZ][securindex & ((1 << SECBLKSZ) - 1)].filecount)))
		{
		if (opt_v || opt_b) {
			if ((securindex < -1) || (securindex >= MAXSECURID))
				printf("Security key : 0x%x out of range\n",securindex);
			else
				if (securindex == -1)
					printf("Security key : none\n");
				else
					printf("Security key : 0x%x\n",securindex);
		} else {
			printf("%s %s",(isdir ? "Directory" : "File"),fullname);
			if ((securindex < -1) || (securindex >= MAXSECURID))
					printf(" : key 0x%x out of range\n",securindex);
			else
				if (securindex == -1)
					printf(" : no key\n");
				else
					printf(" : key 0x%x\n",securindex);
		}

		attrsz = getfull(attr, fullname);
		if (attrsz) {
			psecurdata = (struct SECURITY_DATA*)NULL;
			if ((securindex < MAXSECURID) && (securindex > 0)) {
				if (!securdata[securindex >> SECBLKSZ])
					newblock(securindex);
				if (securdata[securindex >> SECBLKSZ])
					psecurdata = &securdata[securindex >> SECBLKSZ]
					   [securindex & ((1 << SECBLKSZ) - 1)];
			}
			if (opt_v && (opt_a || opt_b) && psecurdata) {
				newattr = (char*)malloc(attrsz);
				printf("# %s %s hash 0x%lx\n",(isdir ? "Directory" : "File"),
					fullname,
					(unsigned long)hash((le32*)attr,attrsz));
				if (newattr) {
					memcpy(newattr,attr,attrsz);
					psecurdata->attr = newattr;
				}
			}
			if ((opt_v || opt_b)
				&& ((securindex >= MAXSECURID)
				   || (securindex <= 0)
				   || !psecurdata
				   || (!psecurdata->filecount
					&& !psecurdata->flags))) {
				hexdump(attr,attrsz,8);
				printf("Computed hash : 0x%08lx\n",
					(unsigned long)hash((le32*)attr,attrsz));
			}
			if (ntfs_valid_descr((char*)attr,attrsz)) {
#if POSIXACLS
				pxdesc = linux_permissions_posix(attr,isdir);
				if (pxdesc)
					mode = pxdesc->mode;
				else
					mode = 0;
#else
				mode = linux_permissions(attr,isdir);
#endif
				attrib = ntfs_get_file_attributes(ntfs_context,fullname);
				if (opt_v >= 2) {
					level = (opt_b ? 4 : 0);
					showheader(attr,level);
					showusid(attr,level);
					showgsid(attr,level);
					showdacl(attr,isdir,level);
					showsacl(attr,isdir,level);
				}
				if (attrib != INVALID_FILE_ATTRIBUTES)
					printf("Windows attrib : 0x%x\n",attrib);
				uid = linux_owner(attr);
				gid = linux_group(attr);
				if (opt_b) {
					printf("# Interpreted Unix owner %d, group %d, mode 0%03o\n",
						(int)uid,(int)gid,mode);
				} else {
					printf("Interpreted Unix owner %d, group %d, mode 0%03o\n",
						(int)uid,(int)gid,mode);
				}
#if POSIXACLS
				if (pxdesc) {
					if (!opt_b
					    && (pxdesc->defcnt
					       || (pxdesc->tagsset
						   & (POSIX_ACL_USER
							| POSIX_ACL_GROUP
							| POSIX_ACL_MASK))))
						showposix(pxdesc);
#if USESTUBS
					stdfree(pxdesc); /* allocated within library */
#else
					free(pxdesc);
#endif
				}
#endif
				if ((opt_r || opt_b) && (securindex < MAXSECURID)
				    && (securindex > 0) && psecurdata) {
					psecurdata->filecount++;
					psecurdata->mode = mode;
				}
			} else {
				printf("** Descriptor fails sanity check\n");
				errors++;
			}
		}
	} else
		if (securindex > 0) {
			if (securdata[securindex >> SECBLKSZ]) {
				psecurdata = &securdata[securindex >> SECBLKSZ]
					[securindex & ((1 << SECBLKSZ) - 1)];
				psecurdata->filecount++;
				if (opt_b || opt_r) {
					if (!opt_b && !opt_v)
						printf("%s %s\n",(isdir ? "Directory" : "File"),fullname);
					printf("Security key : 0x%x mode %03o (already displayed)\n",
						securindex,psecurdata->mode);
					if (attrib != INVALID_FILE_ATTRIBUTES)
						printf("Windows attrib : 0x%x\n",attrib);
				} else {
					printf("%s %s",(isdir ? "Directory" : "File"),fullname);
					printf(" : key 0x%x\n",securindex);
				}
				if ((opt_a || opt_b) && opt_v
				    && psecurdata && psecurdata->attr) {
					printf("# %s %s hash 0x%lx\n",(isdir ? "Directory" : "File"),
						fullname,
						(unsigned long)hash((le32*)psecurdata->attr,
							ntfs_attr_size(psecurdata->attr)));
				}
			}
		} else {
			if (!opt_v && !opt_b)
				printf("%s %s",(isdir ? "Directory" : "File"),fullname);
			printf("   (Failed)\n");
			printf("** Could not get security data of %s, partsz %d\n",
				fullname,partsz);
			printerror(stdout);
			errors++;
		}
}

BOOL recurseshow(const char *path)
{
	struct CALLBACK dircontext;
	struct LINK *current;
	BOOL isdir;
	BOOL err;

	err = FALSE;
	dircontext.head = (struct LINK*)NULL;
	dircontext.dir = path;
	isdir = ntfs_read_directory(ntfs_context, path,
			callback, &dircontext);
	if (isdir) {
		showfull(path,TRUE);
		if (opt_v) {
			if (opt_b)
				printf("#\n#\n");
			else
				printf("\n\n");
		}
		while (dircontext.head) {
			current = dircontext.head;
			if (recurseshow(current->name)) err = TRUE;
			dircontext.head = dircontext.head->next;
			free(current);
		}
	} else
		if (errno == ENOTDIR) {
			showfull(path,FALSE);
			if (opt_v) {
				if (opt_b)
					printf("#\n#\n");
				else
					printf("\n\n");
			}
		} else {
			printf("** Could not access %s\n",path);
			printerror(stdout);
			errors++;
			err = TRUE;
		}
	return (!err);
}


BOOL singleshow(const char *path)
{
	BOOL isdir;
	BOOL err;

	err = FALSE;
	isdir = ntfs_read_directory(ntfs_context, path,
			callback, (struct CALLBACK*)NULL);
	if (isdir || (errno == ENOTDIR))
		showfull(path,isdir);
	else {
		printf("** Could not access %s\n",path);
		printerror(stdout);
		errors++;
		err = TRUE;
	}
	return (err);
}

#ifdef HAVE_SETXATTR

static ssize_t ntfs_getxattr(const char *path, const char *name, void *value, size_t size)
{
#if defined(__APPLE__) || defined(__DARWIN__)
    return getxattr(path, name, value, size, 0, 0);
#else
    return getxattr(path, name, value, size);
#endif
}

/*
 *		   Display all the parameters associated to a mounted file
 */

BOOL showmounted(const char *fullname)
{

	static char attr[MAXATTRSZ];
	struct stat st;
#if POSIXACLS
	struct POSIX_SECURITY *pxdesc;
#endif
	BOOL mapped;
	int attrsz;
	int mode;
	uid_t uid;
	gid_t gid;
	u32 attrib;
	int level;
	BOOL isdir;
	BOOL err;

	err = FALSE;
	if (!stat(fullname,&st)) {
		isdir = S_ISDIR(st.st_mode);
		printf("%s ",(isdir ? "Directory" : "File"));
		printname(stdout,fullname);
		printf("\n");

		attrsz = ntfs_getxattr(fullname,"system.ntfs_acl",attr,MAXATTRSZ);
		if (attrsz > 0) {
			if (opt_v) {
				hexdump(attr,attrsz,8);
				printf("Computed hash : 0x%08lx\n",
					(unsigned long)hash((le32*)attr,attrsz));
			}
			if (ntfs_getxattr(fullname,"system.ntfs_attrib",&attrib,4) != 4) {
				printf("** Could not get file attrib\n");
				errors++;
			} else
				printf("Windows attrib : 0x%x\n",(int)attrib);
			if (ntfs_valid_descr(attr,attrsz)) {
				mapped = !local_build_mapping(context.mapping,fullname);
#if POSIXACLS
				if (mapped) {
					pxdesc = linux_permissions_posix(attr,isdir);
					if (pxdesc)
						mode = pxdesc->mode;
					else
						mode = 0;
				} else {
					pxdesc = (struct POSIX_SECURITY*)NULL;
					mode = linux_permissions(attr,isdir);
					printf("No user mapping : "
						"cannot display the Posix ACL\n");
				}
#else
				mode = linux_permissions(attr,isdir);
#endif
				if (opt_v >= 2) {
					level = (opt_b ? 4 : 0);
					showheader(attr,level);
					showusid(attr,level);
					showgsid(attr,level);
					showdacl(attr,isdir,level);
					showsacl(attr,isdir,level);
				}
				if (mapped) {
					uid = linux_owner(attr);
					gid = linux_group(attr);
					printf("Interpreted Unix owner %d, group %d, mode 0%03o\n",
						(int)uid,(int)gid,mode);
				} else {
					printf("Interpreted Unix mode 0%03o (owner and group are unmapped)\n",
						mode);
				}
#if POSIXACLS
				if (pxdesc) {
					if ((pxdesc->defcnt
						|| (pxdesc->tagsset
						    & (POSIX_ACL_USER
							| POSIX_ACL_GROUP
							| POSIX_ACL_MASK))))
						showposix(pxdesc);
#if USESTUBS
					stdfree(pxdesc); /* allocated within library */
#else
					free(pxdesc);
#endif
				}
				if (mapped)
					ntfs_free_mapping(context.mapping);
#endif
			} else {
				printf("Descriptor fails sanity check\n");
				errors++;
			}
		} else {
			printf("** Could not get the NTFS ACL, check whether file is on NTFS\n");
			errors++;
		}
	} else {
		printf("%s not found\n",fullname);
		err = TRUE;
	}
	return (err);
}

BOOL processmounted(const char *fullname)
{

	static char attr[MAXATTRSZ];
	struct stat st;
	int attrsz;
	BOOL err;

	err = FALSE;
	if (!opt_u)
		err = showmounted(fullname);
	else
	if (!stat(fullname,&st)) {
		attrsz = ntfs_getxattr(fullname,"system.ntfs_acl",attr,MAXATTRSZ);
		if (attrsz > 0) {
			if (opt_v) {
				hexdump(attr,attrsz,8);
				printf("Computed hash : 0x%08lx\n",
					(unsigned long)hash((le32*)attr,attrsz));
			}
			if (ntfs_valid_descr(attr,attrsz)) {
				err = proposal(fullname, attr);
			} else {
				printf("*** Descriptor fails sanity check\n");
				errors++;
			}
		} else {
			printf("** Could not get the NTFS ACL, check whether file is on NTFS\n");
			errors++;
		}
	} else {
		printf("%s not found\n",fullname);
		err = TRUE;
	}
	return (err);
}

#else /* HAVE_SETXATTR */

BOOL processmounted(const char *fullname __attribute__((unused)))
{
	fprintf(stderr,"Not possible on this configuration,\n");
	fprintf(stderr,"you have to use an unmounted partition\n");
	return (TRUE);
}

#endif /* HAVE_SETXATTR */

#if POSIXACLS

BOOL recurseset_posix(const char *path, const struct POSIX_SECURITY *pxdesc)
{
	struct CALLBACK dircontext;
	struct LINK *current;
	BOOL isdir;
	BOOL err;

	err = FALSE;
	dircontext.head = (struct LINK*)NULL;
	dircontext.dir = path;
	isdir = ntfs_read_directory(ntfs_context, path,
			callback, &dircontext);
	if (isdir) {
		err = !setfull_posix(path,pxdesc,TRUE);
		if (err) {
			printf("** Failed to update %s\n",path);
			printerror(stdout);
			errors++;
		} else {
			if (opt_b)
				printf("#\n#\n");
			else
				printf("\n\n");
			while (dircontext.head) {
				current = dircontext.head;
				recurseset_posix(current->name,pxdesc);
				dircontext.head = dircontext.head->next;
				free(current);
			}
		}
	} else
		if (errno == ENOTDIR) {
			err = !setfull_posix(path,pxdesc,FALSE);
			if (err) {
				printf("** Failed to update %s\n",path);
				printerror(stdout);
				errors++;
			}
		} else {
			printf("** Could not access %s\n",path);
			printerror(stdout);
			errors++;
			err = TRUE;
		}
	return (!err);
}

#else

BOOL recurseset(const char *path, int mode)
{
	struct CALLBACK dircontext;
	struct LINK *current;
	BOOL isdir;
	BOOL err;

	err = FALSE;
	dircontext.head = (struct LINK*)NULL;
	dircontext.dir = path;
	isdir = ntfs_read_directory(ntfs_context, path,
			callback, &dircontext);
	if (isdir) {
		setfull(path,mode,TRUE);
		if (opt_b)
			printf("#\n#\n");
		else
			printf("\n\n");
		while (dircontext.head) {
			current = dircontext.head;
			recurseset(current->name,mode);
			dircontext.head = dircontext.head->next;
			free(current);
		}
	} else
		if (errno == ENOTDIR)
			setfull(path,mode,FALSE);
		else {
			printf("** Could not access %s\n",path);
			printerror(stdout);
			errors++;
			err = TRUE;
		}
	return (!err);
}

#endif

#if POSIXACLS

BOOL singleset_posix(const char *path, const struct POSIX_SECURITY *pxdesc)
{
	BOOL isdir;
	BOOL err;

	err = FALSE;
	isdir = ntfs_read_directory(ntfs_context, path,
			callback, (struct CALLBACK*)NULL);
	if (isdir || (errno == ENOTDIR)) {
		err = !setfull_posix(path,pxdesc,isdir);
		if (err) {
			printf("** Failed to update %s\n",path);
			printerror(stdout);
			errors++;
		}
	} else {
		printf("** Could not access %s\n",path);
		printerror(stdout);
		errors++;
		err = TRUE;
	}
	return (!err);
}

#endif

BOOL singleset(const char *path, int mode)
{
	BOOL isdir;
	BOOL err;

	err = FALSE;
	isdir = ntfs_read_directory(ntfs_context, path,
			callback, (struct CALLBACK*)NULL);
	if (isdir || (errno == ENOTDIR))
		setfull(path,mode,isdir);
	else {
		printf("** Could not access %s\n",path);
		printerror(stdout);
		errors++;
		err = TRUE;
	}
	return (!err);
}

int callback(struct CALLBACK *dircontext, char *ntfsname,
	int length, int type,
	long long pos  __attribute__((unused)), u64 mft_ref __attribute__((unused)),
	unsigned int dt_type __attribute__((unused)))
{
	struct LINK *linkage;
	char *name;
	int newlth;
	int size;

	size = utf8size(ntfsname,length);
	if (dircontext
	    && (type != 2)     /* 2 : dos name (8+3) */
	    && (size > 0)      /* chars convertible to utf8 */
	    && ((length > 2)
		|| (ntfsname[0] != '.')
		|| (ntfsname[1] != '\0')
		|| ((ntfsname[2] || ntfsname[3])
		   && ((ntfsname[2] != '.') || (ntfsname[3] != '\0'))))) {
		linkage = (struct LINK*)malloc(sizeof(struct LINK) 
				+ strlen(dircontext->dir)
				+ size + 2);
		if (linkage) {
		/* may find ".fuse_hidden*" files */
		/* recommendation is not to hide them, so that */
		/* the user has a clue to delete them */
			strcpy(linkage->name,dircontext->dir);
			if (linkage->name[strlen(linkage->name) - 1] != '/')
				strcat(linkage->name,"/");
			name = &linkage->name[strlen(linkage->name)];
			newlth = makeutf8(name,ntfsname,length);
			name[newlth] = 0;
			linkage->next = dircontext->head;
			dircontext->head = linkage;
		}
	}
	return (0);
}

#endif

#ifdef WIN32

/*
 *		 Backup security descriptors in a directory tree (Windows mode)
 */

BOOL backup(const char *root)
{
	BOOL err;
	time_t now;
	const char *txtime;

	now = time((time_t*)NULL);
	txtime = ctime(&now);
	printf("#\n# Recursive ACL collection on %s#\n",txtime);
	err = recurseshow(root);
	return (err);
}

#else

/*
 *		 Backup security descriptors in a directory tree (Linux mode)
 */

BOOL backup(const char *volume, const char *root)
{
	BOOL err;
	int count;
	int i,j;
	time_t now;
	const char *txtime;

	now = time((time_t*)NULL);
	txtime = ctime(&now);
	if (!getuid() && open_security_api()) {
		if (open_volume(volume,MS_RDONLY)) {
			printf("#\n# Recursive ACL collection on %s#\n",txtime);
			err = recurseshow(root);
			count = 0;
			for (i=0; i<(MAXSECURID + (1 << SECBLKSZ) - 1)/(1 << SECBLKSZ); i++)
				if (securdata[i])
					for (j=0; j<(1 << SECBLKSZ); j++)
						if (securdata[i][j].filecount) {
							count++;
						}
			printf("# %d security keys\n",count);
			close_volume(volume);
		} else {
			fprintf(stderr,"Could not open volume %s\n",volume);
			printerror(stdout);
			err = TRUE;
		}
		close_security_api();
	} else {
		if (getuid())
			fprintf(stderr,"This is only possible as root\n");
		else
			fprintf(stderr,"Could not open security API\n");
		err = TRUE;
	}
	return (err);
}

#endif

#ifdef WIN32

/*
 *		 List security descriptors in a directory tree (Windows mode)
 */

BOOL listfiles(const char *root)
{
	BOOL err;

	if (opt_r) {
		printf("\nRecursive file check\n");
		err = recurseshow(root);
	} else
		err = singleshow(root);
	return (err);
}

#else

/*
 *		 List security descriptors in a directory tree (Linux mode)
 */

BOOL listfiles(const char *volume, const char *root)
{
	BOOL err;
	int i,j;
	int count;

	if (!getuid() && open_security_api()) {
		if (open_volume(volume,MS_RDONLY)) {
			if (opt_r) {
				printf("\nRecursive file check\n");
				err = recurseshow(root);
				printf("Summary\n");
				count = 0;
				for (i=0; i<(MAXSECURID + (1 << SECBLKSZ) - 1)/(1 << SECBLKSZ); i++)
					if (securdata[i])
						for (j=0; j<(1 << SECBLKSZ); j++)
							if (securdata[i][j].filecount) {
								printf("Key 0x%x : %d files, mode 0%03o\n",
									i*(1 << SECBLKSZ)+j,securdata[i][j].filecount,
									securdata[i][j].mode);
								count++;
							}
				printf("%d security keys\n",count);
			} else
				err = singleshow(root);
			close_volume(volume);
		} else {
			fprintf(stderr,"Could not open volume %s\n",volume);
			printerror(stdout);
			err = TRUE;
		}
		close_security_api();
	} else {
		if (getuid())
			fprintf(stderr,"This is only possible as root\n");
		else
			fprintf(stderr,"Could not open security API\n");
		err = TRUE;
	}
	return (err);
}

BOOL mapproposal(const char *volume, const char *name)
{
	BOOL err;
	u32 attrsz;
	int securindex;
	char attr[256]; /* header (20) and a couple of SIDs (max 40 each) */

	err = FALSE;
	if (!getuid() && open_security_api()) {
		if (open_volume(volume,MS_RDONLY)) {

			attrsz = 0;
			securindex = ntfs_get_file_security(ntfs_context,name,
					OWNER_SECURITY_INFORMATION
					    | GROUP_SECURITY_INFORMATION,
					(char*)attr,MAXATTRSZ,&attrsz);
			if (securindex)
				err = proposal(name,attr);
			else {
				fprintf(stderr,"*** Could not get the ACL of %s\n",
						name);
				printerror(stdout);
				errors++;
			}
			close_volume(volume);
		} else {
			fprintf(stderr,"Could not open volume %s\n",volume);
			printerror(stdout);
			err = TRUE;
		}
		close_security_api();
	} else {
		if (getuid())
			fprintf(stderr,"This is only possible as root\n");
		else
			fprintf(stderr,"Could not open security API\n");
		err = TRUE;
	}
	return (err);
}

#endif

#ifndef WIN32

/*
 *		Check whether a SDS entry is valid
 */

BOOL valid_sds(const char *attr, unsigned int offset,
		unsigned int entrysz, unsigned int size, u32 prevkey,
		BOOL second)
{
	BOOL unsane;
	u32 comphash;
	u32 key;

	unsane = FALSE;
	if (!get4l(attr,0) && !get4l(attr,4)) {
		printf("Entry at 0x%lx was deleted\n",(long)offset);
	} else {
		if ((ntfs_attr_size(&attr[20]) + 20) > entrysz) {
			printf("** Entry is truncated (expected size %ld)\n",
				(long)ntfs_attr_size(&attr[20] + 20));
			unsane = TRUE;
			errors++;
		}
		if ((ntfs_attr_size(&attr[20]) + 20) < entrysz) {
			printf("** Extra data appended to entry (expected size %ld)\n",
				(long)ntfs_attr_size(&attr[20]) + 20);
			warnings++;
		}
		if (!unsane && !ntfs_valid_descr((const char*)&attr[20],size)) {
			printf("** General sanity check has failed\n");
			unsane = TRUE;
			errors++;
		}
		if (!unsane) {
			comphash = hash((const le32*)&attr[20],entrysz-20);
			if ((u32)get4l(attr,0) == comphash) {
				if (opt_v >= 2)
					printf("Hash	 0x%08lx (correct)\n",
						(unsigned long)comphash);
			} else {
				printf("** hash  0x%08lx (computed : 0x%08lx)\n",
					(unsigned long)get4l(attr,0),
					(unsigned long)comphash);
				unsane = TRUE;
				errors++;
			}
		}
		if (!unsane) {
			if ((second ? get8l(attr,8) + 0x40000 : get8l(attr,8)) == offset) {
				if (opt_v >= 2)
					printf("Offset	 0x%lx (correct)\n",(long)offset);
			} else {
				printf("** offset  0x%llx (expected : 0x%llx)\n",
					(long long)get8l(attr,8),
					(long long)(second ? get8l(attr,8) - 0x40000 : get8l(attr,8)));
//				unsane = TRUE;
				errors++;
			}
		}
		if (!unsane) {
			key = get4l(attr,4);
			if (opt_v >= 2)
				printf("Key	 0x%x\n",(int)key);
			if (key) {
				if (key <= prevkey) {
					printf("** Unordered key 0x%lx after 0x%lx\n",
						(long)key,(long)prevkey);
					unsane = TRUE;
					errors++;
				}
			}
		}
	}
	return (!unsane);
}

/*
 *		Check whether a SDS entry is consistent with other known data
 *	and store current data for subsequent checks
 */

int consist_sds(const char *attr, unsigned int offset,
		unsigned int entrysz, BOOL second)
{
	int errcnt;
	u32 key;
	u32 comphash;
	struct SECURITY_DATA *psecurdata;

	errcnt = 0;
	key = get4l(attr,4);
	if ((key > 0) && (key < MAXSECURID)) {
		printf("Valid entry at 0x%lx for key 0x%lx\n",
			(long)offset,(long)key);
		if (!securdata[key >> SECBLKSZ])
			newblock(key);
		if (securdata[key >> SECBLKSZ]) {
			psecurdata = &securdata[key >> SECBLKSZ][key & ((1 << SECBLKSZ) - 1)];
			comphash = hash((const le32*)&attr[20],entrysz-20);
			if (psecurdata->flags & INSDS1) {
				if (psecurdata->hash != comphash) {
					printf("** Different hash values : $SDS-1 0x%08lx $SDS-2 0x%08lx\n",
						(unsigned long)psecurdata->hash,
						(unsigned long)comphash);
					errcnt++;
					errors++;
				}
				if (psecurdata->offset != get8l(attr,8)) {
					printf("** Different offsets : $SDS-1 0x%llx $SDS-2 0x%llx\n",
						(long long)psecurdata->offset,(long long)get8l(attr,8));
					errcnt++;
					errors++;
				}
				if (psecurdata->length != get4l(attr,16)) {
					printf("** Different lengths : $SDS-1 0x%lx $SDS-2 0x%lx\n",
						(long)psecurdata->length,(long)get4l(attr,16));
					errcnt++;
					errors++;
				}
			} else {
				if (second) {
					printf("** Entry was not present in $SDS-1\n");
					errcnt++;
					errors++;
				}
				psecurdata->hash = comphash;
				psecurdata->offset = get8l(attr,8);
				psecurdata->length = get4l(attr,16);
			}
			psecurdata->flags |= (second ? INSDS2 : INSDS1);
		}
	} else
		if (key || get4l(attr,0)) {
			printf("** Security_id 0x%x out of bounds\n",key);
			warnings++;
		}
	return (errcnt);
}


/*
 *		       Auditing of $SDS (Linux only)
 */

int audit_sds(BOOL second)
{
	static char attr[MAXATTRSZ + 20];
	BOOL isdir;
	BOOL done;
	BOOL unsane;
	u32 prevkey;
	int errcnt;
	int size;
	unsigned int entrysz;
	unsigned int entryalsz;
	unsigned int offset;
	int count;
	int deleted;
	int mode;

	if (second)
		printf("\nAuditing $SDS-2\n");
	else
		printf("\nAuditing $SDS-1\n");
	errcnt = 0;
	offset = (second ? 0x40000 : 0);
	count = 0;
	deleted = 0;
	done = FALSE;
	prevkey = 0;

	  /* get size of first record */

	size = ntfs_read_sds(ntfs_context,(char*)attr,20,offset);
	if (size != 20) {
		if ((size < 0) && (errno == ENOTSUP))
			printf("** There is no $SDS-%d in this volume\n",
							(second ? 2 : 1));
		else {
			printf("** Could not open $SDS-%d, size %d\n",
							(second ? 2 : 1),size);
			errors++;
			errcnt++;
		}
	} else
		do {
			entrysz = get4l(attr,16);
			entryalsz = ((entrysz - 1) | 15) + 1;
			if (entryalsz <= (MAXATTRSZ + 20)) {
				/* read next header in anticipation, to get its size */
				size = ntfs_read_sds(ntfs_context,
					(char*)&attr[20],entryalsz,offset + 20);
				if (opt_v)
					printf("\nAt offset 0x%lx got %lu bytes\n",(long)offset,(long)size);
			} else {
				printf("** Security attribute is too long (%ld bytes) - stopping\n",
					(long)entryalsz);
				errcnt++;
			}
			if ((entryalsz > (MAXATTRSZ + 20)) || (size < (int)(entrysz - 20)))
				done = TRUE;
			else {
				if (opt_v) {
					printf("Entry size %d bytes\n",entrysz);
					hexdump(&attr[20],size,8);
				}

				unsane = !valid_sds(attr,offset,entrysz,
					size,prevkey,second);
				if (!unsane) {
					if (!get4l(attr,0) && !get4l(attr,4))
						deleted++;
					else
						count++;
					errcnt += consist_sds(attr,offset,
						entrysz, second);
					if (opt_v >= 2) {
						isdir = guess_dir(&attr[20]);
						printf("Assuming %s descriptor\n",(isdir ? "directory" : "file"));
						showheader(&attr[20],0);
						showusid(&attr[20],0);
						showgsid(&attr[20],0);
						showdacl(&attr[20],isdir,0);
						showsacl(&attr[20],isdir,0);
						mode = linux_permissions(
						    &attr[20],isdir);
						printf("Interpreted Unix mode 0%03o\n",mode);
					}
					prevkey = get4l(attr,4);
				}
				if (!unsane) {
					memcpy(attr,&attr[entryalsz],20);
					offset += entryalsz;
					if (!get4l(attr,16)
					   || ((((offset - 1) | 0x3ffff) - offset + 1) < 20)) {
						if (second)
							offset = ((offset - 1) | 0x7ffff) + 0x40001;
						else
							offset = ((offset - 1) | 0x7ffff) + 1;
						if (opt_v)
							printf("Trying next SDS-%d block at offset 0x%lx\n",
								(second ? 2 : 1), (long)offset);
						size = ntfs_read_sds(ntfs_context,
							(char*)attr,20,offset);
						if (size != 20) {
							if (opt_v)
								printf("Assuming end of $SDS, got %d bytes\n",size);
							done = TRUE;
						}
					}
				} else {
					printf("** Sanity check failed - stopping there\n");
					errcnt++;
					errors++;
					done = TRUE;
				}
			}
		} while (!done);
	if (count || deleted || errcnt) {
		printf("%d valid and %d deleted entries in $SDS-%d\n",
				count,deleted,(second ? 2 : 1));
		printf("%d errors in $SDS-%c\n",errcnt,(second ? '2' : '1'));
	}
	return (errcnt);
}

/*
 *		Check whether a SII entry is sane
 */

BOOL valid_sii(const char *entry, u32 prevkey)
{
	BOOL valid;
	u32 key;

	valid = TRUE;
	key = get4l(entry,16);
	if (key <= prevkey) {
		printf("** Unordered key 0x%lx after 0x%lx\n",
			(long)key,(long)prevkey);
		valid = FALSE;
		errors++;
	}
	prevkey = key;
	if (get2l(entry,0) != 20) {
		printf("** offset %d (instead of 20)\n",(int)get2l(entry,0));
		valid = FALSE;
		errors++;
	}
	if (get2l(entry,2) != 20) {
		printf("** size %d (instead of 20)\n",(int)get2l(entry,2));
		valid = FALSE;
		errors++;
	}
	if (get4l(entry,4) != 0) {
		printf("** fill1 %d (instead of 0)\n",(int)get4l(entry,4));
		valid = FALSE;
		errors++;
	}
	if (get2l(entry,12) & 1) {
		if (get2l(entry,8) != 48) {
			printf("** index size %d (instead of 48)\n",(int)get2l(entry,8));
			valid = FALSE;
			errors++;
		}
	} else
		if (get2l(entry,8) != 40) {
			printf("** index size %d (instead of 40)\n",(int)get2l(entry,8));
			valid = FALSE;
			errors++;
		}
	if (get2l(entry,10) != 4) {
		printf("** index key size %d (instead of 4)\n",(int)get2l(entry,10));
		valid = FALSE;
		errors++;
	}
	if ((get2l(entry,12) & ~3) != 0) {
		printf("** flags 0x%x (instead of < 4)\n",(int)get2l(entry,12));
		valid = FALSE;
		errors++;
	}
	if (get2l(entry,14) != 0) {
		printf("** fill2 %d (instead of 0)\n",(int)get2l(entry,14));
		valid = FALSE;
		errors++;
	}
	if (get4l(entry,24) != key) {
		printf("** key 0x%x (instead of 0x%x)\n",
						(int)get4l(entry,24),(int)key);
		valid = FALSE;
		errors++;
	}
	return (valid);
}

/*
 *		Check whether a SII entry is consistent with other known data
 */

int consist_sii(const char *entry)
{
	int errcnt;
	u32 key;
	struct SECURITY_DATA *psecurdata;

	errcnt = 0;
	key = get4l(entry,16);
	if ((key > 0) && (key < MAXSECURID)) {
		printf("Valid entry for key 0x%lx\n",(long)key);
		if (!securdata[key >> SECBLKSZ])
			newblock(key);
		if (securdata[key >> SECBLKSZ]) {
			psecurdata = &securdata[key >> SECBLKSZ][key & ((1 << SECBLKSZ) - 1)];
			psecurdata->flags |= INSII;
			if (psecurdata->flags & (INSDS1 | INSDS2)) {
				if ((u32)get4l(entry,20) != psecurdata->hash) {
					printf("** hash 0x%x (instead of 0x%x)\n",
						(unsigned int)get4l(entry,20),
						(unsigned int)psecurdata->hash);
					errors++;
				}
				if (get8l(entry,28) != psecurdata->offset) {
					printf("** offset 0x%llx (instead of 0x%llx)\n",
						(long long)get8l(entry,28),
						(long long)psecurdata->offset);
					errors++;
				}
				if (get4l(entry,36) != psecurdata->length) {
					printf("** length 0x%lx (instead of %ld)\n",
						(long)get4l(entry,36),
						(long)psecurdata->length);
					errors++;
				}
			} else {
				printf("** Entry was not present in $SDS\n");
				errors++;
				psecurdata->hash = get4l(entry,20);
				psecurdata->offset = get8l(entry,28);
				psecurdata->length = get4l(entry,36);
				if (opt_v) {
					printf("   hash 0x%x\n",(unsigned int)psecurdata->hash);
					printf("   offset 0x%llx\n",(long long)psecurdata->offset);
					printf("   length %ld\n",(long)psecurdata->length);
				}
				errcnt++;
			}
		}
	} else {
		printf("** Security_id 0x%x out of bounds\n",key);
		warnings++;
	}
	return (errcnt);
}


/*
 *		       Auditing of $SII (Linux only)
 */

int audit_sii()
{
	char *entry;
	int errcnt;
	u32 prevkey;
	BOOL valid;
	BOOL done;
	int count;

	printf("\nAuditing $SII\n");
	errcnt = 0;
	count = 0;
	entry = (char*)NULL;
	prevkey = 0;
	done = FALSE;
	do {
		entry = (char*)ntfs_read_sii(ntfs_context,(void*)entry);
		if (entry) {
			valid = valid_sii(entry,prevkey);
			if (valid) {
				count++;
				errcnt += consist_sii(entry);
				prevkey = get4l(entry,16);
			} else
				errcnt++;
		} else
			if ((errno == ENOTSUP) && !prevkey)
				printf("** There is no $SII in this volume\n");
	} while (entry && !done);
	if (count || errcnt) {
		printf("%d valid entries in $SII\n",count);
		printf("%d errors in $SII\n",errcnt);
	}
	return (errcnt);
}

/*
 *		Check whether a SII entry is sane
 */

BOOL valid_sdh(const char *entry, u32 prevkey, u32 prevhash)
{
	BOOL valid;
	u32 key;
	u32 currhash;

	valid = TRUE;
	currhash = get4l(entry,16);
	key = get4l(entry,20);
	if ((currhash < prevhash)
		|| ((currhash == prevhash) && (key <= prevkey))) {
		printf("** Unordered hash and key 0x%x 0x%x after 0x%x 0x%x\n",
			(unsigned int)currhash,(unsigned int)key,
			(unsigned int)prevhash,(unsigned int)prevkey);
		valid = FALSE;
		errors++;
	}
	if ((opt_v >= 2) && (currhash == prevhash))
		printf("Hash collision (not an error)\n");

	if (get2l(entry,0) != 24) {
		printf("** offset %d (instead of 24)\n",(int)get2l(entry,0));
		valid = FALSE;
		errors++;
	}
	if (get2l(entry,2) != 20) {
		printf("** size %d (instead of 20)\n",(int)get2l(entry,2));
		valid = FALSE;
		errors++;
	}
	if (get4l(entry,4) != 0) {
		printf("** fill1 %d (instead of 0)\n",(int)get4l(entry,4));
		valid = FALSE;
		errors++;
	}
	if (get2l(entry,12) & 1) {
		if (get2l(entry,8) != 56) {
			printf("** index size %d (instead of 56)\n",(int)get2l(entry,8));
			valid = FALSE;
			errors++;
		}
	} else
		if (get2l(entry,8) != 48) {
			printf("** index size %d (instead of 48)\n",(int)get2l(entry,8));
			valid = FALSE;
			errors++;
		}
	if (get2l(entry,10) != 8) {
		printf("** index key size %d (instead of 8)\n",(int)get2l(entry,10));
		valid = FALSE;
		errors++;
	}
	if ((get2l(entry,12) & ~3) != 0) {
		printf("** flags 0x%x (instead of < 4)\n",(int)get2l(entry,12));
		valid = FALSE;
		errors++;
	}
	if (get2l(entry,14) != 0) {
		printf("** fill2 %d (instead of 0)\n",(int)get2l(entry,14));
		valid = FALSE;
		errors++;
	}
	if ((u32)get4l(entry,24) != currhash) {
		printf("** hash 0x%x (instead of 0x%x)\n",
			(unsigned int)get4l(entry,24),(unsigned int)currhash);
		valid = FALSE;
		errors++;
	}
	if (get4l(entry,28) != key) {
		printf("** key 0x%x (instead of 0x%x)\n",
			(int)get4l(entry,28),(int)key);
		valid = FALSE;
		errors++;
	}
	if (get4l(entry,44)
		&& (get4l(entry,44) != 0x490049)) {
		printf("** fill3 0x%lx (instead of 0 or 0x490049)\n",
			(long)get4l(entry,44));
		valid = FALSE;
		errors++;
	}
	return (valid);
}

/*
 *		Check whether a SDH entry is consistent with other known data
 */

int consist_sdh(const char *entry)
{
	int errcnt;
	u32 key;
	struct SECURITY_DATA *psecurdata;

	errcnt = 0;
	key = get4l(entry,20);
	if ((key > 0) && (key < MAXSECURID)) {
		printf("Valid entry for key 0x%lx\n",(long)key);
		if (!securdata[key >> SECBLKSZ])
			newblock(key);
		if (securdata[key >> SECBLKSZ]) {
			psecurdata = &securdata[key >> SECBLKSZ][key & ((1 << SECBLKSZ) - 1)];
			psecurdata->flags |= INSDH;
			if (psecurdata->flags & (INSDS1 | INSDS2 | INSII)) {
				if ((u32)get4l(entry,24) != psecurdata->hash) {
					printf("** hash 0x%x (instead of 0x%x)\n",
						(unsigned int)get4l(entry,24),
						(unsigned int)psecurdata->hash);
					errors++;
				}
				if (get8l(entry,32) != psecurdata->offset) {
					printf("** offset 0x%llx (instead of 0x%llx)\n",
						(long long)get8l(entry,32),
						(long long)psecurdata->offset);
					errors++;
				}
				if (get4l(entry,40) != psecurdata->length) {
					printf("** length %ld (instead of %ld)\n",
						(long)get4l(entry,40),
						(long)psecurdata->length);
					errors++;
				}
			} else {
				printf("** Entry was not present in $SDS nor in $SII\n");
				errors++;
				psecurdata->hash = get4l(entry,24);
				psecurdata->offset = get8l(entry,32);
				psecurdata->length = get4l(entry,40);
				if (opt_v) {
					printf("   offset 0x%llx\n",(long long)psecurdata->offset);
					printf("   length %ld\n",(long)psecurdata->length);
				}
				errcnt++;
			}
		}
	} else {
		printf("** Security_id 0x%x out of bounds\n",key);
		warnings++;
	}
	return (errcnt);
}

/*
 *		       Auditing of $SDH (Linux only)
 */

int audit_sdh()
{
	char *entry;
	int errcnt;
	int count;
	u32 prevkey;
	u32 prevhash;
	BOOL valid;
	BOOL done;

	printf("\nAuditing $SDH\n");
	count = 0;
	errcnt = 0;
	prevkey = 0;
	prevhash = 0;
	entry = (char*)NULL;
	done = FALSE;
	do {
		entry = (char*)ntfs_read_sdh(ntfs_context,(void*)entry);
		if (entry) {
			valid = valid_sdh(entry,prevkey,prevhash);
			if (valid) {
				count++;
				errcnt += consist_sdh(entry);
				prevhash = get4l(entry,16);
				prevkey = get4l(entry,20);
			} else
				errcnt++;
		} else
			if ((errno == ENOTSUP) && !prevkey)
				printf("** There is no $SDH in this volume\n");
	} while (entry && !done);
	if (count || errcnt) {
		printf("%d valid entries in $SDH\n",count);
		printf("%d errors in $SDH\n",errcnt);
	}
	return (errcnt);
}

/*
 *		Audit summary
 */

void audit_summary()
{
	int count;
	int flags;
	int cnt;
	int found;
	int i,j;

	count = 0;
	found = 0;
	if (opt_r) printf("Summary of security key use :\n");
	for (i=0; i<(MAXSECURID + (1 << SECBLKSZ) - 1)/(1 << SECBLKSZ); i++)
		if (securdata[i])
			for (j=0; j<(1 << SECBLKSZ); j++) {
				flags = securdata[i][j].flags & (INSDS1 + INSDS2 + INSII + INSDH);
				if (flags) found++;
				if (flags
					&& (flags != (INSDS1 + INSDS2 + INSII + INSDH)))
					{
					if (!count && !opt_r)
						printf("\n** Keys not present in all files :\n");
					cnt = securdata[i][j].filecount;
					if (opt_r)
						printf("Key 0x%x used by %d %s, not in",
							i*(1 << SECBLKSZ)+j,cnt,
							(cnt > 1 ? "files" : "file"));
					else
						printf("Key 0x%x not in", i*(1 << SECBLKSZ)+j);
					if (!(flags & INSDS1))
						printf(" SDS-1");
					if (!(flags & INSDS2))
						printf(" SDS-2");
					if (!(flags & INSII))
						printf(" SII");
					if (!(flags & INSDH))
						printf(" SDH");
					printf("\n");
					count++;
				} else {
					cnt = securdata[i][j].filecount;
					if (opt_r && cnt)
						printf("Key 0x%x used by %d %s\n",
							i*(1 << SECBLKSZ)+j,cnt,
							(cnt > 1 ? "files" : "file"));
				}
			}
	if (found) {
		if (count)
			printf("%d keys not present in all lists\n",count);
		else
			printf("All keys are present in all lists\n");
	}
}

/*
 *		       Auditing (Linux only)
 */

BOOL audit(const char *volume)
{
	BOOL err;

	err = FALSE;
	if (!getuid() && open_security_api()) {
		if (open_volume(volume,MS_RDONLY)) {
			if (audit_sds(FALSE)) err = TRUE;
			if (audit_sds(TRUE)) err = TRUE;
			if (audit_sii()) err = TRUE;
			if (audit_sdh()) err = TRUE;
			if (opt_r) recurseshow("/");

			audit_summary();
			close_volume(volume);
		}
		else {
			fprintf(stderr,"Could not open volume %s\n",volume);
			printerror(stdout);
			err = TRUE;
		}
		close_security_api();
	}
	else {
		if (getuid())
			fprintf(stderr,"This is only possible as root\n");
		else fprintf(stderr,"Could not open security API\n");
		err = TRUE;
	}
	return (err);
}

#endif

#if POSIXACLS

/*
 *		Encode a Posix ACL string
 *	[d:]{ugmo}:uid[:perms],...
 */

struct POSIX_SECURITY *encode_posix_acl(const char *str)
{
	int acccnt;
	int defcnt;
	int i,k,l;
	int c;
	s32 id;
	u16 perms;
	u16 apermsset;
	u16 dpermsset;
	u16 tag;
	u16 tagsset;
	mode_t mode;
	BOOL defacl;
	BOOL dmask;
	BOOL amask;
	const char *p;
	struct POSIX_ACL *acl;
	struct POSIX_SECURITY *pxdesc;
	enum { PXBEGIN, PXTAG, PXTAG1, PXID, PXID1, PXID2,
		PXPERM, PXPERM1, PXPERM2, PXOCT, PXNEXT, PXEND, PXERR
	} state;

				/* raw evaluation of ACE count */
	p = str;
	amask = FALSE;
	dmask = FALSE;
	if (*p == 'd') {
		acccnt = 0;
		defcnt = 1;
	} else {
		if ((*p >= '0') && (*p <= '7'))
			acccnt = 0;
		else
			acccnt = 1;
		defcnt = 0;
	}
	while (*p)
		if (*p++ == ',') {
			if (*p == 'd') {
				defcnt++;
				if (p[1] && (p[2] == 'm'))
					dmask = TRUE;
			} else {
				acccnt++;
				if (*p == 'm')
					amask = TRUE;
			}
		}
		/* account for an implicit mask if none defined */
	if (acccnt && !amask)
		acccnt++;
	if (defcnt && !dmask)
		defcnt++;
	pxdesc = (struct POSIX_SECURITY*)malloc(sizeof(struct POSIX_SECURITY)
				+ (acccnt + defcnt)*sizeof(struct POSIX_ACE));
	if (pxdesc) {
		pxdesc->acccnt = acccnt;
		pxdesc->firstdef = acccnt;
		pxdesc->defcnt = defcnt;
		acl = &pxdesc->acl;
		p = str;
		state = PXBEGIN;
		id = 0;
		defacl = FALSE;
		mode = 0;
		apermsset = 0;
		dpermsset = 0;
		tag = 0;
		perms = 0;
		k = l = 0;
		c = *p++;
		while ((state != PXEND) && (state != PXERR)) {
			switch (state) {
			case PXBEGIN :
				if (c == 'd') {
					defacl = TRUE;
					state = PXTAG1;
					break;
				} else
					if ((c >= '0') && (c <= '7')) {
						mode = c - '0';
						state = PXOCT;
						break;
					}
				defacl = FALSE;
				/* fall through */
			case PXTAG :
				switch (c) {
				case 'u' :
					tag = POSIX_ACL_USER;
					state = PXID;
					break;
				case 'g' :
					tag = POSIX_ACL_GROUP;
					state = PXID;
					break;
				case 'o' :
					tag = POSIX_ACL_OTHER;
					state = PXID;
					break;
				case 'm' :
					tag = POSIX_ACL_MASK;
					state = PXID;
					break;
				default :
					state = PXERR;
					break;
				}
				break;
			case PXTAG1 :
				if (c == ':')
					state = PXTAG;
				else
					state = PXERR;
				break;
			case PXID :
				if (c == ':') {
					if ((tag == POSIX_ACL_OTHER)
					   || (tag == POSIX_ACL_MASK))
						state = PXPERM;
					else
						state = PXID1;
				} else
					state = PXERR;
				break;
			case PXID1 :
				if ((c >= '0') && (c <= '9')) {
					id = c - '0';
					state = PXID2;
				} else
					if (c == ':') {
						id = -1;
						if (tag == POSIX_ACL_USER)
							tag = POSIX_ACL_USER_OBJ;
						if (tag == POSIX_ACL_GROUP)
							tag = POSIX_ACL_GROUP_OBJ;
						state = PXPERM1;
					} else
						state = PXERR;
				break;
			case PXID2 :
				if ((c >= '0') && (c <= '9'))
					id = 10*id + c - '0';
				else
					if (c == ':')
						state = PXPERM1;
					else
						state = PXERR;
				break;
			case PXPERM :
				if (c == ':') {
					id = -1;
					state = PXPERM1;
				} else
					state = PXERR;
				break;
			case PXPERM1 :
				if ((c >= '0') && (c <= '7')) {
					perms = c - '0';
					state = PXNEXT;
					break;
				}
				state = PXPERM2;
				perms = 0;
				/* fall through */
			case PXPERM2 :
				switch (c) {
				case 'r' :
					perms |= POSIX_PERM_R;
					break;
				case 'w' :
					perms |= POSIX_PERM_W;
					break;
				case 'x' :
					perms |= POSIX_PERM_X;
					break;
				case ',' :
				case '\0' :
					if (defacl) {
						i = acccnt + l++;
						dpermsset |= perms;
					} else {
						i = k++;
						apermsset |= perms;
					}
					acl->ace[i].tag = tag;
					acl->ace[i].perms = perms;
					acl->ace[i].id = id;
					if (c == '\0')
						state = PXEND;
					else
						state = PXBEGIN;
					break;
				}
				break;
			case PXNEXT :
				if (!c || (c == ',')) {
					if (defacl) {
						i = acccnt + l++;
						dpermsset |= perms;
					} else {
						i = k++;
						apermsset |= perms;
					}
					acl->ace[i].tag = tag;
					acl->ace[i].perms = perms;
					acl->ace[i].id = id;
					if (c == '\0')
						state = PXEND;
					else
						state = PXBEGIN;
				} else
					state = PXERR;
				break;
			case PXOCT :
				if ((c >= '0') && (c <= '7'))
					mode = (mode << 3) + c - '0';
				else
					if (c == '\0')
						state = PXEND;
					else
						state = PXBEGIN;
				break;
			default :
				break;
			}
			c = *p++;
		}
			/* insert default mask if none defined */
		if (acccnt && !amask) {
			i = k++;
			acl->ace[i].tag = POSIX_ACL_MASK;
			acl->ace[i].perms = apermsset;
			acl->ace[i].id = -1;
		}
		if (defcnt && !dmask) {
			i = acccnt + l++;
			acl->ace[i].tag = POSIX_ACL_MASK;
			acl->ace[i].perms = dpermsset;
			acl->ace[i].id = -1;
		}
			/* compute the mode and tagsset */
		tagsset = 0;
		for (i=0; i<acccnt; i++)
			tagsset |= acl->ace[i].tag;
			switch (acl->ace[i].tag) {
			case POSIX_ACL_USER_OBJ :
				mode |= acl->ace[i].perms << 6;
				break;
			case POSIX_ACL_GROUP_OBJ :
				mode |= acl->ace[i].perms << 3;
				break;
			case POSIX_ACL_OTHER :
				mode |= acl->ace[i].perms;
				break;
			default :
				break;
			}
		pxdesc->mode = mode;
		pxdesc->tagsset = tagsset;
		pxdesc->acl.version = POSIX_VERSION;
		pxdesc->acl.flags = 0;
		pxdesc->acl.filler = 0;
		if (state != PXERR)
			ntfs_sort_posix(pxdesc);
showposix(pxdesc);
		if ((state == PXERR)
		   || (k != acccnt)
		   || (l != defcnt)
                   || !ntfs_valid_posix(pxdesc)) {
			if (~pxdesc->tagsset
			    & (POSIX_ACL_USER_OBJ | POSIX_ACL_GROUP_OBJ | POSIX_ACL_OTHER))
				fprintf(stderr,"User, group or other permissions missing\n");
			else
				fprintf(stderr,"Bad ACL description\n");
			free(pxdesc);
			pxdesc = (struct POSIX_SECURITY*)NULL;
		} else
			if (opt_v >= 2) {
				printf("Interpreted input description :\n");
				showposix(pxdesc);
			}
	} else
		errno = ENOMEM;
	return (pxdesc);
}

#endif /* POSIXACLS */


int getoptions(int argc, char *argv[])
{
	int xarg;
	int narg;
	const char *parg;
	BOOL err;

	opt_a = FALSE;
	opt_b = FALSE;
	opt_e = FALSE;
	opt_h = FALSE;
#if FORCEMASK
	opt_m = FALSE;
#endif
	opt_r = FALSE;
	opt_s = FALSE;
#if SELFTESTS & !USESTUBS
	opt_t = FALSE;
#endif
	opt_u = FALSE;
	opt_v = 0;
	xarg = 1;
	err = FALSE;
	while ((xarg < argc) && (argv[xarg][0] == '-')) {
		parg = argv[xarg++];
		while (*++parg)
			switch (*parg)
				{
#ifndef WIN32
				case 'a' :
					opt_a = TRUE;
					break;
#endif
				case 'b' :
					opt_b = TRUE;
					break;
				case 'e' :
					opt_e = TRUE;
					break;
				case 'h' :
					opt_h = TRUE;
					break;
#if FORCEMASK
				case 'm' :
					opt_m = TRUE;
					break;
#endif
				case 'r' :
				case 'R' :
					opt_r = TRUE;
					break;
				case 's' :
					opt_s = TRUE;
					break;
#if SELFTESTS & !USESTUBS
				case 't' :
					opt_t = TRUE;
					break;
#endif
				case 'u' :
					opt_u = TRUE;
					break;
				case 'v' :
					opt_v++;
					break;
				default :
					fprintf(stderr,"Invalid option -%c\n",*parg);
					err = TRUE;
			}
	}
	narg = argc - xarg;
#ifdef WIN32
	if (   ((opt_h || opt_s) && (narg > 1))
	    || ((opt_r || opt_b || opt_u) && ((narg < 1) || (narg > 2)))
#if SELFTESTS & !USESTUBS
	    || (opt_t && (narg > 0))
#endif
	    || (opt_e && !opt_s)
	    || (!opt_h && !opt_r && !opt_b && !opt_s
#if SELFTESTS & !USESTUBS
			&& !opt_t
#endif
			&& ((narg < 1) || (narg > 2))))

		err = TRUE;
	if (err) {
		xarg = 0;
		fprintf(stderr,"Usage:\n");
#if SELFTESTS & !USESTUBS
		fprintf(stderr,"   secaudit -t\n");
		fprintf(stderr,"	run self-tests\n");
#endif
		fprintf(stderr,"   secaudit -h [file]\n");
		fprintf(stderr,"	display security descriptors within file\n");
		fprintf(stderr,"   secaudit [-v] file\n");
		fprintf(stderr,"	display the security parameters of file\n");
		fprintf(stderr,"   secaudit -r[v] directory\n");
		fprintf(stderr,"	display the security parameters of files in directory\n");
		fprintf(stderr,"   secaudit -b[v] directory\n");
		fprintf(stderr,"        backup the security parameters of files in directory\n");
		fprintf(stderr,"   secaudit -s[ev] [backupfile]\n");
		fprintf(stderr,"        set the security parameters as indicated in backup file\n");
		fprintf(stderr,"        with -e also set extra parameters (Windows attrib)\n");
		fprintf(stderr,"   secaudit perms file\n");
		fprintf(stderr,"	set the security parameters of file to perms\n");
		fprintf(stderr,"   secaudit -r[v] perms directory\n");
		fprintf(stderr,"	set the security parameters of files in directory to perms\n");
		fprintf(stderr,"   secaudit -u file\n");
		fprintf(stderr,"	get a user mapping proposal applicable to file\n");
#if POSIXACLS
		fprintf(stderr,"   Note: perms can be an octal mode or a Posix ACL description\n");
#else
		fprintf(stderr,"   Note: perms is an octal mode\n");
#endif
		fprintf(stderr,"          -v is for verbose, -vv for very verbose\n");
	}
#else
	if (   (opt_h && (narg > 1))
	    || (opt_a && (narg != 1))
	    || ((opt_r || opt_b || opt_s || opt_u)
			&& ((narg < 1) || (narg > 3)))
#if SELFTESTS & !USESTUBS
	    || (opt_t && (narg > 0))
#endif
	    || (opt_e && !opt_s)
	    || (!opt_h && !opt_a && !opt_r && !opt_b && !opt_s && !opt_u
#if SELFTESTS & !USESTUBS
		&& !opt_t
#endif
#ifdef HAVE_SETXATTR
		&& ((narg < 1) || (narg > 3))))
#else
		&& ((narg < 2) || (narg > 3))))
#endif
		err = TRUE;
	if (err) {
		xarg = 0;
		fprintf(stderr,"Usage:\n");
#if SELFTESTS & !USESTUBS
		fprintf(stderr,"   secaudit -t\n");
		fprintf(stderr,"	run self-tests\n");
#endif
		fprintf(stderr,"   secaudit -h [file]\n");
		fprintf(stderr,"	display security descriptors within file\n");
		fprintf(stderr,"   secaudit -a[rv] volume\n");
		fprintf(stderr,"	audit the volume\n");
		fprintf(stderr,"   secaudit [-v] volume file\n");
		fprintf(stderr,"	display the security parameters of file\n");
		fprintf(stderr,"   secaudit -r[v] volume directory\n");
		fprintf(stderr,"	display the security parameters of files in directory\n");
		fprintf(stderr,"   secaudit -b[v] volume directory\n");
		fprintf(stderr,"        backup the security parameters of files in directory\n");
		fprintf(stderr,"   secaudit -s[ev] volume [backupfile]\n");
		fprintf(stderr,"        set the security parameters as indicated in backup file\n");
		fprintf(stderr,"        with -e also set extra parameters (Windows attrib)\n");
		fprintf(stderr,"   secaudit volume perms file\n");
		fprintf(stderr,"	set the security parameters of file to perms\n");
		fprintf(stderr,"   secaudit -r[v] volume perms directory\n");
		fprintf(stderr,"	set the security parameters of files in directory to perms\n");
		fprintf(stderr,"   secaudit -u volume file\n");
		fprintf(stderr,"	get a user mapping proposal applicable to file\n");
#ifdef HAVE_SETXATTR
		fprintf(stderr," special cases, do not require being root :\n");
		fprintf(stderr,"   secaudit -u mounted-file\n");
		fprintf(stderr,"	get a user mapping proposal applicable to mounted file\n");
		fprintf(stderr,"   secaudit [-v] mounted-file\n");
		fprintf(stderr,"	display the security parameters of a mounted file\n");
#endif
#if POSIXACLS
		fprintf(stderr,"   Note: perms can be an octal mode or a Posix ACL description\n");
#else
		fprintf(stderr,"   Note: perms is an octal mode\n");
#endif
		fprintf(stderr,"          -v is for verbose, -vv for very verbose\n");
	}
#endif
	if ((sizeof(SID) != 12) && !err) {
		fprintf(stderr,"Possible alignment problem, check your compiler options\n");
		err = TRUE;
		xarg = 0;
	}
	return (xarg);
}

/*
 *		Memory allocation with checks
 */

#undef malloc
#undef calloc
#undef free
#undef isalloc

void dumpalloc(const char *txt)
{
	struct CHKALLOC *q;

	if (firstalloc) {
		printf("alloc table at %s\n",txt);
		for (q=firstalloc; q; q=q->next)
			printf("%08lx : %u bytes at %08lx allocated at %s line %d\n",
				(long)q,(unsigned int)q->size,
				(long)q->alloc,q->file,q->line);
	}
}

void *chkmalloc(size_t size, const char *file, int line)
{
	void *p;
	struct CHKALLOC *q;

	p = (void*)malloc(size+1);
	if (p) {
		((unsigned char*)p)[size] = 0xaa;
		q = (struct CHKALLOC*)malloc(sizeof(struct CHKALLOC));
		if (q) {
			q->next = firstalloc;
			q->alloc = p;
			q->size = size;
			q->file = file;
			q->line = line;
			firstalloc = q;
		}
	}
	return (p);
}

void *chkcalloc(size_t cnt, size_t size, const char *file, int line)
{
	return (chkmalloc(cnt*size,file,line));
}

void chkfree(void *p, const char *file, int line)
{
	struct CHKALLOC *q;
	struct CHKALLOC *r;

	if (p) {
		if (firstalloc && (firstalloc->alloc == p)) {
			r = firstalloc;
			firstalloc = firstalloc->next;
		} else {
			q = firstalloc;
			if (q)
				while (q->next && (q->next->alloc != p))
					q = q->next;
			if (q && q->next) {
				r = q->next;
				q->next = r->next;
			} else {
				r = (struct CHKALLOC*)NULL;
				printf("** freeing unallocated memory in %s line %d\n",file,line);
				if (!isatty(1))
					fprintf(stderr,"** freeing unallocated memory in %s line %d\n",file,line);
			}
		}
		if (r) {
			if (((unsigned char*)p)[r->size] != 0xaa) {
				printf("** memory corruption, alloc in %s line %d release in %s %d\n",
					r->file,r->line,file,line);
				if (!isatty(1))
					fprintf(stderr,"** memory corruption, alloc in %s line %d release in %s %d\n",
						r->file,r->line,file,line);
			}
			memset(p,0xaa,r->size);
			free(r);
			free(p);
		}
	}
}

void *stdmalloc(size_t size)
{
	return (malloc(size));
}

void stdfree(void *p)
{
	free(p);
}

BOOL chkisalloc(void *p, const char *file, int line)
{
	struct CHKALLOC *q;

	if (p) {
		q = firstalloc;
		while (q && (q->alloc != p))
			q = q->next;
	} else
		q = (struct CHKALLOC*)NULL;
	if (!p || !q) {
		printf("error in %s %d : 0x%lx not allocated\n",file,line,(long)p);
	}
	return (p && q);
}




#ifdef WIN32

/*
 *		 Windows version
 */

main(argc,argv)
int argc;
char *argv[];
{
	FILE *fd;
	int xarg;
	int mode;
	unsigned int size;
	BOOL cmderr;
	char *filename;
	const char *p;
	int i;
#if POSIXACLS
	struct POSIX_SECURITY *pxdesc;
#endif

	printf("%s\n",BANNER);
	cmderr = FALSE;
	errors = 0;
	warnings = 0;
	xarg = getoptions(argc,argv);
	if (xarg) {
		for (i=0; i<(MAXSECURID + (1 << SECBLKSZ) - 1)/(1 << SECBLKSZ); i++)
			securdata[i] = (struct SECURITY_DATA*)NULL;
#if POSIXACLS
		context.mapping[MAPUSERS] = (struct MAPPING*)NULL;
		context.mapping[MAPGROUPS] = (struct MAPPING*)NULL;
#endif
		firstalloc = (struct CHKALLOC*)NULL;
		mappingtype = MAPNONE;
		switch (argc - xarg) {
		case 0 :
			if (opt_h)
				showhex(stdin);
			else
				if (opt_s)
					restore(stdin);
#if SELFTESTS & !USESTUBS
			if (opt_t)
				selftests();
#endif
			break;
		case 1 :
			if (opt_h || opt_s) {
				fd = fopen(argv[xarg],"r");
				if (fd) {
					if (opt_h)
						showhex(fd);
					else
						restore(fd);
					fclose(fd);
				} else { 
					fprintf(stderr,"Could not open %s\n",argv[xarg]);
					cmderr = TRUE;
				}
			} else {
				size = utf16size(argv[xarg]);
				if (size) {
					filename = (char*)malloc(2*size + 2);
					if (filename) {
						makeutf16(filename,argv[xarg]);
						if (opt_u) {
							cmderr = mapproposal(filename);
						} else {
#if POSIXACLS
							if (local_build_mapping(context.mapping,filename)) {
								printf("*** Could not get user mapping data\n");
								warnings++;
							}
#endif
							if (opt_b)
								cmderr = backup(filename);
							else {
								if (opt_r)
									cmderr = listfiles(filename);
								else
									cmderr = singleshow(filename);
							}
#if POSIXACLS
							ntfs_free_mapping(context.mapping);
#endif
						}
						free(filename);
					} else {
						fprintf(stderr,"No more memory\n");
						cmderr = TRUE;
					}
				} else {
					fprintf(stderr,"Bad UTF-8 name \"%s\"\n",argv[xarg]);
					cmderr = TRUE;
				}
			}
			break;
		case 2 :
			mode = 0;
			p = argv[xarg];
#if POSIXACLS
			pxdesc = encode_posix_acl(p);
			if (pxdesc) {
				size = utf16size(argv[xarg + 1]);
				if (size) {
					filename = (char*)malloc(2*size + 2);
					if (filename) {
						makeutf16(filename,argv[xarg + 1]);
						if (local_build_mapping(context.mapping,filename)) {
							printf("*** Could not get user mapping data\n");
							warnings++;
						}
						if (opt_r) {
							for (i=0; i<(MAXSECURID + (1 << SECBLKSZ) - 1)/(1 << SECBLKSZ); i++)
								securdata[i] = (struct SECURITY_DATA*)NULL;
							recurseset_posix(filename,pxdesc);
						} else
							singleset_posix(filename,pxdesc);
						ntfs_free_mapping(context.mapping);
						free(filename);
					} else {
						fprintf(stderr,"No more memory\n");
						cmderr = TRUE;
					}
					chkfree(pxdesc,__FILE__,__LINE__);
				} else {
					fprintf(stderr,"Bad UTF-8 name \"%s\"\n",argv[xarg + 1]);
					cmderr = TRUE;
				}
			}
#else
			while ((*p >= '0') && (*p <= '7'))
				mode = (mode << 3) + (*p++) - '0';
			if (*p) {
				fprintf(stderr,"New mode should be given in octal\n");
				cmderr = TRUE;
			} else {
				size = utf16size(argv[xarg + 1]);
				if (size) {
					filename = (char*)malloc(2*size + 2);
					if (filename) {
						makeutf16(filename,argv[xarg + 1]);
#if POSIXACLS
						if (local_build_mapping(&context,filename)) {
							printf("*** Could not get user mapping data\n");
							warnings++;
						}
#endif
						if (opt_r) {
							for (i=0; i<(MAXSECURID + (1 << SECBLKSZ) - 1)/(1 << SECBLKSZ); i++)
								securdata[i] = (struct SECURITY_DATA*)NULL;
							recurseset(filename,mode);
						} else
							singleset(filename,mode);
						free(filename);
					} else {
						fprintf(stderr,"No more memory\n");
						cmderr = TRUE;
					}
				} else {
					fprintf(stderr,"Bad UTF-8 name \"%s\"\n",argv[xarg + 1]);
					cmderr = TRUE;
				}
			}
#endif
			break;
#if FORCEMASK
		case 3 :
			mode = 0;
			forcemsk = 0;
			p = argv[xarg];
			while (*p) {
				if ((*p >= '0') && (*p <= '9'))
					forcemsk = (forcemsk << 4) + *p - '0';
				else forcemsk = (forcemsk << 4) + (*p & 7) + 9;
			p++;
			}
			p = argv[xarg + 1];
			while ((*p >= '0') && (*p <= '7'))
				mode = (mode << 3) + (*p++) - '0';
			if (*p) {
				fprintf(stderr,"New mode should be given in octal\n");
				cmderr = TRUE;
			} else {
				if (opt_r) {
					recurseset(argv[xarg + 2],mode);
				}
				else singleset(argv[xarg + 2],mode);
			}
			break;
#endif
		}
		if (warnings)
			printf("** %u %s signalled\n",warnings,
				(warnings > 1 ? "warnings were" : "warning was"));
		if (errors)
			printf("** %u %s found\n",errors,
				(errors > 1 ? "errors were" : "error was"));
		else
			if (!cmderr)
				printf("No errors were found\n");
		if (!isatty(1)) {
			fflush(stdout);
			if (warnings)
				fprintf(stderr,"** %u %s signalled\n",warnings,
					(warnings > 1 ? "warnings were" : "warning was"));
			if (errors)
				fprintf(stderr,"** %u %s found\n",errors,
					(errors > 1 ? "errors were" : "error was"));
			else
				fprintf(stderr,"No errors were found\n");
		freeblocks();
		}
	}
	dumpalloc("termination");
	if (cmderr || errors)
		exit(1);
	return (0);
}

#else

/*
 *		 Linux version
 */

int main(int argc, char *argv[])
{
	FILE *fd;
	const char *p;
	int xarg;
	BOOL cmderr;
	int i;
#if POSIXACLS
	struct POSIX_SECURITY *pxdesc;
#else
	unsigned int mode;
#endif

	printf("%s\n",BANNER);
	cmderr = FALSE;
	errors = 0;
	warnings = 0;
	xarg = getoptions(argc,argv);
	if (xarg) {
		for (i=0; i<(MAXSECURID + (1 << SECBLKSZ) - 1)/(1 << SECBLKSZ); i++)
			securdata[i] = (struct SECURITY_DATA*)NULL;
#if POSIXACLS
		context.mapping[MAPUSERS] = (struct MAPPING*)NULL;
		context.mapping[MAPGROUPS] = (struct MAPPING*)NULL;
#endif
		firstalloc = (struct CHKALLOC*)NULL;
		mappingtype = MAPNONE;
		switch (argc - xarg) {
		case 0 :
			if (opt_h)
				showhex(stdin);
#if SELFTESTS & !USESTUBS
			if (opt_t)
				selftests();
#endif
			break;
		case 1 :
			if (opt_a)
				cmderr = audit(argv[xarg]);
			else
				if (opt_h) {
					fd = fopen(argv[xarg],"rb");
					if (fd) {
						showhex(fd);
						fclose(fd);
					} else { 
						fprintf(stderr,"Could not open %s\n",argv[xarg]);
						cmderr = TRUE;
					}
				} else
					if (opt_b)
						cmderr = backup(argv[xarg],"/");
					else
						if (opt_r)
							cmderr = listfiles(argv[xarg],"/");
						else
							if (opt_s)
								cmderr = dorestore(argv[xarg],stdin);
							else
								cmderr = processmounted(argv[xarg]);
			break;
		case 2 :
			if (opt_b)
				cmderr = backup(argv[xarg],argv[xarg+1]);
			else
				if (opt_s) {
					fd = fopen(argv[xarg+1],"rb");
					if (fd) {
						if (dorestore(argv[xarg],fd))
							cmderr = TRUE;
						fclose(fd);
					} else { 
						fprintf(stderr,"Could not open %s\n",argv[xarg]);
						cmderr = TRUE;
					}
				} else
					if (opt_u)
						cmderr = mapproposal(argv[xarg],argv[xarg+1]);
					else
						cmderr = listfiles(argv[xarg],argv[xarg+1]);
			break;
		case 3 :
			p = argv[xarg+1];
#if POSIXACLS
			pxdesc = encode_posix_acl(p);
			if (pxdesc) {
				if (!getuid() && open_security_api()) {
					if (open_volume(argv[xarg],MS_NONE)) {
						if (opt_r) {
							for (i=0; i<(MAXSECURID + (1 << SECBLKSZ) - 1)/(1 << SECBLKSZ); i++)
								securdata[i] = (struct SECURITY_DATA*)NULL;
							recurseset_posix(argv[xarg + 2],pxdesc);
						} else
							singleset_posix(argv[xarg + 2],pxdesc);
						close_volume(argv[xarg]);
					} else {
						fprintf(stderr,"Could not open volume %s\n",argv[xarg]);
						printerror(stderr);
						cmderr = TRUE;
					}
					close_security_api();
				} else {
					if (getuid())
						fprintf(stderr,"This is only possible as root\n");
					else
						fprintf(stderr,"Could not open security API\n");
					cmderr = TRUE;
				}
			chkfree(pxdesc,__FILE__,__LINE__);
			} else
				cmderr = TRUE;
#else
			mode = 0;
			while ((*p >= '0') && (*p <= '7'))
				mode = (mode << 3) + (*p++) - '0';
			if (*p) {
				fprintf(stderr,"New mode should be given in octal\n");
				cmderr = TRUE;
			} else
				if (!getuid() && open_security_api()) {
					if (open_volume(argv[xarg],MS_NONE)) {
						if (opt_r) {
							for (i=0; i<(MAXSECURID + (1 << SECBLKSZ) - 1)/(1 << SECBLKSZ); i++)
								securdata[i] = (struct SECURITY_DATA*)NULL;
							recurseset(argv[xarg + 2],mode);
						} else
							singleset(argv[xarg + 2],mode);
						close_volume(argv[xarg]);
					} else {
						fprintf(stderr,"Could not open volume %s\n",argv[xarg]);
						printerror(stderr);
						cmderr = TRUE;
					}
					close_security_api();
				} else {
					if (getuid())
						fprintf(stderr,"This is only possible as root\n");
					else
						fprintf(stderr,"Could not open security API\n");
					cmderr = TRUE;
				}
#endif
			break;
		}
		if (warnings)
			printf("** %u %s signalled\n",warnings,
				(warnings > 1 ? "warnings were" : "warning was"));
		if (errors)
			printf("** %u %s found\n",errors,
				(errors > 1 ? "errors were" : "error was"));
		else
			if (!cmderr)
				printf("No errors were found\n");
		if (!isatty(1)) {
			fflush(stdout);
			if (warnings)
				fprintf(stderr,"** %u %s signalled\n",warnings,
					(warnings > 1 ? "warnings were" : "warning was"));
			if (errors)
				fprintf(stderr,"** %u %s found\n",errors,
					(errors > 1 ? "errors were" : "error was"));
			else
				if (!cmderr)
					fprintf(stderr,"No errors were found\n");
		}
		freeblocks();
	} else
		cmderr = TRUE;
	dumpalloc("termination");
	if (cmderr || errors)
		exit(1);
	return (0);
}

#endif
