180bca3d3SAxel Dörfler/**
280bca3d3SAxel Dörfler * security.c - Handling security/ACLs in NTFS.  Originated from the Linux-NTFS project.
380bca3d3SAxel Dörfler *
480bca3d3SAxel Dörfler * Copyright (c) 2004 Anton Altaparmakov
580bca3d3SAxel Dörfler * Copyright (c) 2005-2006 Szabolcs Szakacsits
680bca3d3SAxel Dörfler * Copyright (c) 2006 Yura Pakhuchiy
70490778eSAugustin Cavalier * Copyright (c) 2007-2015 Jean-Pierre Andre
880bca3d3SAxel Dörfler *
980bca3d3SAxel Dörfler * This program/include file is free software; you can redistribute it and/or
1080bca3d3SAxel Dörfler * modify it under the terms of the GNU General Public License as published
1180bca3d3SAxel Dörfler * by the Free Software Foundation; either version 2 of the License, or
1280bca3d3SAxel Dörfler * (at your option) any later version.
1380bca3d3SAxel Dörfler *
1480bca3d3SAxel Dörfler * This program/include file is distributed in the hope that it will be
1580bca3d3SAxel Dörfler * useful, but WITHOUT ANY WARRANTY; without even the implied warranty
1780bca3d3SAxel Dörfler * GNU General Public License for more details.
1880bca3d3SAxel Dörfler *
1980bca3d3SAxel Dörfler * You should have received a copy of the GNU General Public License
2080bca3d3SAxel Dörfler * along with this program (in the main directory of the NTFS-3G
2180bca3d3SAxel Dörfler * distribution in the file COPYING); if not, write to the Free Software
2280bca3d3SAxel Dörfler * Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
2380bca3d3SAxel Dörfler */
2480bca3d3SAxel Dörfler
2580bca3d3SAxel Dörfler#ifdef HAVE_CONFIG_H
2680bca3d3SAxel Dörfler#include "config.h"
2780bca3d3SAxel Dörfler#endif
2880bca3d3SAxel Dörfler
2980bca3d3SAxel Dörfler#ifdef HAVE_STDIO_H
3080bca3d3SAxel Dörfler#include <stdio.h>
3180bca3d3SAxel Dörfler#endif
3280bca3d3SAxel Dörfler#ifdef HAVE_STDLIB_H
3380bca3d3SAxel Dörfler#include <stdlib.h>
3480bca3d3SAxel Dörfler#endif
3580bca3d3SAxel Dörfler#ifdef HAVE_STRING_H
3680bca3d3SAxel Dörfler#include <string.h>
3780bca3d3SAxel Dörfler#endif
3880bca3d3SAxel Dörfler#ifdef HAVE_ERRNO_H
3980bca3d3SAxel Dörfler#include <errno.h>
4080bca3d3SAxel Dörfler#endif
410d2c294fSGerasim Troeglazov#ifdef HAVE_FCNTL_H
420d2c294fSGerasim Troeglazov#include <fcntl.h>
430d2c294fSGerasim Troeglazov#endif
440d2c294fSGerasim Troeglazov#ifdef HAVE_SYS_STAT_H
450d2c294fSGerasim Troeglazov#include <sys/stat.h>
460d2c294fSGerasim Troeglazov#endif
470d2c294fSGerasim Troeglazov
480d2c294fSGerasim Troeglazov#include <unistd.h>
490d2c294fSGerasim Troeglazov#include <pwd.h>
500d2c294fSGerasim Troeglazov#include <grp.h>
5180bca3d3SAxel Dörfler
520490778eSAugustin Cavalier#ifdef __HAIKU__
530490778eSAugustin Cavalier#define getgrgid(a) NULL
540490778eSAugustin Cavalier#define getpwuid(a) NULL
550490778eSAugustin Cavalier#define getgrnam(x) NULL
560490778eSAugustin Cavalier#define getpwnam(x) NULL
570490778eSAugustin Cavalier#endif
580490778eSAugustin Cavalier
59da0906f2SGerasim Troeglazov#include "compat.h"
600d2c294fSGerasim Troeglazov#include "param.h"
6180bca3d3SAxel Dörfler#include "types.h"
6280bca3d3SAxel Dörfler#include "layout.h"
6380bca3d3SAxel Dörfler#include "attrib.h"
640d2c294fSGerasim Troeglazov#include "index.h"
650d2c294fSGerasim Troeglazov#include "dir.h"
660d2c294fSGerasim Troeglazov#include "bitmap.h"
6780bca3d3SAxel Dörfler#include "security.h"
680d2c294fSGerasim Troeglazov#include "acls.h"
690d2c294fSGerasim Troeglazov#include "cache.h"
7080bca3d3SAxel Dörfler#include "misc.h"
710490778eSAugustin Cavalier#include "xattrs.h"
72da0906f2SGerasim Troeglazov
730d2c294fSGerasim Troeglazov/*
740d2c294fSGerasim Troeglazov *	JPA NTFS constants or structs
750d2c294fSGerasim Troeglazov *	should be moved to layout.h
760d2c294fSGerasim Troeglazov */
770d2c294fSGerasim Troeglazov
780d2c294fSGerasim Troeglazov#define ALIGN_SDS_BLOCK 0x40000 /* Alignment for a $SDS block */
790d2c294fSGerasim Troeglazov#define ALIGN_SDS_ENTRY 16 /* Alignment for a $SDS entry */
800d2c294fSGerasim Troeglazov#define STUFFSZ 0x4000 /* unitary stuffing size for $SDS */
810d2c294fSGerasim Troeglazov#define FIRST_SECURITY_ID 0x100 /* Lowest security id */
820d2c294fSGerasim Troeglazov
830d2c294fSGerasim Troeglazov	/* Mask for attributes which can be forced */
840d2c294fSGerasim Troeglazov#define FILE_ATTR_SETTABLE ( FILE_ATTR_READONLY		\
850d2c294fSGerasim Troeglazov				| FILE_ATTR_HIDDEN	\
860d2c294fSGerasim Troeglazov				| FILE_ATTR_SYSTEM	\
870d2c294fSGerasim Troeglazov				| FILE_ATTR_ARCHIVE	\
880d2c294fSGerasim Troeglazov				| FILE_ATTR_TEMPORARY	\
890d2c294fSGerasim Troeglazov				| FILE_ATTR_OFFLINE	\
900d2c294fSGerasim Troeglazov				| FILE_ATTR_NOT_CONTENT_INDEXED )
910d2c294fSGerasim Troeglazov
920d2c294fSGerasim Troeglazovstruct SII {		/* this is an image of an $SII index entry */
930d2c294fSGerasim Troeglazov	le16 offs;
940d2c294fSGerasim Troeglazov	le16 size;
950d2c294fSGerasim Troeglazov	le32 fill1;
960d2c294fSGerasim Troeglazov	le16 indexsz;
970d2c294fSGerasim Troeglazov	le16 indexksz;
980d2c294fSGerasim Troeglazov	le16 flags;
990d2c294fSGerasim Troeglazov	le16 fill2;
1000d2c294fSGerasim Troeglazov	le32 keysecurid;
1010d2c294fSGerasim Troeglazov
1020d2c294fSGerasim Troeglazov	/* did not find official description for the following */
1030d2c294fSGerasim Troeglazov	le32 hash;
1040d2c294fSGerasim Troeglazov	le32 securid;
1050d2c294fSGerasim Troeglazov	le32 dataoffsl;	/* documented as badly aligned */
1060d2c294fSGerasim Troeglazov	le32 dataoffsh;
1070d2c294fSGerasim Troeglazov	le32 datasize;
1080d2c294fSGerasim Troeglazov} ;
1090d2c294fSGerasim Troeglazov
1100d2c294fSGerasim Troeglazovstruct SDH {		/* this is an image of an $SDH index entry */
1110d2c294fSGerasim Troeglazov	le16 offs;
1120d2c294fSGerasim Troeglazov	le16 size;
1130d2c294fSGerasim Troeglazov	le32 fill1;
1140d2c294fSGerasim Troeglazov	le16 indexsz;
1150d2c294fSGerasim Troeglazov	le16 indexksz;
1160d2c294fSGerasim Troeglazov	le16 flags;
1170d2c294fSGerasim Troeglazov	le16 fill2;
1180d2c294fSGerasim Troeglazov	le32 keyhash;
1190d2c294fSGerasim Troeglazov	le32 keysecurid;
1200d2c294fSGerasim Troeglazov
1210d2c294fSGerasim Troeglazov	/* did not find official description for the following */
1220d2c294fSGerasim Troeglazov	le32 hash;
1230d2c294fSGerasim Troeglazov	le32 securid;
1240d2c294fSGerasim Troeglazov	le32 dataoffsl;
1250d2c294fSGerasim Troeglazov	le32 dataoffsh;
1260d2c294fSGerasim Troeglazov	le32 datasize;
1270d2c294fSGerasim Troeglazov	le32 fill3;
1280d2c294fSGerasim Troeglazov	} ;
1290d2c294fSGerasim Troeglazov
1300d2c294fSGerasim Troeglazov/*
1310d2c294fSGerasim Troeglazov *	A few useful constants
1320d2c294fSGerasim Troeglazov */
1330d2c294fSGerasim Troeglazov
1340d2c294fSGerasim Troeglazovstatic ntfschar sii_stream[] = { const_cpu_to_le16('$'),
1350d2c294fSGerasim Troeglazov				 const_cpu_to_le16('S'),
1360490778eSAugustin Cavalier				 const_cpu_to_le16('I'),
1370490778eSAugustin Cavalier				 const_cpu_to_le16('I'),
1380d2c294fSGerasim Troeglazov				 const_cpu_to_le16(0) };
1390d2c294fSGerasim Troeglazovstatic ntfschar sdh_stream[] = { const_cpu_to_le16('$'),
1400d2c294fSGerasim Troeglazov				 const_cpu_to_le16('S'),
1410d2c294fSGerasim Troeglazov				 const_cpu_to_le16('D'),
1420d2c294fSGerasim Troeglazov				 const_cpu_to_le16('H'),
1430d2c294fSGerasim Troeglazov				 const_cpu_to_le16(0) };
1440d2c294fSGerasim Troeglazov
1450d2c294fSGerasim Troeglazov/*
1460d2c294fSGerasim Troeglazov *		null SID (S-1-0-0)
1470d2c294fSGerasim Troeglazov */
1480d2c294fSGerasim Troeglazov
1490d2c294fSGerasim Troeglazovextern const SID *nullsid;
15080bca3d3SAxel Dörfler
15180bca3d3SAxel Dörfler/*
15280bca3d3SAxel Dörfler * The zero GUID.
15380bca3d3SAxel Dörfler */
1540d2c294fSGerasim Troeglazov
15580bca3d3SAxel Dörflerstatic const GUID __zero_guid = { const_cpu_to_le32(0), const_cpu_to_le16(0),
15680bca3d3SAxel Dörfler		const_cpu_to_le16(0), { 0, 0, 0, 0, 0, 0, 0, 0 } };
1570d2c294fSGerasim Troeglazovstatic const GUID *const zero_guid = &__zero_guid;
15880bca3d3SAxel Dörfler
15980bca3d3SAxel Dörfler/**
16080bca3d3SAxel Dörfler * ntfs_guid_is_zero - check if a GUID is zero
16180bca3d3SAxel Dörfler * @guid:	[IN] guid to check
16280bca3d3SAxel Dörfler *
16380bca3d3SAxel Dörfler * Return TRUE if @guid is a valid pointer to a GUID and it is the zero GUID
16480bca3d3SAxel Dörfler * and FALSE otherwise.
16580bca3d3SAxel Dörfler */
16680bca3d3SAxel DörflerBOOL ntfs_guid_is_zero(const GUID *guid)
16780bca3d3SAxel Dörfler{
16880bca3d3SAxel Dörfler	return (memcmp(guid, zero_guid, sizeof(*zero_guid)));
16980bca3d3SAxel Dörfler}
17080bca3d3SAxel Dörfler
17180bca3d3SAxel Dörfler/**
17280bca3d3SAxel Dörfler * ntfs_guid_to_mbs - convert a GUID to a multi byte string
17380bca3d3SAxel Dörfler * @guid:	[IN]  guid to convert
17480bca3d3SAxel Dörfler * @guid_str:	[OUT] string in which to return the GUID (optional)
17580bca3d3SAxel Dörfler *
17680bca3d3SAxel Dörfler * Convert the GUID pointed to by @guid to a multi byte string of the form
17780bca3d3SAxel Dörfler * "XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX".  Therefore, @guid_str (if not NULL)
17880bca3d3SAxel Dörfler * needs to be able to store at least 37 bytes.
17980bca3d3SAxel Dörfler *
18080bca3d3SAxel Dörfler * If @guid_str is not NULL it will contain the converted GUID on return.  If
18180bca3d3SAxel Dörfler * it is NULL a string will be allocated and this will be returned.  The caller
18280bca3d3SAxel Dörfler * is responsible for free()ing the string in that case.
18380bca3d3SAxel Dörfler *
18480bca3d3SAxel Dörfler * On success return the converted string and on failure return NULL with errno
18580bca3d3SAxel Dörfler * set to the error code.
18680bca3d3SAxel Dörfler */
18780bca3d3SAxel Dörflerchar *ntfs_guid_to_mbs(const GUID *guid, char *guid_str)
18880bca3d3SAxel Dörfler{
18980bca3d3SAxel Dörfler	char *_guid_str;
19080bca3d3SAxel Dörfler	int res;
19180bca3d3SAxel Dörfler
19280bca3d3SAxel Dörfler	if (!guid) {
19380bca3d3SAxel Dörfler		errno = EINVAL;
19480bca3d3SAxel Dörfler		return NULL;
19580bca3d3SAxel Dörfler	}
19680bca3d3SAxel Dörfler	_guid_str = guid_str;
19780bca3d3SAxel Dörfler	if (!_guid_str) {
1980d2c294fSGerasim Troeglazov		_guid_str = (char*)ntfs_malloc(37);
19980bca3d3SAxel Dörfler		if (!_guid_str)
20080bca3d3SAxel Dörfler			return _guid_str;
20180bca3d3SAxel Dörfler	}
20280bca3d3SAxel Dörfler	res = snprintf(_guid_str, 37,
20380bca3d3SAxel Dörfler			"%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x",
20480bca3d3SAxel Dörfler			(unsigned int)le32_to_cpu(guid->data1),
20580bca3d3SAxel Dörfler			le16_to_cpu(guid->data2), le16_to_cpu(guid->data3),
20680bca3d3SAxel Dörfler			guid->data4[0], guid->data4[1],
20780bca3d3SAxel Dörfler			guid->data4[2], guid->data4[3], guid->data4[4],
20880bca3d3SAxel Dörfler			guid->data4[5], guid->data4[6], guid->data4[7]);
20980bca3d3SAxel Dörfler	if (res == 36)
21080bca3d3SAxel Dörfler		return _guid_str;
21180bca3d3SAxel Dörfler	if (!guid_str)
21280bca3d3SAxel Dörfler		free(_guid_str);
21380bca3d3SAxel Dörfler	errno = EINVAL;
21480bca3d3SAxel Dörfler	return NULL;
21580bca3d3SAxel Dörfler}
21680bca3d3SAxel Dörfler
21780bca3d3SAxel Dörfler/**
21880bca3d3SAxel Dörfler * ntfs_sid_to_mbs_size - determine maximum size for the string of a SID
21980bca3d3SAxel Dörfler * @sid:	[IN]  SID for which to determine the maximum string size
22080bca3d3SAxel Dörfler *
22180bca3d3SAxel Dörfler * Determine the maximum multi byte string size in bytes which is needed to
22280bca3d3SAxel Dörfler * store the standard textual representation of the SID pointed to by @sid.
22380bca3d3SAxel Dörfler * See ntfs_sid_to_mbs(), below.
22480bca3d3SAxel Dörfler *
22580bca3d3SAxel Dörfler * On success return the maximum number of bytes needed to store the multi byte
22680bca3d3SAxel Dörfler * string and on failure return -1 with errno set to the error code.
22780bca3d3SAxel Dörfler */
22880bca3d3SAxel Dörflerint ntfs_sid_to_mbs_size(const SID *sid)
22980bca3d3SAxel Dörfler{
23080bca3d3SAxel Dörfler	int size, i;
23180bca3d3SAxel Dörfler
2320490778eSAugustin Cavalier	if (!ntfs_valid_sid(sid)) {
23380bca3d3SAxel Dörfler		errno = EINVAL;
23480bca3d3SAxel Dörfler		return -1;
23580bca3d3SAxel Dörfler	}
23680bca3d3SAxel Dörfler	/* Start with "S-". */
23780bca3d3SAxel Dörfler	size = 2;
23880bca3d3SAxel Dörfler	/*
23980bca3d3SAxel Dörfler	 * Add the SID_REVISION.  Hopefully the compiler will optimize this
24080bca3d3SAxel Dörfler	 * away as SID_REVISION is a constant.
24180bca3d3SAxel Dörfler	 */
24280bca3d3SAxel Dörfler	for (i = SID_REVISION; i > 0; i /= 10)
24380bca3d3SAxel Dörfler		size++;
24480bca3d3SAxel Dörfler	/* Add the "-". */
24580bca3d3SAxel Dörfler	size++;
24680bca3d3SAxel Dörfler	/*
24780bca3d3SAxel Dörfler	 * Add the identifier authority.  If it needs to be in decimal, the
24880bca3d3SAxel Dörfler	 * maximum is 2^32-1 = 4294967295 = 10 characters.  If it needs to be
24980bca3d3SAxel Dörfler	 * in hexadecimal, then maximum is 0x665544332211 = 14 characters.
25080bca3d3SAxel Dörfler	 */
25180bca3d3SAxel Dörfler	if (!sid->identifier_authority.high_part)
25280bca3d3SAxel Dörfler		size += 10;
25380bca3d3SAxel Dörfler	else
25480bca3d3SAxel Dörfler		size += 14;
25580bca3d3SAxel Dörfler	/*
25680bca3d3SAxel Dörfler	 * Finally, add the sub authorities.  For each we have a "-" followed
25780bca3d3SAxel Dörfler	 * by a decimal which can be up to 2^32-1 = 4294967295 = 10 characters.
25880bca3d3SAxel Dörfler	 */
25980bca3d3SAxel Dörfler	size += (1 + 10) * sid->sub_authority_count;
26080bca3d3SAxel Dörfler	/* We need the zero byte at the end, too. */
26180bca3d3SAxel Dörfler	size++;
26280bca3d3SAxel Dörfler	return size * sizeof(char);
26380bca3d3SAxel Dörfler}
26480bca3d3SAxel Dörfler
26580bca3d3SAxel Dörfler/**
26680bca3d3SAxel Dörfler * ntfs_sid_to_mbs - convert a SID to a multi byte string
26780bca3d3SAxel Dörfler * @sid:		[IN]  SID to convert
26880bca3d3SAxel Dörfler * @sid_str:		[OUT] string in which to return the SID (optional)
26980bca3d3SAxel Dörfler * @sid_str_size:	[IN]  size in bytes of @sid_str
27080bca3d3SAxel Dörfler *
27180bca3d3SAxel Dörfler * Convert the SID pointed to by @sid to its standard textual representation.
27280bca3d3SAxel Dörfler * @sid_str (if not NULL) needs to be able to store at least
27380bca3d3SAxel Dörfler * ntfs_sid_to_mbs_size() bytes.  @sid_str_size is the size in bytes of
27480bca3d3SAxel Dörfler * @sid_str if @sid_str is not NULL.
27580bca3d3SAxel Dörfler *
27680bca3d3SAxel Dörfler * The standard textual representation of the SID is of the form:
27780bca3d3SAxel Dörfler *	S-R-I-S-S...
27880bca3d3SAxel Dörfler * Where:
27980bca3d3SAxel Dörfler *    - The first "S" is the literal character 'S' identifying the following
28080bca3d3SAxel Dörfler *	digits as a SID.
28180bca3d3SAxel Dörfler *    - R is the revision level of the SID expressed as a sequence of digits
28280bca3d3SAxel Dörfler *	in decimal.
28380bca3d3SAxel Dörfler *    - I is the 48-bit identifier_authority, expressed as digits in decimal,
28480bca3d3SAxel Dörfler *	if I < 2^32, or hexadecimal prefixed by "0x", if I >= 2^32.
28580bca3d3SAxel Dörfler *    - S... is one or more sub_authority values, expressed as digits in
28680bca3d3SAxel Dörfler *	decimal.
28780bca3d3SAxel Dörfler *
28880bca3d3SAxel Dörfler * If @sid_str is not NULL it will contain the converted SUID on return.  If it
28980bca3d3SAxel Dörfler * is NULL a string will be allocated and this will be returned.  The caller is
29080bca3d3SAxel Dörfler * responsible for free()ing the string in that case.
29180bca3d3SAxel Dörfler *
29280bca3d3SAxel Dörfler * On success return the converted string and on failure return NULL with errno
29380bca3d3SAxel Dörfler * set to the error code.
29480bca3d3SAxel Dörfler */
29580bca3d3SAxel Dörflerchar *ntfs_sid_to_mbs(const SID *sid, char *sid_str, size_t sid_str_size)
29680bca3d3SAxel Dörfler{
29780bca3d3SAxel Dörfler	u64 u;
2980d2c294fSGerasim Troeglazov	le32 leauth;
29980bca3d3SAxel Dörfler	char *s;
30080bca3d3SAxel Dörfler	int i, j, cnt;
30180bca3d3SAxel Dörfler
30280bca3d3SAxel Dörfler	/*
30380bca3d3SAxel Dörfler	 * No need to check @sid if !@sid_str since ntfs_sid_to_mbs_size() will
30480bca3d3SAxel Dörfler	 * check @sid, too.  8 is the minimum SID string size.
30580bca3d3SAxel Dörfler	 */
3060490778eSAugustin Cavalier	if (sid_str && (sid_str_size < 8 || !ntfs_valid_sid(sid))) {
30780bca3d3SAxel Dörfler		errno = EINVAL;
30880bca3d3SAxel Dörfler		return NULL;
30980bca3d3SAxel Dörfler	}
31080bca3d3SAxel Dörfler	/* Allocate string if not provided. */
31180bca3d3SAxel Dörfler	if (!sid_str) {
31280bca3d3SAxel Dörfler		cnt = ntfs_sid_to_mbs_size(sid);
31380bca3d3SAxel Dörfler		if (cnt < 0)
31480bca3d3SAxel Dörfler			return NULL;
3150d2c294fSGerasim Troeglazov		s = (char*)ntfs_malloc(cnt);
31680bca3d3SAxel Dörfler		if (!s)
31780bca3d3SAxel Dörfler			return s;
31880bca3d3SAxel Dörfler		sid_str = s;
31980bca3d3SAxel Dörfler		/* So we know we allocated it. */
32080bca3d3SAxel Dörfler		sid_str_size = 0;
32180bca3d3SAxel Dörfler	} else {
32280bca3d3SAxel Dörfler		s = sid_str;
32380bca3d3SAxel Dörfler		cnt = sid_str_size;
32480bca3d3SAxel Dörfler	}
32580bca3d3SAxel Dörfler	/* Start with "S-R-". */
32680bca3d3SAxel Dörfler	i = snprintf(s, cnt, "S-%hhu-", (unsigned char)sid->revision);
32780bca3d3SAxel Dörfler	if (i < 0 || i >= cnt)
32880bca3d3SAxel Dörfler		goto err_out;
32980bca3d3SAxel Dörfler	s += i;
33080bca3d3SAxel Dörfler	cnt -= i;
33180bca3d3SAxel Dörfler	/* Add the identifier authority. */
33280bca3d3SAxel Dörfler	for (u = i = 0, j = 40; i < 6; i++, j -= 8)
33380bca3d3SAxel Dörfler		u += (u64)sid->identifier_authority.value[i] << j;
33480bca3d3SAxel Dörfler	if (!sid->identifier_authority.high_part)
33580bca3d3SAxel Dörfler		i = snprintf(s, cnt, "%lu", (unsigned long)u);
33680bca3d3SAxel Dörfler	else
33780bca3d3SAxel Dörfler		i = snprintf(s, cnt, "0x%llx", (unsigned long long)u);
33880bca3d3SAxel Dörfler	if (i < 0 || i >= cnt)
33980bca3d3SAxel Dörfler		goto err_out;
34080bca3d3SAxel Dörfler	s += i;
34180bca3d3SAxel Dörfler	cnt -= i;
34280bca3d3SAxel Dörfler	/* Finally, add the sub authorities. */
34380bca3d3SAxel Dörfler	for (j = 0; j < sid->sub_authority_count; j++) {
3440d2c294fSGerasim Troeglazov		leauth = sid->sub_authority[j];
34580bca3d3SAxel Dörfler		i = snprintf(s, cnt, "-%u", (unsigned int)
3460d2c294fSGerasim Troeglazov				le32_to_cpu(leauth));
34780bca3d3SAxel Dörfler		if (i < 0 || i >= cnt)
34880bca3d3SAxel Dörfler			goto err_out;
34980bca3d3SAxel Dörfler		s += i;
35080bca3d3SAxel Dörfler		cnt -= i;
35180bca3d3SAxel Dörfler	}
35280bca3d3SAxel Dörfler	return sid_str;
35380bca3d3SAxel Dörflererr_out:
35480bca3d3SAxel Dörfler	if (i >= cnt)
35580bca3d3SAxel Dörfler		i = EMSGSIZE;
35680bca3d3SAxel Dörfler	else
35780bca3d3SAxel Dörfler		i = errno;
35880bca3d3SAxel Dörfler	if (!sid_str_size)
35980bca3d3SAxel Dörfler		free(sid_str);
36080bca3d3SAxel Dörfler	errno = i;
36180bca3d3SAxel Dörfler	return NULL;
36280bca3d3SAxel Dörfler}
36380bca3d3SAxel Dörfler
36480bca3d3SAxel Dörfler/**
36580bca3d3SAxel Dörfler * ntfs_generate_guid - generatates a random current guid.
36680bca3d3SAxel Dörfler * @guid:	[OUT]   pointer to a GUID struct to hold the generated guid.
36780bca3d3SAxel Dörfler *
36880bca3d3SAxel Dörfler * perhaps not a very good random number generator though...
36980bca3d3SAxel Dörfler */
37080bca3d3SAxel Dörflervoid ntfs_generate_guid(GUID *guid)
37180bca3d3SAxel Dörfler{
37280bca3d3SAxel Dörfler	unsigned int i;
37380bca3d3SAxel Dörfler	u8 *p = (u8 *)guid;
37480bca3d3SAxel Dörfler
375d68289f7SGerasim Troeglazov	/* this is called at most once from mkntfs */
376d68289f7SGerasim Troeglazov	srandom(time((time_t*)NULL) ^ (getpid() << 16));
37780bca3d3SAxel Dörfler	for (i = 0; i < sizeof(GUID); i++) {
37880bca3d3SAxel Dörfler		p[i] = (u8)(random() & 0xFF);
37980bca3d3SAxel Dörfler		if (i == 7)
38080bca3d3SAxel Dörfler			p[7] = (p[7] & 0x0F) | 0x40;
38180bca3d3SAxel Dörfler		if (i == 8)
38280bca3d3SAxel Dörfler			p[8] = (p[8] & 0x3F) | 0x80;
38380bca3d3SAxel Dörfler	}
38480bca3d3SAxel Dörfler}
38580bca3d3SAxel Dörfler
38630bc84e9SGerasim Troeglazov/**
38730bc84e9SGerasim Troeglazov * ntfs_security_hash - calculate the hash of a security descriptor
38830bc84e9SGerasim Troeglazov * @sd:         self-relative security descriptor whose hash to calculate
38930bc84e9SGerasim Troeglazov * @length:     size in bytes of the security descritor @sd
39030bc84e9SGerasim Troeglazov *
39130bc84e9SGerasim Troeglazov * Calculate the hash of the self-relative security descriptor @sd of length
39230bc84e9SGerasim Troeglazov * @length bytes.
39330bc84e9SGerasim Troeglazov *
39430bc84e9SGerasim Troeglazov * This hash is used in the $Secure system file as the primary key for the $SDH
39530bc84e9SGerasim Troeglazov * index and is also stored in the header of each security descriptor in the
39630bc84e9SGerasim Troeglazov * $SDS data stream as well as in the index data of both the $SII and $SDH
39730bc84e9SGerasim Troeglazov * indexes.  In all three cases it forms part of the SDS_ENTRY_HEADER
39830bc84e9SGerasim Troeglazov * structure.
39930bc84e9SGerasim Troeglazov *
40030bc84e9SGerasim Troeglazov * Return the calculated security hash in little endian.
40130bc84e9SGerasim Troeglazov */
40230bc84e9SGerasim Troeglazovle32 ntfs_security_hash(const SECURITY_DESCRIPTOR_RELATIVE *sd, const u32 len)
40330bc84e9SGerasim Troeglazov{
4040d2c294fSGerasim Troeglazov	const le32 *pos = (const le32*)sd;
4050d2c294fSGerasim Troeglazov	const le32 *end = pos + (len >> 2);
4060d2c294fSGerasim Troeglazov	u32 hash = 0;
40730bc84e9SGerasim Troeglazov
4080d2c294fSGerasim Troeglazov	while (pos < end) {
4090d2c294fSGerasim Troeglazov		hash = le32_to_cpup(pos) + ntfs_rol32(hash, 3);
41030bc84e9SGerasim Troeglazov		pos++;
41130bc84e9SGerasim Troeglazov	}
4120d2c294fSGerasim Troeglazov	return cpu_to_le32(hash);
4130d2c294fSGerasim Troeglazov}
4140d2c294fSGerasim Troeglazov
4150d2c294fSGerasim Troeglazov/*
4160d2c294fSGerasim Troeglazov *	Get the first entry of current index block
4170d2c294fSGerasim Troeglazov *	cut and pasted form ntfs_ie_get_first() in index.c
4180d2c294fSGerasim Troeglazov */
4190d2c294fSGerasim Troeglazov
4200d2c294fSGerasim Troeglazovstatic INDEX_ENTRY *ntfs_ie_get_first(INDEX_HEADER *ih)
4210d2c294fSGerasim Troeglazov{
4220d2c294fSGerasim Troeglazov	return (INDEX_ENTRY*)((u8*)ih + le32_to_cpu(ih->entries_offset));
4230d2c294fSGerasim Troeglazov}
4240d2c294fSGerasim Troeglazov
4250d2c294fSGerasim Troeglazov/*
4260d2c294fSGerasim Troeglazov *		Stuff a 256KB block into $SDS before writing descriptors
4270d2c294fSGerasim Troeglazov *	into the block.
4280d2c294fSGerasim Troeglazov *
4290d2c294fSGerasim Troeglazov *	This prevents $SDS from being automatically declared as sparse
4300d2c294fSGerasim Troeglazov *	when the second copy of the first security descriptor is written
4310d2c294fSGerasim Troeglazov *	256KB further ahead.
4320d2c294fSGerasim Troeglazov *
4330d2c294fSGerasim Troeglazov *	Having $SDS declared as a sparse file is not wrong by itself
4340d2c294fSGerasim Troeglazov *	and chkdsk leaves it as a sparse file. It does however complain
4350d2c294fSGerasim Troeglazov *	and add a sparse flag (0x0200) into field file_attributes of
4360d2c294fSGerasim Troeglazov *	STANDARD_INFORMATION of $Secure. This probably means that a
4370d2c294fSGerasim Troeglazov *	sparse attribute (ATTR_IS_SPARSE) is only allowed in sparse
4380d2c294fSGerasim Troeglazov *	files (FILE_ATTR_SPARSE_FILE).
4390d2c294fSGerasim Troeglazov *
4400d2c294fSGerasim Troeglazov *	Windows normally does not convert to sparse attribute or sparse
4410d2c294fSGerasim Troeglazov *	file. Stuffing is just a way to get to the same result.
4420d2c294fSGerasim Troeglazov */
4430d2c294fSGerasim Troeglazov
4440d2c294fSGerasim Troeglazovstatic int entersecurity_stuff(ntfs_volume *vol, off_t offs)
4450d2c294fSGerasim Troeglazov{
4460d2c294fSGerasim Troeglazov	int res;
4470d2c294fSGerasim Troeglazov	int written;
4480d2c294fSGerasim Troeglazov	unsigned long total;
4490d2c294fSGerasim Troeglazov	char *stuff;
4500d2c294fSGerasim Troeglazov
4510d2c294fSGerasim Troeglazov	res = 0;
4520d2c294fSGerasim Troeglazov	total = 0;
4530d2c294fSGerasim Troeglazov	stuff = (char*)ntfs_malloc(STUFFSZ);
4540d2c294fSGerasim Troeglazov	if (stuff) {
4550d2c294fSGerasim Troeglazov		memset(stuff, 0, STUFFSZ);
4560d2c294fSGerasim Troeglazov		do {
457a814d850Sthreedeyes			written = ntfs_attr_data_write(vol->secure_ni,
4580d2c294fSGerasim Troeglazov				STREAM_SDS, 4, stuff, STUFFSZ, offs);
4590d2c294fSGerasim Troeglazov			if (written == STUFFSZ) {
4600d2c294fSGerasim Troeglazov				total += STUFFSZ;
4610d2c294fSGerasim Troeglazov				offs += STUFFSZ;
4620d2c294fSGerasim Troeglazov			} else {
4630d2c294fSGerasim Troeglazov				errno = ENOSPC;
4640d2c294fSGerasim Troeglazov				res = -1;
4650d2c294fSGerasim Troeglazov			}
4660d2c294fSGerasim Troeglazov		} while (!res && (total < ALIGN_SDS_BLOCK));
4670d2c294fSGerasim Troeglazov		free(stuff);
4680d2c294fSGerasim Troeglazov	} else {
4690d2c294fSGerasim Troeglazov		errno = ENOMEM;
4700d2c294fSGerasim Troeglazov		res = -1;
4710d2c294fSGerasim Troeglazov	}
4720d2c294fSGerasim Troeglazov	return (res);
4730d2c294fSGerasim Troeglazov}
4740d2c294fSGerasim Troeglazov
4750d2c294fSGerasim Troeglazov/*
4760d2c294fSGerasim Troeglazov *		Enter a new security descriptor into $Secure (data only)
4770d2c294fSGerasim Troeglazov *      it has to be written twice with an offset of 256KB
4780d2c294fSGerasim Troeglazov *
4790d2c294fSGerasim Troeglazov *	Should only be called by entersecurityattr() to ensure consistency
4800d2c294fSGerasim Troeglazov *
4810d2c294fSGerasim Troeglazov *	Returns zero if sucessful
4820d2c294fSGerasim Troeglazov */
4830d2c294fSGerasim Troeglazov
4840d2c294fSGerasim Troeglazovstatic int entersecurity_data(ntfs_volume *vol,
4850d2c294fSGerasim Troeglazov			const SECURITY_DESCRIPTOR_RELATIVE *attr, s64 attrsz,
4860d2c294fSGerasim Troeglazov			le32 hash, le32 keyid, off_t offs, int gap)
4870d2c294fSGerasim Troeglazov{
4880d2c294fSGerasim Troeglazov	int res;
4890d2c294fSGerasim Troeglazov	int written1;
4900d2c294fSGerasim Troeglazov	int written2;
4910d2c294fSGerasim Troeglazov	char *fullattr;
4920d2c294fSGerasim Troeglazov	int fullsz;
4930d2c294fSGerasim Troeglazov	SECURITY_DESCRIPTOR_HEADER *phsds;
4940d2c294fSGerasim Troeglazov
4950d2c294fSGerasim Troeglazov	res = -1;
4960d2c294fSGerasim Troeglazov	fullsz = attrsz + gap + sizeof(SECURITY_DESCRIPTOR_HEADER);
4970d2c294fSGerasim Troeglazov	fullattr = (char*)ntfs_malloc(fullsz);
4980d2c294fSGerasim Troeglazov	if (fullattr) {
4990d2c294fSGerasim Troeglazov			/*
5000d2c294fSGerasim Troeglazov			 * Clear the gap from previous descriptor
5010d2c294fSGerasim Troeglazov			 * this could be useful for appending the second
5020d2c294fSGerasim Troeglazov			 * copy to the end of file. When creating a new
5030d2c294fSGerasim Troeglazov			 * 256K block, the gap is cleared while writing
5040d2c294fSGerasim Troeglazov			 * the first copy
5050d2c294fSGerasim Troeglazov			 */
5060d2c294fSGerasim Troeglazov		if (gap)
5070d2c294fSGerasim Troeglazov			memset(fullattr,0,gap);
5080d2c294fSGerasim Troeglazov		memcpy(&fullattr[gap + sizeof(SECURITY_DESCRIPTOR_HEADER)],
5090d2c294fSGerasim Troeglazov				attr,attrsz);
5100d2c294fSGerasim Troeglazov		phsds = (SECURITY_DESCRIPTOR_HEADER*)&fullattr[gap];
5110d2c294fSGerasim Troeglazov		phsds->hash = hash;
5120d2c294fSGerasim Troeglazov		phsds->security_id = keyid;
5130d2c294fSGerasim Troeglazov		phsds->offset = cpu_to_le64(offs);
5140d2c294fSGerasim Troeglazov		phsds->length = cpu_to_le32(fullsz - gap);
515a814d850Sthreedeyes		written1 = ntfs_attr_data_write(vol->secure_ni,
5160d2c294fSGerasim Troeglazov			STREAM_SDS, 4, fullattr, fullsz,
5170d2c294fSGerasim Troeglazov			offs - gap);
518a814d850Sthreedeyes		written2 = ntfs_attr_data_write(vol->secure_ni,
5190d2c294fSGerasim Troeglazov			STREAM_SDS, 4, fullattr, fullsz,
5200d2c294fSGerasim Troeglazov			offs - gap + ALIGN_SDS_BLOCK);
5210d2c294fSGerasim Troeglazov		if ((written1 == fullsz)
5220490778eSAugustin Cavalier		     && (written2 == written1)) {
5230490778eSAugustin Cavalier			/*
5240490778eSAugustin Cavalier			 * Make sure the data size for $SDS marks the end
5250490778eSAugustin Cavalier			 * of the last security attribute. Windows uses
5260490778eSAugustin Cavalier			 * this to determine where the next attribute will
5270490778eSAugustin Cavalier			 * be written, which causes issues if chkdsk had
5280490778eSAugustin Cavalier			 * previously deleted the last entries without
5290490778eSAugustin Cavalier			 * adjusting the size.
5300490778eSAugustin Cavalier			 */
5310490778eSAugustin Cavalier			res = ntfs_attr_shrink_size(vol->secure_ni,STREAM_SDS,
5320490778eSAugustin Cavalier				4, offs - gap + ALIGN_SDS_BLOCK + fullsz);
5330490778eSAugustin Cavalier		} else
5340d2c294fSGerasim Troeglazov			errno = ENOSPC;
5350d2c294fSGerasim Troeglazov		free(fullattr);
5360d2c294fSGerasim Troeglazov	} else
5370d2c294fSGerasim Troeglazov		errno = ENOMEM;
5380d2c294fSGerasim Troeglazov	return (res);
5390d2c294fSGerasim Troeglazov}
5400d2c294fSGerasim Troeglazov
5410d2c294fSGerasim Troeglazov/*
5420d2c294fSGerasim Troeglazov *	Enter a new security descriptor in $Secure (indexes only)
5430d2c294fSGerasim Troeglazov *
5440d2c294fSGerasim Troeglazov *	Should only be called by entersecurityattr() to ensure consistency
5450d2c294fSGerasim Troeglazov *
5460d2c294fSGerasim Troeglazov *	Returns zero if sucessful
5470d2c294fSGerasim Troeglazov */
5480d2c294fSGerasim Troeglazov
5490d2c294fSGerasim Troeglazovstatic int entersecurity_indexes(ntfs_volume *vol, s64 attrsz,
5500d2c294fSGerasim Troeglazov			le32 hash, le32 keyid, off_t offs)
5510d2c294fSGerasim Troeglazov{
5520d2c294fSGerasim Troeglazov	union {
5530d2c294fSGerasim Troeglazov		struct {
5540d2c294fSGerasim Troeglazov			le32 dataoffsl;
5550d2c294fSGerasim Troeglazov			le32 dataoffsh;
5560d2c294fSGerasim Troeglazov		} parts;
5570d2c294fSGerasim Troeglazov		le64 all;
5580d2c294fSGerasim Troeglazov	} realign;
5590d2c294fSGerasim Troeglazov	int res;
5600d2c294fSGerasim Troeglazov	ntfs_index_context *xsii;
5610d2c294fSGerasim Troeglazov	ntfs_index_context *xsdh;
5620d2c294fSGerasim Troeglazov	struct SII newsii;
5630d2c294fSGerasim Troeglazov	struct SDH newsdh;
5640d2c294fSGerasim Troeglazov
5650d2c294fSGerasim Troeglazov	res = -1;
5660d2c294fSGerasim Troeglazov				/* enter a new $SII record */
5670d2c294fSGerasim Troeglazov
5680d2c294fSGerasim Troeglazov	xsii = vol->secure_xsii;
5690d2c294fSGerasim Troeglazov	ntfs_index_ctx_reinit(xsii);
5700d2c294fSGerasim Troeglazov	newsii.offs = const_cpu_to_le16(20);
5710d2c294fSGerasim Troeglazov	newsii.size = const_cpu_to_le16(sizeof(struct SII) - 20);
5720d2c294fSGerasim Troeglazov	newsii.fill1 = const_cpu_to_le32(0);
5730d2c294fSGerasim Troeglazov	newsii.indexsz = const_cpu_to_le16(sizeof(struct SII));
5740d2c294fSGerasim Troeglazov	newsii.indexksz = const_cpu_to_le16(sizeof(SII_INDEX_KEY));
5750d2c294fSGerasim Troeglazov	newsii.flags = const_cpu_to_le16(0);
5760d2c294fSGerasim Troeglazov	newsii.fill2 = const_cpu_to_le16(0);
5770d2c294fSGerasim Troeglazov	newsii.keysecurid = keyid;
5780d2c294fSGerasim Troeglazov	newsii.hash = hash;
5790d2c294fSGerasim Troeglazov	newsii.securid = keyid;
5800d2c294fSGerasim Troeglazov	realign.all = cpu_to_le64(offs);
5810d2c294fSGerasim Troeglazov	newsii.dataoffsh = realign.parts.dataoffsh;
5820d2c294fSGerasim Troeglazov	newsii.dataoffsl = realign.parts.dataoffsl;
5830d2c294fSGerasim Troeglazov	newsii.datasize = cpu_to_le32(attrsz
5840d2c294fSGerasim Troeglazov			 + sizeof(SECURITY_DESCRIPTOR_HEADER));
5850d2c294fSGerasim Troeglazov	if (!ntfs_ie_add(xsii,(INDEX_ENTRY*)&newsii)) {
5860d2c294fSGerasim Troeglazov
5870d2c294fSGerasim Troeglazov		/* enter a new $SDH record */
5880d2c294fSGerasim Troeglazov
5890d2c294fSGerasim Troeglazov		xsdh = vol->secure_xsdh;
5900d2c294fSGerasim Troeglazov		ntfs_index_ctx_reinit(xsdh);
5910d2c294fSGerasim Troeglazov		newsdh.offs = const_cpu_to_le16(24);
5920d2c294fSGerasim Troeglazov		newsdh.size = const_cpu_to_le16(
5930d2c294fSGerasim Troeglazov			sizeof(SECURITY_DESCRIPTOR_HEADER));
5940d2c294fSGerasim Troeglazov		newsdh.fill1 = const_cpu_to_le32(0);
5950d2c294fSGerasim Troeglazov		newsdh.indexsz = const_cpu_to_le16(
5960d2c294fSGerasim Troeglazov				sizeof(struct SDH));
5970d2c294fSGerasim Troeglazov		newsdh.indexksz = const_cpu_to_le16(
5980d2c294fSGerasim Troeglazov				sizeof(SDH_INDEX_KEY));
5990d2c294fSGerasim Troeglazov		newsdh.flags = const_cpu_to_le16(0);
6000d2c294fSGerasim Troeglazov		newsdh.fill2 = const_cpu_to_le16(0);
6010d2c294fSGerasim Troeglazov		newsdh.keyhash = hash;
6020d2c294fSGerasim Troeglazov		newsdh.keysecurid = keyid;
6030d2c294fSGerasim Troeglazov		newsdh.hash = hash;
6040d2c294fSGerasim Troeglazov		newsdh.securid = keyid;
6050d2c294fSGerasim Troeglazov		newsdh.dataoffsh = realign.parts.dataoffsh;
6060d2c294fSGerasim Troeglazov		newsdh.dataoffsl = realign.parts.dataoffsl;
6070d2c294fSGerasim Troeglazov		newsdh.datasize = cpu_to_le32(attrsz
6080d2c294fSGerasim Troeglazov			 + sizeof(SECURITY_DESCRIPTOR_HEADER));
6090d2c294fSGerasim Troeglazov                           /* special filler value, Windows generally */
6100d2c294fSGerasim Troeglazov                           /* fills with 0x00490049, sometimes with zero */
6110d2c294fSGerasim Troeglazov		newsdh.fill3 = const_cpu_to_le32(0x00490049);
6120d2c294fSGerasim Troeglazov		if (!ntfs_ie_add(xsdh,(INDEX_ENTRY*)&newsdh))
6130d2c294fSGerasim Troeglazov			res = 0;
6140d2c294fSGerasim Troeglazov	}
6150d2c294fSGerasim Troeglazov	return (res);
6160d2c294fSGerasim Troeglazov}
6170d2c294fSGerasim Troeglazov
6180d2c294fSGerasim Troeglazov/*
6190d2c294fSGerasim Troeglazov *	Enter a new security descriptor in $Secure (data and indexes)
6200d2c294fSGerasim Troeglazov *	Returns id of entry, or zero if there is a problem.
6210d2c294fSGerasim Troeglazov *	(should not be called for NTFS version < 3.0)
6220d2c294fSGerasim Troeglazov *
6230d2c294fSGerasim Troeglazov *	important : calls have to be serialized, however no locking is
6240d2c294fSGerasim Troeglazov *	needed while fuse is not multithreaded
6250d2c294fSGerasim Troeglazov */
6260d2c294fSGerasim Troeglazov
6270d2c294fSGerasim Troeglazovstatic le32 entersecurityattr(ntfs_volume *vol,
6280d2c294fSGerasim Troeglazov			const SECURITY_DESCRIPTOR_RELATIVE *attr, s64 attrsz,
6290d2c294fSGerasim Troeglazov			le32 hash)
6300d2c294fSGerasim Troeglazov{
6310d2c294fSGerasim Troeglazov	union {
6320d2c294fSGerasim Troeglazov		struct {
6330d2c294fSGerasim Troeglazov			le32 dataoffsl;
6340d2c294fSGerasim Troeglazov			le32 dataoffsh;
6350d2c294fSGerasim Troeglazov		} parts;
6360d2c294fSGerasim Troeglazov		le64 all;
6370d2c294fSGerasim Troeglazov	} realign;
6380d2c294fSGerasim Troeglazov	le32 securid;
6390d2c294fSGerasim Troeglazov	le32 keyid;
6400d2c294fSGerasim Troeglazov	u32 newkey;
6410d2c294fSGerasim Troeglazov	off_t offs;
6420d2c294fSGerasim Troeglazov	int gap;
6430d2c294fSGerasim Troeglazov	int size;
6440d2c294fSGerasim Troeglazov	BOOL found;
6450d2c294fSGerasim Troeglazov	struct SII *psii;
6460d2c294fSGerasim Troeglazov	INDEX_ENTRY *entry;
6470d2c294fSGerasim Troeglazov	INDEX_ENTRY *next;
6480d2c294fSGerasim Troeglazov	ntfs_index_context *xsii;
6490d2c294fSGerasim Troeglazov	int retries;
6500d2c294fSGerasim Troeglazov	ntfs_attr *na;
6510d2c294fSGerasim Troeglazov	int olderrno;
6520d2c294fSGerasim Troeglazov
6530d2c294fSGerasim Troeglazov	/* find the first available securid beyond the last key */
6540d2c294fSGerasim Troeglazov	/* in $Secure:$SII. This also determines the first */
6550d2c294fSGerasim Troeglazov	/* available location in $Secure:$SDS, as this stream */
6560d2c294fSGerasim Troeglazov	/* is always appended to and the id's are allocated */
6570d2c294fSGerasim Troeglazov	/* in sequence */
6580d2c294fSGerasim Troeglazov
6590d2c294fSGerasim Troeglazov	securid = const_cpu_to_le32(0);
6600d2c294fSGerasim Troeglazov	xsii = vol->secure_xsii;
6610d2c294fSGerasim Troeglazov	ntfs_index_ctx_reinit(xsii);
6620d2c294fSGerasim Troeglazov	offs = size = 0;
6630d2c294fSGerasim Troeglazov	keyid = const_cpu_to_le32(-1);
6640d2c294fSGerasim Troeglazov	olderrno = errno;
6650d2c294fSGerasim Troeglazov	found = !ntfs_index_lookup((char*)&keyid,
6660d2c294fSGerasim Troeglazov			       sizeof(SII_INDEX_KEY), xsii);
6670d2c294fSGerasim Troeglazov	if (!found && (errno != ENOENT)) {
6680d2c294fSGerasim Troeglazov		ntfs_log_perror("Inconsistency in index $SII");
6690d2c294fSGerasim Troeglazov		psii = (struct SII*)NULL;
6700d2c294fSGerasim Troeglazov	} else {
6710d2c294fSGerasim Troeglazov			/* restore errno to avoid misinterpretation */
6720d2c294fSGerasim Troeglazov		errno = olderrno;
6730d2c294fSGerasim Troeglazov		entry = xsii->entry;
6740d2c294fSGerasim Troeglazov		psii = (struct SII*)xsii->entry;
6750d2c294fSGerasim Troeglazov	}
6760d2c294fSGerasim Troeglazov	if (psii) {
6770d2c294fSGerasim Troeglazov		/*
6780d2c294fSGerasim Troeglazov		 * Get last entry in block, but must get first one
6790d2c294fSGerasim Troeglazov		 * one first, as we should already be beyond the
6800d2c294fSGerasim Troeglazov		 * last one. For some reason the search for the last
6810d2c294fSGerasim Troeglazov		 * entry sometimes does not return the last block...
6820d2c294fSGerasim Troeglazov		 * we assume this can only happen in root block
6830d2c294fSGerasim Troeglazov		 */
6840d2c294fSGerasim Troeglazov		if (xsii->is_in_root)
6850d2c294fSGerasim Troeglazov			entry = ntfs_ie_get_first
6860d2c294fSGerasim Troeglazov				((INDEX_HEADER*)&xsii->ir->index);
6870d2c294fSGerasim Troeglazov		else
6880d2c294fSGerasim Troeglazov			entry = ntfs_ie_get_first
6890d2c294fSGerasim Troeglazov				((INDEX_HEADER*)&xsii->ib->index);
6900d2c294fSGerasim Troeglazov		/*
6910d2c294fSGerasim Troeglazov		 * All index blocks should be at least half full
6920d2c294fSGerasim Troeglazov		 * so there always is a last entry but one,
6930d2c294fSGerasim Troeglazov		 * except when creating the first entry in index root.
6940d2c294fSGerasim Troeglazov		 * This was however found not to be true : chkdsk
6950d2c294fSGerasim Troeglazov		 * sometimes deletes all the (unused) keys in the last
6960d2c294fSGerasim Troeglazov		 * index block without rebalancing the tree.
6970d2c294fSGerasim Troeglazov		 * When this happens, a new search is restarted from
6980d2c294fSGerasim Troeglazov		 * the smallest key.
6990d2c294fSGerasim Troeglazov		 */
7000d2c294fSGerasim Troeglazov		keyid = const_cpu_to_le32(0);
7010d2c294fSGerasim Troeglazov		retries = 0;
7020d2c294fSGerasim Troeglazov		while (entry) {
7030d2c294fSGerasim Troeglazov			next = ntfs_index_next(entry,xsii);
7040490778eSAugustin Cavalier			if (next) {
7050d2c294fSGerasim Troeglazov				psii = (struct SII*)next;
7060d2c294fSGerasim Troeglazov					/* save last key and */
7070d2c294fSGerasim Troeglazov					/* available position */
7080d2c294fSGerasim Troeglazov				keyid = psii->keysecurid;
7090d2c294fSGerasim Troeglazov				realign.parts.dataoffsh
7100d2c294fSGerasim Troeglazov						 = psii->dataoffsh;
7110d2c294fSGerasim Troeglazov				realign.parts.dataoffsl
7120d2c294fSGerasim Troeglazov						 = psii->dataoffsl;
7130d2c294fSGerasim Troeglazov				offs = le64_to_cpu(realign.all);
7140d2c294fSGerasim Troeglazov				size = le32_to_cpu(psii->datasize);
7150d2c294fSGerasim Troeglazov			}
7160d2c294fSGerasim Troeglazov			entry = next;
7170d2c294fSGerasim Troeglazov			if (!entry && !keyid && !retries) {
7180d2c294fSGerasim Troeglazov				/* search failed, retry from smallest key */
7190d2c294fSGerasim Troeglazov				ntfs_index_ctx_reinit(xsii);
7200d2c294fSGerasim Troeglazov				found = !ntfs_index_lookup((char*)&keyid,
7210d2c294fSGerasim Troeglazov					       sizeof(SII_INDEX_KEY), xsii);
7220d2c294fSGerasim Troeglazov				if (!found && (errno != ENOENT)) {
7230d2c294fSGerasim Troeglazov					ntfs_log_perror("Index $SII is broken");
7240490778eSAugustin Cavalier					psii = (struct SII*)NULL;
7250d2c294fSGerasim Troeglazov				} else {
7260d2c294fSGerasim Troeglazov						/* restore errno */
7270d2c294fSGerasim Troeglazov					errno = olderrno;
7280d2c294fSGerasim Troeglazov					entry = xsii->entry;
7290490778eSAugustin Cavalier					psii = (struct SII*)entry;
7300490778eSAugustin Cavalier				}
7310490778eSAugustin Cavalier				if (psii
7320490778eSAugustin Cavalier				    && !(psii->flags & INDEX_ENTRY_END)) {
7330490778eSAugustin Cavalier						/* save first key and */
7340490778eSAugustin Cavalier						/* available position */
7350490778eSAugustin Cavalier					keyid = psii->keysecurid;
7360490778eSAugustin Cavalier					realign.parts.dataoffsh
7370490778eSAugustin Cavalier							 = psii->dataoffsh;
7380490778eSAugustin Cavalier					realign.parts.dataoffsl
7390490778eSAugustin Cavalier							 = psii->dataoffsl;
7400490778eSAugustin Cavalier					offs = le64_to_cpu(realign.all);
7410490778eSAugustin Cavalier					size = le32_to_cpu(psii->datasize);
7420d2c294fSGerasim Troeglazov				}
7430d2c294fSGerasim Troeglazov				retries++;
7440d2c294fSGerasim Troeglazov			}
7450d2c294fSGerasim Troeglazov		}
7460d2c294fSGerasim Troeglazov	}
7470d2c294fSGerasim Troeglazov	if (!keyid) {
7480d2c294fSGerasim Troeglazov		/*
7490d2c294fSGerasim Troeglazov		 * could not find any entry, before creating the first
7500d2c294fSGerasim Troeglazov		 * entry, make a double check by making sure size of $SII
7510d2c294fSGerasim Troeglazov		 * is less than needed for one entry
7520d2c294fSGerasim Troeglazov		 */
7530d2c294fSGerasim Troeglazov		securid = const_cpu_to_le32(0);
7540d2c294fSGerasim Troeglazov		na = ntfs_attr_open(vol->secure_ni,AT_INDEX_ROOT,sii_stream,4);
7550d2c294fSGerasim Troeglazov		if (na) {
7560490778eSAugustin Cavalier			if ((size_t)na->data_size < (sizeof(struct SII)
7570490778eSAugustin Cavalier					+ sizeof(INDEX_ENTRY_HEADER))) {
7580d2c294fSGerasim Troeglazov				ntfs_log_error("Creating the first security_id\n");
7590d2c294fSGerasim Troeglazov				securid = const_cpu_to_le32(FIRST_SECURITY_ID);
7600d2c294fSGerasim Troeglazov			}
7610d2c294fSGerasim Troeglazov			ntfs_attr_close(na);
7620d2c294fSGerasim Troeglazov		}
7630d2c294fSGerasim Troeglazov		if (!securid) {
7640d2c294fSGerasim Troeglazov			ntfs_log_error("Error creating a security_id\n");
7650d2c294fSGerasim Troeglazov			errno = EIO;
7660d2c294fSGerasim Troeglazov		}
7670d2c294fSGerasim Troeglazov	} else {
7680d2c294fSGerasim Troeglazov		newkey = le32_to_cpu(keyid) + 1;
7690d2c294fSGerasim Troeglazov		securid = cpu_to_le32(newkey);
7700d2c294fSGerasim Troeglazov	}
7710d2c294fSGerasim Troeglazov	/*
7720d2c294fSGerasim Troeglazov	 * The security attr has to be written twice 256KB
7730d2c294fSGerasim Troeglazov	 * apart. This implies that offsets like
7740d2c294fSGerasim Troeglazov	 * 0x40000*odd_integer must be left available for
7750d2c294fSGerasim Troeglazov	 * the second copy. So align to next block when
7760d2c294fSGerasim Troeglazov	 * the last byte overflows on a wrong block.
7770d2c294fSGerasim Troeglazov	 */
7780d2c294fSGerasim Troeglazov
7790d2c294fSGerasim Troeglazov	if (securid) {
7800d2c294fSGerasim Troeglazov		gap = (-size) & (ALIGN_SDS_ENTRY - 1);
7810d2c294fSGerasim Troeglazov		offs += gap + size;
7820d2c294fSGerasim Troeglazov		if ((offs + attrsz + sizeof(SECURITY_DESCRIPTOR_HEADER) - 1)
7830d2c294fSGerasim Troeglazov	 	   & ALIGN_SDS_BLOCK) {
7840d2c294fSGerasim Troeglazov			offs = ((offs + attrsz
7850d2c294fSGerasim Troeglazov				 + sizeof(SECURITY_DESCRIPTOR_HEADER) - 1)
7860d2c294fSGerasim Troeglazov			 	| (ALIGN_SDS_BLOCK - 1)) + 1;
7870d2c294fSGerasim Troeglazov		}
7880d2c294fSGerasim Troeglazov		if (!(offs & (ALIGN_SDS_BLOCK - 1)))
7890d2c294fSGerasim Troeglazov			entersecurity_stuff(vol, offs);
7900d2c294fSGerasim Troeglazov		/*
7910d2c294fSGerasim Troeglazov		 * now write the security attr to storage :
7920d2c294fSGerasim Troeglazov		 * first data, then SII, then SDH
7930d2c294fSGerasim Troeglazov		 * If failure occurs while writing SDS, data will never
7940d2c294fSGerasim Troeglazov		 *    be accessed through indexes, and will be overwritten
7950d2c294fSGerasim Troeglazov		 *    by the next allocated descriptor
7960d2c294fSGerasim Troeglazov		 * If failure occurs while writing SII, the id has not
7970d2c294fSGerasim Troeglazov		 *    recorded and will be reallocated later
7980d2c294fSGerasim Troeglazov		 * If failure occurs while writing SDH, the space allocated
7990d2c294fSGerasim Troeglazov		 *    in SDS or SII will not be reused, an inconsistency
8000d2c294fSGerasim Troeglazov		 *    will persist with no significant consequence
8010d2c294fSGerasim Troeglazov		 */
8020d2c294fSGerasim Troeglazov		if (entersecurity_data(vol, attr, attrsz, hash, securid, offs, gap)
8030d2c294fSGerasim Troeglazov		    || entersecurity_indexes(vol, attrsz, hash, securid, offs))
8040d2c294fSGerasim Troeglazov			securid = const_cpu_to_le32(0);
8050d2c294fSGerasim Troeglazov	}
8060d2c294fSGerasim Troeglazov		/* inode now is dirty, synchronize it all */
8070d2c294fSGerasim Troeglazov	ntfs_index_entry_mark_dirty(vol->secure_xsii);
8080d2c294fSGerasim Troeglazov	ntfs_index_ctx_reinit(vol->secure_xsii);
8090d2c294fSGerasim Troeglazov	ntfs_index_entry_mark_dirty(vol->secure_xsdh);
8100d2c294fSGerasim Troeglazov	ntfs_index_ctx_reinit(vol->secure_xsdh);
8110d2c294fSGerasim Troeglazov	NInoSetDirty(vol->secure_ni);
8120d2c294fSGerasim Troeglazov	if (ntfs_inode_sync(vol->secure_ni))
8130d2c294fSGerasim Troeglazov		ntfs_log_perror("Could not sync $Secure\n");
8140d2c294fSGerasim Troeglazov	return (securid);
8150d2c294fSGerasim Troeglazov}
8160d2c294fSGerasim Troeglazov
8170d2c294fSGerasim Troeglazov/*
8180d2c294fSGerasim Troeglazov *		Find a matching security descriptor in $Secure,
8190d2c294fSGerasim Troeglazov *	if none, allocate a new id and write the descriptor to storage
8200d2c294fSGerasim Troeglazov *	Returns id of entry, or zero if there is a problem.
8210d2c294fSGerasim Troeglazov *
8220d2c294fSGerasim Troeglazov *	important : calls have to be serialized, however no locking is
8230d2c294fSGerasim Troeglazov *	needed while fuse is not multithreaded
8240d2c294fSGerasim Troeglazov */
8250d2c294fSGerasim Troeglazov
8260d2c294fSGerasim Troeglazovstatic le32 setsecurityattr(ntfs_volume *vol,
8270d2c294fSGerasim Troeglazov			const SECURITY_DESCRIPTOR_RELATIVE *attr, s64 attrsz)
8280d2c294fSGerasim Troeglazov{
8290d2c294fSGerasim Troeglazov	struct SDH *psdh;	/* this is an image of index (le) */
8300d2c294fSGerasim Troeglazov	union {
8310d2c294fSGerasim Troeglazov		struct {
8320d2c294fSGerasim Troeglazov			le32 dataoffsl;
8330d2c294fSGerasim Troeglazov			le32 dataoffsh;
8340d2c294fSGerasim Troeglazov		} parts;
8350d2c294fSGerasim Troeglazov		le64 all;
8360d2c294fSGerasim Troeglazov	} realign;
8370d2c294fSGerasim Troeglazov	BOOL found;
8380d2c294fSGerasim Troeglazov	BOOL collision;
8390d2c294fSGerasim Troeglazov	size_t size;
8400d2c294fSGerasim Troeglazov	size_t rdsize;
8410d2c294fSGerasim Troeglazov	s64 offs;
8420d2c294fSGerasim Troeglazov	int res;
8430d2c294fSGerasim Troeglazov	ntfs_index_context *xsdh;
8440d2c294fSGerasim Troeglazov	char *oldattr;
8450d2c294fSGerasim Troeglazov	SDH_INDEX_KEY key;
8460d2c294fSGerasim Troeglazov	INDEX_ENTRY *entry;
8470d2c294fSGerasim Troeglazov	le32 securid;
8480d2c294fSGerasim Troeglazov	le32 hash;
8490d2c294fSGerasim Troeglazov	int olderrno;
8500d2c294fSGerasim Troeglazov
8510d2c294fSGerasim Troeglazov	hash = ntfs_security_hash(attr,attrsz);
8520d2c294fSGerasim Troeglazov	oldattr = (char*)NULL;
8530d2c294fSGerasim Troeglazov	securid = const_cpu_to_le32(0);