]> wimlib.net Git - wimlib/blob - src/ntfs-3g_security.c
NTFS capture update
[wimlib] / src / ntfs-3g_security.c
1 /**
2  * security.c - Handling security/ACLs in NTFS.  Originated from the Linux-NTFS project.
3  *
4  * Copyright (c) 2004 Anton Altaparmakov
5  * Copyright (c) 2005-2006 Szabolcs Szakacsits
6  * Copyright (c) 2006 Yura Pakhuchiy
7  * Copyright (c) 2007-2010 Jean-Pierre Andre
8  *
9  * This program/include file is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU General Public License as published
11  * by the Free Software Foundation; either version 2 of the License, or
12  * (at your option) any later version.
13  *
14  * This program/include file is distributed in the hope that it will be
15  * useful, but WITHOUT ANY WARRANTY; without even the implied warranty
16  * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program (in the main directory of the NTFS-3G
21  * distribution in the file COPYING); if not, write to the Free Software
22  * Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
23  */
24
25 #ifdef HAVE_CONFIG_H
26 #include "config.h"
27 #endif
28
29 #ifdef ENABLE_XATTR
30 #define HAVE_SETXATTR
31 #endif
32
33 #include <stdarg.h>
34
35 #include <stdio.h>
36 #include <stdlib.h>
37 #include <string.h>
38 #include <errno.h>
39 #include <fcntl.h>
40 #ifdef HAVE_SETXATTR
41 #include <sys/xattr.h>
42 #endif
43 #ifdef HAVE_SYS_STAT_H
44 #include <sys/stat.h>
45 #endif
46
47 #include <unistd.h>
48 #include <pwd.h>
49 #include <grp.h>
50
51 #include <ntfs-3g/param.h>
52 #include <ntfs-3g/types.h>
53 #include <ntfs-3g/layout.h>
54 #include <ntfs-3g/attrib.h>
55 #include <ntfs-3g/index.h>
56 #include <ntfs-3g/dir.h>
57 #include <ntfs-3g/bitmap.h>
58 #include <ntfs-3g/security.h>
59 #include <ntfs-3g/acls.h>
60 #include <ntfs-3g/cache.h>
61 #include <ntfs-3g/misc.h>
62
63 /*
64  *      JPA NTFS constants or structs
65  *      should be moved to layout.h
66  */
67
68 #define ALIGN_SDS_BLOCK 0x40000 /* Alignment for a $SDS block */
69 #define ALIGN_SDS_ENTRY 16 /* Alignment for a $SDS entry */
70 #define STUFFSZ 0x4000 /* unitary stuffing size for $SDS */
71 #define FIRST_SECURITY_ID 0x100 /* Lowest security id */
72
73         /* Mask for attributes which can be forced */
74 #define FILE_ATTR_SETTABLE ( FILE_ATTR_READONLY         \
75                                 | FILE_ATTR_HIDDEN      \
76                                 | FILE_ATTR_SYSTEM      \
77                                 | FILE_ATTR_ARCHIVE     \
78                                 | FILE_ATTR_TEMPORARY   \
79                                 | FILE_ATTR_OFFLINE     \
80                                 | FILE_ATTR_NOT_CONTENT_INDEXED )
81
82 struct SII {            /* this is an image of an $SII index entry */
83         le16 offs;
84         le16 size;
85         le32 fill1;
86         le16 indexsz;
87         le16 indexksz;
88         le16 flags;
89         le16 fill2;
90         le32 keysecurid;
91
92         /* did not find official description for the following */
93         le32 hash;
94         le32 securid;
95         le32 dataoffsl; /* documented as badly aligned */
96         le32 dataoffsh;
97         le32 datasize;
98 } ;
99
100 struct SDH {            /* this is an image of an $SDH index entry */
101         le16 offs;
102         le16 size;
103         le32 fill1;
104         le16 indexsz;
105         le16 indexksz;
106         le16 flags;
107         le16 fill2;
108         le32 keyhash;
109         le32 keysecurid;
110
111         /* did not find official description for the following */
112         le32 hash;
113         le32 securid;
114         le32 dataoffsl;
115         le32 dataoffsh;
116         le32 datasize;
117         le32 fill3;
118         } ;
119
120 /*
121  *      A few useful constants
122  */
123
124 static ntfschar sii_stream[] = { const_cpu_to_le16('$'),
125                                  const_cpu_to_le16('S'),
126                                  const_cpu_to_le16('I'),   
127                                  const_cpu_to_le16('I'),   
128                                  const_cpu_to_le16(0) };
129 static ntfschar sdh_stream[] = { const_cpu_to_le16('$'),
130                                  const_cpu_to_le16('S'),
131                                  const_cpu_to_le16('D'),
132                                  const_cpu_to_le16('H'),
133                                  const_cpu_to_le16(0) };
134
135 /*
136  *              null SID (S-1-0-0)
137  */
138
139 extern const SID *nullsid;
140
141 /*
142  * The zero GUID.
143  */
144
145 static const GUID __zero_guid = { const_cpu_to_le32(0), const_cpu_to_le16(0),
146                 const_cpu_to_le16(0), { 0, 0, 0, 0, 0, 0, 0, 0 } };
147 static const GUID *const zero_guid = &__zero_guid;
148
149 /**
150  * ntfs_guid_is_zero - check if a GUID is zero
151  * @guid:       [IN] guid to check
152  *
153  * Return TRUE if @guid is a valid pointer to a GUID and it is the zero GUID
154  * and FALSE otherwise.
155  */
156 BOOL ntfs_guid_is_zero(const GUID *guid)
157 {
158         return (memcmp(guid, zero_guid, sizeof(*zero_guid)));
159 }
160
161 /**
162  * ntfs_guid_to_mbs - convert a GUID to a multi byte string
163  * @guid:       [IN]  guid to convert
164  * @guid_str:   [OUT] string in which to return the GUID (optional)
165  *
166  * Convert the GUID pointed to by @guid to a multi byte string of the form
167  * "XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX".  Therefore, @guid_str (if not NULL)
168  * needs to be able to store at least 37 bytes.
169  *
170  * If @guid_str is not NULL it will contain the converted GUID on return.  If
171  * it is NULL a string will be allocated and this will be returned.  The caller
172  * is responsible for free()ing the string in that case.
173  *
174  * On success return the converted string and on failure return NULL with errno
175  * set to the error code.
176  */
177 char *ntfs_guid_to_mbs(const GUID *guid, char *guid_str)
178 {
179         char *_guid_str;
180         int res;
181
182         if (!guid) {
183                 errno = EINVAL;
184                 return NULL;
185         }
186         _guid_str = guid_str;
187         if (!_guid_str) {
188                 _guid_str = (char*)ntfs_malloc(37);
189                 if (!_guid_str)
190                         return _guid_str;
191         }
192         res = snprintf(_guid_str, 37,
193                         "%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x",
194                         (unsigned int)le32_to_cpu(guid->data1),
195                         le16_to_cpu(guid->data2), le16_to_cpu(guid->data3),
196                         guid->data4[0], guid->data4[1],
197                         guid->data4[2], guid->data4[3], guid->data4[4],
198                         guid->data4[5], guid->data4[6], guid->data4[7]);
199         if (res == 36)
200                 return _guid_str;
201         if (!guid_str)
202                 free(_guid_str);
203         errno = EINVAL;
204         return NULL;
205 }
206
207 /**
208  * ntfs_sid_to_mbs_size - determine maximum size for the string of a SID
209  * @sid:        [IN]  SID for which to determine the maximum string size
210  *
211  * Determine the maximum multi byte string size in bytes which is needed to
212  * store the standard textual representation of the SID pointed to by @sid.
213  * See ntfs_sid_to_mbs(), below.
214  *
215  * On success return the maximum number of bytes needed to store the multi byte
216  * string and on failure return -1 with errno set to the error code.
217  */
218 int ntfs_sid_to_mbs_size(const SID *sid)
219 {
220         int size, i;
221
222         if (!ntfs_sid_is_valid(sid)) {
223                 errno = EINVAL;
224                 return -1;
225         }
226         /* Start with "S-". */
227         size = 2;
228         /*
229          * Add the SID_REVISION.  Hopefully the compiler will optimize this
230          * away as SID_REVISION is a constant.
231          */
232         for (i = SID_REVISION; i > 0; i /= 10)
233                 size++;
234         /* Add the "-". */
235         size++;
236         /*
237          * Add the identifier authority.  If it needs to be in decimal, the
238          * maximum is 2^32-1 = 4294967295 = 10 characters.  If it needs to be
239          * in hexadecimal, then maximum is 0x665544332211 = 14 characters.
240          */
241         if (!sid->identifier_authority.high_part)
242                 size += 10;
243         else
244                 size += 14;
245         /*
246          * Finally, add the sub authorities.  For each we have a "-" followed
247          * by a decimal which can be up to 2^32-1 = 4294967295 = 10 characters.
248          */
249         size += (1 + 10) * sid->sub_authority_count;
250         /* We need the zero byte at the end, too. */
251         size++;
252         return size * sizeof(char);
253 }
254
255 /**
256  * ntfs_sid_to_mbs - convert a SID to a multi byte string
257  * @sid:                [IN]  SID to convert
258  * @sid_str:            [OUT] string in which to return the SID (optional)
259  * @sid_str_size:       [IN]  size in bytes of @sid_str
260  *
261  * Convert the SID pointed to by @sid to its standard textual representation.
262  * @sid_str (if not NULL) needs to be able to store at least
263  * ntfs_sid_to_mbs_size() bytes.  @sid_str_size is the size in bytes of
264  * @sid_str if @sid_str is not NULL.
265  *
266  * The standard textual representation of the SID is of the form:
267  *      S-R-I-S-S...
268  * Where:
269  *    - The first "S" is the literal character 'S' identifying the following
270  *      digits as a SID.
271  *    - R is the revision level of the SID expressed as a sequence of digits
272  *      in decimal.
273  *    - I is the 48-bit identifier_authority, expressed as digits in decimal,
274  *      if I < 2^32, or hexadecimal prefixed by "0x", if I >= 2^32.
275  *    - S... is one or more sub_authority values, expressed as digits in
276  *      decimal.
277  *
278  * If @sid_str is not NULL it will contain the converted SUID on return.  If it
279  * is NULL a string will be allocated and this will be returned.  The caller is
280  * responsible for free()ing the string in that case.
281  *
282  * On success return the converted string and on failure return NULL with errno
283  * set to the error code.
284  */
285 char *ntfs_sid_to_mbs(const SID *sid, char *sid_str, size_t sid_str_size)
286 {
287         u64 u;
288         le32 leauth;
289         char *s;
290         int i, j, cnt;
291
292         /*
293          * No need to check @sid if !@sid_str since ntfs_sid_to_mbs_size() will
294          * check @sid, too.  8 is the minimum SID string size.
295          */
296         if (sid_str && (sid_str_size < 8 || !ntfs_sid_is_valid(sid))) {
297                 errno = EINVAL;
298                 return NULL;
299         }
300         /* Allocate string if not provided. */
301         if (!sid_str) {
302                 cnt = ntfs_sid_to_mbs_size(sid);
303                 if (cnt < 0)
304                         return NULL;
305                 s = (char*)ntfs_malloc(cnt);
306                 if (!s)
307                         return s;
308                 sid_str = s;
309                 /* So we know we allocated it. */
310                 sid_str_size = 0;
311         } else {
312                 s = sid_str;
313                 cnt = sid_str_size;
314         }
315         /* Start with "S-R-". */
316         i = snprintf(s, cnt, "S-%hhu-", (unsigned char)sid->revision);
317         if (i < 0 || i >= cnt)
318                 goto err_out;
319         s += i;
320         cnt -= i;
321         /* Add the identifier authority. */
322         for (u = i = 0, j = 40; i < 6; i++, j -= 8)
323                 u += (u64)sid->identifier_authority.value[i] << j;
324         if (!sid->identifier_authority.high_part)
325                 i = snprintf(s, cnt, "%lu", (unsigned long)u);
326         else
327                 i = snprintf(s, cnt, "0x%llx", (unsigned long long)u);
328         if (i < 0 || i >= cnt)
329                 goto err_out;
330         s += i;
331         cnt -= i;
332         /* Finally, add the sub authorities. */
333         for (j = 0; j < sid->sub_authority_count; j++) {
334                 leauth = sid->sub_authority[j];
335                 i = snprintf(s, cnt, "-%u", (unsigned int)
336                                 le32_to_cpu(leauth));
337                 if (i < 0 || i >= cnt)
338                         goto err_out;
339                 s += i;
340                 cnt -= i;
341         }
342         return sid_str;
343 err_out:
344         if (i >= cnt)
345                 i = EMSGSIZE;
346         else
347                 i = errno;
348         if (!sid_str_size)
349                 free(sid_str);
350         errno = i;
351         return NULL;
352 }
353
354 /**
355  * ntfs_generate_guid - generatates a random current guid.
356  * @guid:       [OUT]   pointer to a GUID struct to hold the generated guid.
357  *
358  * perhaps not a very good random number generator though...
359  */
360 void ntfs_generate_guid(GUID *guid)
361 {
362         unsigned int i;
363         u8 *p = (u8 *)guid;
364
365         for (i = 0; i < sizeof(GUID); i++) {
366                 p[i] = (u8)(random() & 0xFF);
367                 if (i == 7)
368                         p[7] = (p[7] & 0x0F) | 0x40;
369                 if (i == 8)
370                         p[8] = (p[8] & 0x3F) | 0x80;
371         }
372 }
373
374 /**
375  * ntfs_security_hash - calculate the hash of a security descriptor
376  * @sd:         self-relative security descriptor whose hash to calculate
377  * @length:     size in bytes of the security descritor @sd
378  *
379  * Calculate the hash of the self-relative security descriptor @sd of length
380  * @length bytes.
381  *
382  * This hash is used in the $Secure system file as the primary key for the $SDH
383  * index and is also stored in the header of each security descriptor in the
384  * $SDS data stream as well as in the index data of both the $SII and $SDH
385  * indexes.  In all three cases it forms part of the SDS_ENTRY_HEADER
386  * structure.
387  *
388  * Return the calculated security hash in little endian.
389  */
390 le32 ntfs_security_hash(const SECURITY_DESCRIPTOR_RELATIVE *sd, const u32 len)
391 {
392         const le32 *pos = (const le32*)sd;
393         const le32 *end = pos + (len >> 2);
394         u32 hash = 0;
395
396         while (pos < end) {
397                 hash = le32_to_cpup(pos) + ntfs_rol32(hash, 3);
398                 pos++;
399         }
400         return cpu_to_le32(hash);
401 }
402
403 /*
404  *      Get the first entry of current index block
405  *      cut and pasted form ntfs_ie_get_first() in index.c
406  */
407
408 static INDEX_ENTRY *ntfs_ie_get_first(INDEX_HEADER *ih)
409 {
410         return (INDEX_ENTRY*)((u8*)ih + le32_to_cpu(ih->entries_offset));
411 }
412
413 /*
414  *              Stuff a 256KB block into $SDS before writing descriptors
415  *      into the block.
416  *
417  *      This prevents $SDS from being automatically declared as sparse
418  *      when the second copy of the first security descriptor is written
419  *      256KB further ahead.
420  *
421  *      Having $SDS declared as a sparse file is not wrong by itself
422  *      and chkdsk leaves it as a sparse file. It does however complain
423  *      and add a sparse flag (0x0200) into field file_attributes of
424  *      STANDARD_INFORMATION of $Secure. This probably means that a
425  *      sparse attribute (ATTR_IS_SPARSE) is only allowed in sparse
426  *      files (FILE_ATTR_SPARSE_FILE).
427  *
428  *      Windows normally does not convert to sparse attribute or sparse
429  *      file. Stuffing is just a way to get to the same result.
430  */
431
432 static int entersecurity_stuff(ntfs_volume *vol, off_t offs)
433 {
434         int res;
435         int written;
436         unsigned long total;
437         char *stuff;
438
439         res = 0;
440         total = 0;
441         stuff = (char*)ntfs_malloc(STUFFSZ);
442         if (stuff) {
443                 memset(stuff, 0, STUFFSZ);
444                 do {
445                         written = ntfs_attr_data_write(vol->secure_ni,
446                                 STREAM_SDS, 4, stuff, STUFFSZ, offs);
447                         if (written == STUFFSZ) {
448                                 total += STUFFSZ;
449                                 offs += STUFFSZ;
450                         } else {
451                                 errno = ENOSPC;
452                                 res = -1;
453                         }
454                 } while (!res && (total < ALIGN_SDS_BLOCK));
455                 free(stuff);
456         } else {
457                 errno = ENOMEM;
458                 res = -1;
459         }
460         return (res);
461 }
462
463 /*
464  *              Enter a new security descriptor into $Secure (data only)
465  *      it has to be written twice with an offset of 256KB
466  *
467  *      Should only be called by entersecurityattr() to ensure consistency
468  *
469  *      Returns zero if sucessful
470  */
471
472 static int entersecurity_data(ntfs_volume *vol,
473                         const SECURITY_DESCRIPTOR_RELATIVE *attr, s64 attrsz,
474                         le32 hash, le32 keyid, off_t offs, int gap)
475 {
476         int res;
477         int written1;
478         int written2;
479         char *fullattr;
480         int fullsz;
481         SECURITY_DESCRIPTOR_HEADER *phsds;
482
483         res = -1;
484         fullsz = attrsz + gap + sizeof(SECURITY_DESCRIPTOR_HEADER);
485         fullattr = (char*)ntfs_malloc(fullsz);
486         if (fullattr) {
487                         /*
488                          * Clear the gap from previous descriptor
489                          * this could be useful for appending the second
490                          * copy to the end of file. When creating a new
491                          * 256K block, the gap is cleared while writing
492                          * the first copy
493                          */
494                 if (gap)
495                         memset(fullattr,0,gap);
496                 memcpy(&fullattr[gap + sizeof(SECURITY_DESCRIPTOR_HEADER)],
497                                 attr,attrsz);
498                 phsds = (SECURITY_DESCRIPTOR_HEADER*)&fullattr[gap];
499                 phsds->hash = hash;
500                 phsds->security_id = keyid;
501                 phsds->offset = cpu_to_le64(offs);
502                 phsds->length = cpu_to_le32(fullsz - gap);
503                 written1 = ntfs_attr_data_write(vol->secure_ni,
504                         STREAM_SDS, 4, fullattr, fullsz,
505                         offs - gap);
506                 written2 = ntfs_attr_data_write(vol->secure_ni,
507                         STREAM_SDS, 4, fullattr, fullsz,
508                         offs - gap + ALIGN_SDS_BLOCK);
509                 if ((written1 == fullsz)
510                      && (written2 == written1))
511                         res = 0;
512                 else
513                         errno = ENOSPC;
514                 free(fullattr);
515         } else
516                 errno = ENOMEM;
517         return (res);
518 }
519
520 /*
521  *      Enter a new security descriptor in $Secure (indexes only)
522  *
523  *      Should only be called by entersecurityattr() to ensure consistency
524  *
525  *      Returns zero if sucessful
526  */
527
528 static int entersecurity_indexes(ntfs_volume *vol, s64 attrsz,
529                         le32 hash, le32 keyid, off_t offs)
530 {
531         union {
532                 struct {
533                         le32 dataoffsl;
534                         le32 dataoffsh;
535                 } parts;
536                 le64 all;
537         } realign;
538         int res;
539         ntfs_index_context *xsii;
540         ntfs_index_context *xsdh;
541         struct SII newsii;
542         struct SDH newsdh;
543
544         res = -1;
545                                 /* enter a new $SII record */
546
547         xsii = vol->secure_xsii;
548         ntfs_index_ctx_reinit(xsii);
549         newsii.offs = const_cpu_to_le16(20);
550         newsii.size = const_cpu_to_le16(sizeof(struct SII) - 20);
551         newsii.fill1 = const_cpu_to_le32(0);
552         newsii.indexsz = const_cpu_to_le16(sizeof(struct SII));
553         newsii.indexksz = const_cpu_to_le16(sizeof(SII_INDEX_KEY));
554         newsii.flags = const_cpu_to_le16(0);
555         newsii.fill2 = const_cpu_to_le16(0);
556         newsii.keysecurid = keyid;
557         newsii.hash = hash;
558         newsii.securid = keyid;
559         realign.all = cpu_to_le64(offs);
560         newsii.dataoffsh = realign.parts.dataoffsh;
561         newsii.dataoffsl = realign.parts.dataoffsl;
562         newsii.datasize = cpu_to_le32(attrsz
563                          + sizeof(SECURITY_DESCRIPTOR_HEADER));
564         if (!ntfs_ie_add(xsii,(INDEX_ENTRY*)&newsii)) {
565
566                 /* enter a new $SDH record */
567
568                 xsdh = vol->secure_xsdh;
569                 ntfs_index_ctx_reinit(xsdh);
570                 newsdh.offs = const_cpu_to_le16(24);
571                 newsdh.size = const_cpu_to_le16(
572                         sizeof(SECURITY_DESCRIPTOR_HEADER));
573                 newsdh.fill1 = const_cpu_to_le32(0);
574                 newsdh.indexsz = const_cpu_to_le16(
575                                 sizeof(struct SDH));
576                 newsdh.indexksz = const_cpu_to_le16(
577                                 sizeof(SDH_INDEX_KEY));
578                 newsdh.flags = const_cpu_to_le16(0);
579                 newsdh.fill2 = const_cpu_to_le16(0);
580                 newsdh.keyhash = hash;
581                 newsdh.keysecurid = keyid;
582                 newsdh.hash = hash;
583                 newsdh.securid = keyid;
584                 newsdh.dataoffsh = realign.parts.dataoffsh;
585                 newsdh.dataoffsl = realign.parts.dataoffsl;
586                 newsdh.datasize = cpu_to_le32(attrsz
587                          + sizeof(SECURITY_DESCRIPTOR_HEADER));
588                            /* special filler value, Windows generally */
589                            /* fills with 0x00490049, sometimes with zero */
590                 newsdh.fill3 = const_cpu_to_le32(0x00490049);
591                 if (!ntfs_ie_add(xsdh,(INDEX_ENTRY*)&newsdh))
592                         res = 0;
593         }
594         return (res);
595 }
596
597 /*
598  *      Enter a new security descriptor in $Secure (data and indexes)
599  *      Returns id of entry, or zero if there is a problem.
600  *      (should not be called for NTFS version < 3.0)
601  *
602  *      important : calls have to be serialized, however no locking is
603  *      needed while fuse is not multithreaded
604  */
605
606 static le32 entersecurityattr(ntfs_volume *vol,
607                         const SECURITY_DESCRIPTOR_RELATIVE *attr, s64 attrsz,
608                         le32 hash)
609 {
610         union {
611                 struct {
612                         le32 dataoffsl;
613                         le32 dataoffsh;
614                 } parts;
615                 le64 all;
616         } realign;
617         le32 securid;
618         le32 keyid;
619         u32 newkey;
620         off_t offs;
621         int gap;
622         int size;
623         BOOL found;
624         struct SII *psii;
625         INDEX_ENTRY *entry;
626         INDEX_ENTRY *next;
627         ntfs_index_context *xsii;
628         int retries;
629         ntfs_attr *na;
630         int olderrno;
631
632         /* find the first available securid beyond the last key */
633         /* in $Secure:$SII. This also determines the first */
634         /* available location in $Secure:$SDS, as this stream */
635         /* is always appended to and the id's are allocated */
636         /* in sequence */
637
638         securid = const_cpu_to_le32(0);
639         xsii = vol->secure_xsii;
640         ntfs_index_ctx_reinit(xsii);
641         offs = size = 0;
642         keyid = const_cpu_to_le32(-1);
643         olderrno = errno;
644         found = !ntfs_index_lookup((char*)&keyid,
645                                sizeof(SII_INDEX_KEY), xsii);
646         if (!found && (errno != ENOENT)) {
647                 ntfs_log_perror("Inconsistency in index $SII");
648                 psii = (struct SII*)NULL;
649         } else {
650                         /* restore errno to avoid misinterpretation */
651                 errno = olderrno;
652                 entry = xsii->entry;
653                 psii = (struct SII*)xsii->entry;
654         }
655         if (psii) {
656                 /*
657                  * Get last entry in block, but must get first one
658                  * one first, as we should already be beyond the
659                  * last one. For some reason the search for the last
660                  * entry sometimes does not return the last block...
661                  * we assume this can only happen in root block
662                  */
663                 if (xsii->is_in_root)
664                         entry = ntfs_ie_get_first
665                                 ((INDEX_HEADER*)&xsii->ir->index);
666                 else
667                         entry = ntfs_ie_get_first
668                                 ((INDEX_HEADER*)&xsii->ib->index);
669                 /*
670                  * All index blocks should be at least half full
671                  * so there always is a last entry but one,
672                  * except when creating the first entry in index root.
673                  * This was however found not to be true : chkdsk
674                  * sometimes deletes all the (unused) keys in the last
675                  * index block without rebalancing the tree.
676                  * When this happens, a new search is restarted from
677                  * the smallest key.
678                  */
679                 keyid = const_cpu_to_le32(0);
680                 retries = 0;
681                 while (entry) {
682                         next = ntfs_index_next(entry,xsii);
683                         if (next) { 
684                                 psii = (struct SII*)next;
685                                         /* save last key and */
686                                         /* available position */
687                                 keyid = psii->keysecurid;
688                                 realign.parts.dataoffsh
689                                                  = psii->dataoffsh;
690                                 realign.parts.dataoffsl
691                                                  = psii->dataoffsl;
692                                 offs = le64_to_cpu(realign.all);
693                                 size = le32_to_cpu(psii->datasize);
694                         }
695                         entry = next;
696                         if (!entry && !keyid && !retries) {
697                                 /* search failed, retry from smallest key */
698                                 ntfs_index_ctx_reinit(xsii);
699                                 found = !ntfs_index_lookup((char*)&keyid,
700                                                sizeof(SII_INDEX_KEY), xsii);
701                                 if (!found && (errno != ENOENT)) {
702                                         ntfs_log_perror("Index $SII is broken");
703                                 } else {
704                                                 /* restore errno */
705                                         errno = olderrno;
706                                         entry = xsii->entry;
707                                 }
708                                 retries++;
709                         }
710                 }
711         }
712         if (!keyid) {
713                 /*
714                  * could not find any entry, before creating the first
715                  * entry, make a double check by making sure size of $SII
716                  * is less than needed for one entry
717                  */
718                 securid = const_cpu_to_le32(0);
719                 na = ntfs_attr_open(vol->secure_ni,AT_INDEX_ROOT,sii_stream,4);
720                 if (na) {
721                         if ((size_t)na->data_size < sizeof(struct SII)) {
722                                 ntfs_log_error("Creating the first security_id\n");
723                                 securid = const_cpu_to_le32(FIRST_SECURITY_ID);
724                         }
725                         ntfs_attr_close(na);
726                 }
727                 if (!securid) {
728                         ntfs_log_error("Error creating a security_id\n");
729                         errno = EIO;
730                 }
731         } else {
732                 newkey = le32_to_cpu(keyid) + 1;
733                 securid = cpu_to_le32(newkey);
734         }
735         /*
736          * The security attr has to be written twice 256KB
737          * apart. This implies that offsets like
738          * 0x40000*odd_integer must be left available for
739          * the second copy. So align to next block when
740          * the last byte overflows on a wrong block.
741          */
742
743         if (securid) {
744                 gap = (-size) & (ALIGN_SDS_ENTRY - 1);
745                 offs += gap + size;
746                 if ((offs + attrsz + sizeof(SECURITY_DESCRIPTOR_HEADER) - 1)
747                    & ALIGN_SDS_BLOCK) {
748                         offs = ((offs + attrsz
749                                  + sizeof(SECURITY_DESCRIPTOR_HEADER) - 1)
750                                 | (ALIGN_SDS_BLOCK - 1)) + 1;
751                 }
752                 if (!(offs & (ALIGN_SDS_BLOCK - 1)))
753                         entersecurity_stuff(vol, offs);
754                 /*
755                  * now write the security attr to storage :
756                  * first data, then SII, then SDH
757                  * If failure occurs while writing SDS, data will never
758                  *    be accessed through indexes, and will be overwritten
759                  *    by the next allocated descriptor
760                  * If failure occurs while writing SII, the id has not
761                  *    recorded and will be reallocated later
762                  * If failure occurs while writing SDH, the space allocated
763                  *    in SDS or SII will not be reused, an inconsistency
764                  *    will persist with no significant consequence
765                  */
766                 if (entersecurity_data(vol, attr, attrsz, hash, securid, offs, gap)
767                     || entersecurity_indexes(vol, attrsz, hash, securid, offs))
768                         securid = const_cpu_to_le32(0);
769         }
770                 /* inode now is dirty, synchronize it all */
771         ntfs_index_entry_mark_dirty(vol->secure_xsii);
772         ntfs_index_ctx_reinit(vol->secure_xsii);
773         ntfs_index_entry_mark_dirty(vol->secure_xsdh);
774         ntfs_index_ctx_reinit(vol->secure_xsdh);
775         NInoSetDirty(vol->secure_ni);
776         if (ntfs_inode_sync(vol->secure_ni))
777                 ntfs_log_perror("Could not sync $Secure\n");
778         return (securid);
779 }
780
781 /*
782  *              Find a matching security descriptor in $Secure,
783  *      if none, allocate a new id and write the descriptor to storage
784  *      Returns id of entry, or zero if there is a problem.
785  *
786  *      important : calls have to be serialized, however no locking is
787  *      needed while fuse is not multithreaded
788  */
789
790 static le32 setsecurityattr(ntfs_volume *vol,
791                         const SECURITY_DESCRIPTOR_RELATIVE *attr, s64 attrsz)
792 {
793         struct SDH *psdh;       /* this is an image of index (le) */
794         union {
795                 struct {
796                         le32 dataoffsl;
797                         le32 dataoffsh;
798                 } parts;
799                 le64 all;
800         } realign;
801         BOOL found;
802         BOOL collision;
803         size_t size;
804         size_t rdsize;
805         s64 offs;
806         int res;
807         ntfs_index_context *xsdh;
808         char *oldattr;
809         SDH_INDEX_KEY key;
810         INDEX_ENTRY *entry;
811         le32 securid;
812         le32 hash;
813         int olderrno;
814
815         hash = ntfs_security_hash(attr,attrsz);
816         oldattr = (char*)NULL;
817         securid = const_cpu_to_le32(0);
818         res = 0;
819         xsdh = vol->secure_xsdh;
820         if (vol->secure_ni && xsdh && !vol->secure_reentry++) {
821                 ntfs_index_ctx_reinit(xsdh);
822                 /*
823                  * find the nearest key as (hash,0)
824                  * (do not search for partial key : in case of collision,
825                  * it could return a key which is not the first one which
826                  * collides)
827                  */
828                 key.hash = hash;
829                 key.security_id = const_cpu_to_le32(0);
830                 olderrno = errno;
831                 found = !ntfs_index_lookup((char*)&key,
832                                  sizeof(SDH_INDEX_KEY), xsdh);
833                 if (!found && (errno != ENOENT))
834                         ntfs_log_perror("Inconsistency in index $SDH");
835                 else {
836                                 /* restore errno to avoid misinterpretation */
837                         errno = olderrno;
838                         entry = xsdh->entry;
839                         found = FALSE;
840                         /*
841                          * lookup() may return a node with no data,
842                          * if so get next
843                          */
844                         if (entry->ie_flags & INDEX_ENTRY_END)
845                                 entry = ntfs_index_next(entry,xsdh);
846                         do {
847                                 collision = FALSE;
848                                 psdh = (struct SDH*)entry;
849                                 if (psdh)
850                                         size = (size_t) le32_to_cpu(psdh->datasize)
851                                                  - sizeof(SECURITY_DESCRIPTOR_HEADER);
852                                 else size = 0;
853                            /* if hash is not the same, the key is not present */
854                                 if (psdh && (size > 0)
855                                    && (psdh->keyhash == hash)) {
856                                            /* if hash is the same */
857                                            /* check the whole record */
858                                         realign.parts.dataoffsh = psdh->dataoffsh;
859                                         realign.parts.dataoffsl = psdh->dataoffsl;
860                                         offs = le64_to_cpu(realign.all)
861                                                 + sizeof(SECURITY_DESCRIPTOR_HEADER);
862                                         oldattr = (char*)ntfs_malloc(size);
863                                         if (oldattr) {
864                                                 rdsize = ntfs_attr_data_read(
865                                                         vol->secure_ni,
866                                                         STREAM_SDS, 4,
867                                                         oldattr, size, offs);
868                                                 found = (rdsize == size)
869                                                         && !memcmp(oldattr,attr,size);
870                                                 free(oldattr);
871                                           /* if the records do not compare */
872                                           /* (hash collision), try next one */
873                                                 if (!found) {
874                                                         entry = ntfs_index_next(
875                                                                 entry,xsdh);
876                                                         collision = TRUE;
877                                                 }
878                                         } else
879                                                 res = ENOMEM;
880                                 }
881                         } while (collision && entry);
882                         if (found)
883                                 securid = psdh->keysecurid;
884                         else {
885                                 if (res) {
886                                         errno = res;
887                                         securid = const_cpu_to_le32(0);
888                                 } else {
889                                         /*
890                                          * no matching key :
891                                          * have to build a new one
892                                          */
893                                         securid = entersecurityattr(vol,
894                                                 attr, attrsz, hash);
895                                 }
896                         }
897                 }
898         }
899         if (--vol->secure_reentry)
900                 ntfs_log_perror("Reentry error, check no multithreading\n");
901         return (securid);
902 }
903
904
905 /*
906  *              Update the security descriptor of a file
907  *      Either as an attribute (complying with pre v3.x NTFS version)
908  *      or, when possible, as an entry in $Secure (for NTFS v3.x)
909  *
910  *      returns 0 if success
911  */
912
913 static int update_secur_descr(ntfs_volume *vol,
914                                 char *newattr, ntfs_inode *ni)
915 {
916         int newattrsz;
917         int written;
918         int res;
919         ntfs_attr *na;
920
921         newattrsz = ntfs_attr_size(newattr);
922
923 #if !FORCE_FORMAT_v1x
924         if ((vol->major_ver < 3) || !vol->secure_ni) {
925 #endif
926
927                 /* update for NTFS format v1.x */
928
929                 /* update the old security attribute */
930                 na = ntfs_attr_open(ni, AT_SECURITY_DESCRIPTOR, AT_UNNAMED, 0);
931                 if (na) {
932                         /* resize attribute */
933                         res = ntfs_attr_truncate(na, (s64) newattrsz);
934                         /* overwrite value */
935                         if (!res) {
936                                 written = (int)ntfs_attr_pwrite(na, (s64) 0,
937                                          (s64) newattrsz, newattr);
938                                 if (written != newattrsz) {
939                                         ntfs_log_error("Failed to update "
940                                                 "a v1.x security descriptor\n");
941                                         errno = EIO;
942                                         res = -1;
943                                 }
944                         }
945
946                         ntfs_attr_close(na);
947                         /* if old security attribute was found, also */
948                         /* truncate standard information attribute to v1.x */
949                         /* this is needed when security data is wanted */
950                         /* as v1.x though volume is formatted for v3.x */
951                         na = ntfs_attr_open(ni, AT_STANDARD_INFORMATION,
952                                 AT_UNNAMED, 0);
953                         if (na) {
954                                 clear_nino_flag(ni, v3_Extensions);
955                         /*
956                          * Truncating the record does not sweep extensions
957                          * from copy in memory. Clear security_id to be safe
958                          */
959                                 ni->security_id = const_cpu_to_le32(0);
960                                 res = ntfs_attr_truncate(na, (s64)48);
961                                 ntfs_attr_close(na);
962                                 clear_nino_flag(ni, v3_Extensions);
963                         }
964                 } else {
965                         /*
966                          * insert the new security attribute if there
967                          * were none
968                          */
969                         res = ntfs_attr_add(ni, AT_SECURITY_DESCRIPTOR,
970                                             AT_UNNAMED, 0, (u8*)newattr,
971                                             (s64) newattrsz);
972                 }
973 #if !FORCE_FORMAT_v1x
974         } else {
975
976                 /* update for NTFS format v3.x */
977
978                 le32 securid;
979
980                 securid = setsecurityattr(vol,
981                         (const SECURITY_DESCRIPTOR_RELATIVE*)newattr,
982                         (s64)newattrsz);
983                 if (securid) {
984                         na = ntfs_attr_open(ni, AT_STANDARD_INFORMATION,
985                                 AT_UNNAMED, 0);
986                         if (na) {
987                                 res = 0;
988                                 if (!test_nino_flag(ni, v3_Extensions)) {
989                         /* expand standard information attribute to v3.x */
990                                         res = ntfs_attr_truncate(na,
991                                          (s64)sizeof(STANDARD_INFORMATION));
992                                         ni->owner_id = const_cpu_to_le32(0);
993                                         ni->quota_charged = const_cpu_to_le64(0);
994                                         ni->usn = const_cpu_to_le64(0);
995                                         ntfs_attr_remove(ni,
996                                                 AT_SECURITY_DESCRIPTOR,
997                                                 AT_UNNAMED, 0);
998                         }
999                                 set_nino_flag(ni, v3_Extensions);
1000                                 ni->security_id = securid;
1001                                 ntfs_attr_close(na);
1002                         } else {
1003                                 ntfs_log_error("Failed to update "
1004                                         "standard informations\n");
1005                                 errno = EIO;
1006                                 res = -1;
1007                         }
1008                 } else
1009                         res = -1;
1010         }
1011 #endif
1012
1013         /* mark node as dirty */
1014         NInoSetDirty(ni);
1015         return (res);
1016 }
1017
1018 /*
1019  *              Upgrade the security descriptor of a file
1020  *      This is intended to allow graceful upgrades for files which
1021  *      were created in previous versions, with a security attributes
1022  *      and no security id.
1023  *      
1024  *      It will allocate a security id and replace the individual
1025  *      security attribute by a reference to the global one
1026  *
1027  *      Special files are not upgraded (currently / and files in
1028  *      directories /$*)
1029  *
1030  *      Though most code is similar to update_secur_desc() it has
1031  *      been kept apart to facilitate the further processing of
1032  *      special cases or even to remove it if found dangerous.
1033  *
1034  *      returns 0 if success,
1035  *              1 if not upgradable. This is not an error.
1036  *              -1 if there is a problem
1037  */
1038
1039 static int upgrade_secur_desc(ntfs_volume *vol,
1040                                 const char *attr, ntfs_inode *ni)
1041 {
1042         int attrsz;
1043         int res;
1044         le32 securid;
1045         ntfs_attr *na;
1046
1047                 /*
1048                  * upgrade requires NTFS format v3.x
1049                  * also refuse upgrading for special files
1050                  * whose number is less than FILE_first_user
1051                  */
1052
1053         if ((vol->major_ver >= 3)
1054             && (ni->mft_no >= FILE_first_user)) {
1055                 attrsz = ntfs_attr_size(attr);
1056                 securid = setsecurityattr(vol,
1057                         (const SECURITY_DESCRIPTOR_RELATIVE*)attr,
1058                         (s64)attrsz);
1059                 if (securid) {
1060                         na = ntfs_attr_open(ni, AT_STANDARD_INFORMATION,
1061                                 AT_UNNAMED, 0);
1062                         if (na) {
1063                         /* expand standard information attribute to v3.x */
1064                                 res = ntfs_attr_truncate(na,
1065                                          (s64)sizeof(STANDARD_INFORMATION));
1066                                 ni->owner_id = const_cpu_to_le32(0);
1067                                 ni->quota_charged = const_cpu_to_le64(0);
1068                                 ni->usn = const_cpu_to_le64(0);
1069                                 ntfs_attr_remove(ni, AT_SECURITY_DESCRIPTOR,
1070                                                 AT_UNNAMED, 0);
1071                                 set_nino_flag(ni, v3_Extensions);
1072                                 ni->security_id = securid;
1073                                 ntfs_attr_close(na);
1074                         } else {
1075                                 ntfs_log_error("Failed to upgrade "
1076                                         "standard informations\n");
1077                                 errno = EIO;
1078                                 res = -1;
1079                         }
1080                 } else
1081                         res = -1;
1082                         /* mark node as dirty */
1083                 NInoSetDirty(ni);
1084         } else
1085                 res = 1;
1086
1087         return (res);
1088 }
1089
1090 /*
1091  *              Optional simplified checking of group membership
1092  *
1093  *      This only takes into account the groups defined in
1094  *      /etc/group at initialization time.
1095  *      It does not take into account the groups dynamically set by
1096  *      setgroups() nor the changes in /etc/group since initialization
1097  *
1098  *      This optional method could be useful if standard checking
1099  *      leads to a performance concern.
1100  *
1101  *      Should not be called for user root, however the group may be root
1102  *
1103  */
1104
1105 static BOOL staticgroupmember(struct SECURITY_CONTEXT *scx, uid_t uid, gid_t gid)
1106 {
1107         BOOL ingroup;
1108         int grcnt;
1109         gid_t *groups;
1110         struct MAPPING *user;
1111
1112         ingroup = FALSE;
1113         if (uid) {
1114                 user = scx->mapping[MAPUSERS];
1115                 while (user && ((uid_t)user->xid != uid))
1116                         user = user->next;
1117                 if (user) {
1118                         groups = user->groups;
1119                         grcnt = user->grcnt;
1120                         while ((--grcnt >= 0) && (groups[grcnt] != gid)) { }
1121                         ingroup = (grcnt >= 0);
1122                 }
1123         }
1124         return (ingroup);
1125 }
1126
1127
1128 /*
1129  *              Check whether current thread owner is member of file group
1130  *
1131  *      Should not be called for user root, however the group may be root
1132  *
1133  * As indicated by Miklos Szeredi :
1134  *
1135  * The group list is available in
1136  *
1137  *   /proc/$PID/task/$TID/status
1138  *
1139  * and fuse supplies TID in get_fuse_context()->pid.  The only problem is
1140  * finding out PID, for which I have no good solution, except to iterate
1141  * through all processes.  This is rather slow, but may be speeded up
1142  * with caching and heuristics (for single threaded programs PID = TID).
1143  *
1144  * The following implementation gets the group list from
1145  *   /proc/$TID/task/$TID/status which apparently exists and
1146  * contains the same data.
1147  */
1148
1149 static BOOL groupmember(struct SECURITY_CONTEXT *scx, uid_t uid, gid_t gid)
1150 {
1151         static char key[] = "\nGroups:";
1152         char buf[BUFSZ+1];
1153         char filename[64];
1154         enum { INKEY, INSEP, INNUM, INEND } state;
1155         int fd;
1156         char c;
1157         int matched;
1158         BOOL ismember;
1159         int got;
1160         char *p;
1161         gid_t grp;
1162         pid_t tid;
1163
1164         if (scx->vol->secure_flags & (1 << SECURITY_STATICGRPS))
1165                 ismember = staticgroupmember(scx, uid, gid);
1166         else {
1167                 ismember = FALSE; /* default return */
1168                 tid = scx->tid;
1169                 sprintf(filename,"/proc/%u/task/%u/status",tid,tid);
1170                 fd = open(filename,O_RDONLY);
1171                 if (fd >= 0) {
1172                         got = read(fd, buf, BUFSZ);
1173                         buf[got] = 0;
1174                         state = INKEY;
1175                         matched = 0;
1176                         p = buf;
1177                         grp = 0;
1178                                 /*
1179                                  *  A simple automaton to process lines like
1180                                  *  Groups: 14 500 513
1181                                  */
1182                         do {
1183                                 c = *p++;
1184                                 if (!c) {
1185                                         /* refill buffer */
1186                                         got = read(fd, buf, BUFSZ);
1187                                         buf[got] = 0;
1188                                         p = buf;
1189                                         c = *p++; /* 0 at end of file */
1190                                 }
1191                                 switch (state) {
1192                                 case INKEY :
1193                                         if (key[matched] == c) {
1194                                                 if (!key[++matched])
1195                                                         state = INSEP;
1196                                         } else
1197                                                 if (key[0] == c)
1198                                                         matched = 1;
1199                                                 else
1200                                                         matched = 0;
1201                                         break;
1202                                 case INSEP :
1203                                         if ((c >= '0') && (c <= '9')) {
1204                                                 grp = c - '0';
1205                                                 state = INNUM;
1206                                         } else
1207                                                 if ((c != ' ') && (c != '\t'))
1208                                                         state = INEND;
1209                                         break;
1210                                 case INNUM :
1211                                         if ((c >= '0') && (c <= '9'))
1212                                                 grp = grp*10 + c - '0';
1213                                         else {
1214                                                 ismember = (grp == gid);
1215                                                 if ((c != ' ') && (c != '\t'))
1216                                                         state = INEND;
1217                                                 else
1218                                                         state = INSEP;
1219                                         }
1220                                 default :
1221                                         break;
1222                                 }
1223                         } while (!ismember && c && (state != INEND));
1224                 close(fd);
1225                 if (!c)
1226                         ntfs_log_error("No group record found in %s\n",filename);
1227                 } else
1228                         ntfs_log_error("Could not open %s\n",filename);
1229         }
1230         return (ismember);
1231 }
1232
1233 /*
1234  *      Cacheing is done two-way :
1235  *      - from uid, gid and perm to securid (CACHED_SECURID)
1236  *      - from a securid to uid, gid and perm (CACHED_PERMISSIONS)
1237  *
1238  *      CACHED_SECURID data is kept in a most-recent-first list
1239  *      which should not be too long to be efficient. Its optimal
1240  *      size is depends on usage and is hard to determine.
1241  *
1242  *      CACHED_PERMISSIONS data is kept in a two-level indexed array. It
1243  *      is optimal at the expense of storage. Use of a most-recent-first
1244  *      list would save memory and provide similar performances for
1245  *      standard usage, but not for file servers with too many file
1246  *      owners
1247  *
1248  *      CACHED_PERMISSIONS_LEGACY is a special case for CACHED_PERMISSIONS
1249  *      for legacy directories which were not allocated a security_id
1250  *      it is organized in a most-recent-first list.
1251  *
1252  *      In main caches, data is never invalidated, as the meaning of
1253  *      a security_id only changes when user mapping is changed, which
1254  *      current implies remounting. However returned entries may be
1255  *      overwritten at next update, so data has to be copied elsewhere
1256  *      before another cache update is made.
1257  *      In legacy cache, data has to be invalidated when protection is
1258  *      changed.
1259  *
1260  *      Though the same data may be found in both list, they
1261  *      must be kept separately : the interpretation of ACL
1262  *      in both direction are approximations which could be non
1263  *      reciprocal for some configuration of the user mapping data
1264  *
1265  *      During the process of recompiling ntfs-3g from a tgz archive,
1266  *      security processing added 7.6% to the cpu time used by ntfs-3g
1267  *      and 30% if the cache is disabled.
1268  */
1269
1270 static struct PERMISSIONS_CACHE *create_caches(struct SECURITY_CONTEXT *scx,
1271                         u32 securindex)
1272 {
1273         struct PERMISSIONS_CACHE *cache;
1274         unsigned int index1;
1275         unsigned int i;
1276
1277         cache = (struct PERMISSIONS_CACHE*)NULL;
1278                 /* create the first permissions blocks */
1279         index1 = securindex >> CACHE_PERMISSIONS_BITS;
1280         cache = (struct PERMISSIONS_CACHE*)
1281                 ntfs_malloc(sizeof(struct PERMISSIONS_CACHE)
1282                       + index1*sizeof(struct CACHED_PERMISSIONS*));
1283         if (cache) {
1284                 cache->head.last = index1;
1285                 cache->head.p_reads = 0;
1286                 cache->head.p_hits = 0;
1287                 cache->head.p_writes = 0;
1288                 *scx->pseccache = cache;
1289                 for (i=0; i<=index1; i++)
1290                         cache->cachetable[i]
1291                            = (struct CACHED_PERMISSIONS*)NULL;
1292         }
1293         return (cache);
1294 }
1295
1296 /*
1297  *              Free memory used by caches
1298  *      The only purpose is to facilitate the detection of memory leaks
1299  */
1300
1301 static void free_caches(struct SECURITY_CONTEXT *scx)
1302 {
1303         unsigned int index1;
1304         struct PERMISSIONS_CACHE *pseccache;
1305
1306         pseccache = *scx->pseccache;
1307         if (pseccache) {
1308                 for (index1=0; index1<=pseccache->head.last; index1++)
1309                         if (pseccache->cachetable[index1]) {
1310 #if POSIXACLS
1311                                 struct CACHED_PERMISSIONS *cacheentry;
1312                                 unsigned int index2;
1313
1314                                 for (index2=0; index2<(1<< CACHE_PERMISSIONS_BITS); index2++) {
1315                                         cacheentry = &pseccache->cachetable[index1][index2];
1316                                         if (cacheentry->valid
1317                                             && cacheentry->pxdesc)
1318                                                 free(cacheentry->pxdesc);
1319                                         }
1320 #endif
1321                                 free(pseccache->cachetable[index1]);
1322                         }
1323                 free(pseccache);
1324         }
1325 }
1326
1327 static int compare(const struct CACHED_SECURID *cached,
1328                         const struct CACHED_SECURID *item)
1329 {
1330 #if POSIXACLS
1331         size_t csize;
1332         size_t isize;
1333
1334                 /* only compare data and sizes */
1335         csize = (cached->variable ?
1336                 sizeof(struct POSIX_ACL)
1337                 + (((struct POSIX_SECURITY*)cached->variable)->acccnt
1338                    + ((struct POSIX_SECURITY*)cached->variable)->defcnt)
1339                         *sizeof(struct POSIX_ACE) :
1340                 0);
1341         isize = (item->variable ?
1342                 sizeof(struct POSIX_ACL)
1343                 + (((struct POSIX_SECURITY*)item->variable)->acccnt
1344                    + ((struct POSIX_SECURITY*)item->variable)->defcnt)
1345                         *sizeof(struct POSIX_ACE) :
1346                 0);
1347         return ((cached->uid != item->uid)
1348                  || (cached->gid != item->gid)
1349                  || (cached->dmode != item->dmode)
1350                  || (csize != isize)
1351                  || (csize
1352                     && isize
1353                     && memcmp(&((struct POSIX_SECURITY*)cached->variable)->acl,
1354                        &((struct POSIX_SECURITY*)item->variable)->acl, csize)));
1355 #else
1356         return ((cached->uid != item->uid)
1357                  || (cached->gid != item->gid)
1358                  || (cached->dmode != item->dmode));
1359 #endif
1360 }
1361
1362 static int leg_compare(const struct CACHED_PERMISSIONS_LEGACY *cached,
1363                         const struct CACHED_PERMISSIONS_LEGACY *item)
1364 {
1365         return (cached->mft_no != item->mft_no);
1366 }
1367
1368 /*
1369  *      Resize permission cache table
1370  *      do not call unless resizing is needed
1371  *      
1372  *      If allocation fails, the cache size is not updated
1373  *      Lack of memory is not considered as an error, the cache is left
1374  *      consistent and errno is not set.
1375  */
1376
1377 static void resize_cache(struct SECURITY_CONTEXT *scx,
1378                         u32 securindex)
1379 {
1380         struct PERMISSIONS_CACHE *oldcache;
1381         struct PERMISSIONS_CACHE *newcache;
1382         int newcnt;
1383         int oldcnt;
1384         unsigned int index1;
1385         unsigned int i;
1386
1387         oldcache = *scx->pseccache;
1388         index1 = securindex >> CACHE_PERMISSIONS_BITS;
1389         newcnt = index1 + 1;
1390         if (newcnt <= ((CACHE_PERMISSIONS_SIZE
1391                         + (1 << CACHE_PERMISSIONS_BITS)
1392                         - 1) >> CACHE_PERMISSIONS_BITS)) {
1393                 /* expand cache beyond current end, do not use realloc() */
1394                 /* to avoid losing data when there is no more memory */
1395                 oldcnt = oldcache->head.last + 1;
1396                 newcache = (struct PERMISSIONS_CACHE*)
1397                         ntfs_malloc(
1398                             sizeof(struct PERMISSIONS_CACHE)
1399                               + (newcnt - 1)*sizeof(struct CACHED_PERMISSIONS*));
1400                 if (newcache) {
1401                         memcpy(newcache,oldcache,
1402                             sizeof(struct PERMISSIONS_CACHE)
1403                               + (oldcnt - 1)*sizeof(struct CACHED_PERMISSIONS*));
1404                         free(oldcache);
1405                              /* mark new entries as not valid */
1406                         for (i=newcache->head.last+1; i<=index1; i++)
1407                                 newcache->cachetable[i]
1408                                          = (struct CACHED_PERMISSIONS*)NULL;
1409                         newcache->head.last = index1;
1410                         *scx->pseccache = newcache;
1411                 }
1412         }
1413 }
1414
1415 /*
1416  *      Enter uid, gid and mode into cache, if possible
1417  *
1418  *      returns the updated or created cache entry,
1419  *      or NULL if not possible (typically if there is no
1420  *              security id associated)
1421  */
1422
1423 #if POSIXACLS
1424 static struct CACHED_PERMISSIONS *enter_cache(struct SECURITY_CONTEXT *scx,
1425                 ntfs_inode *ni, uid_t uid, gid_t gid,
1426                 struct POSIX_SECURITY *pxdesc)
1427 #else
1428 static struct CACHED_PERMISSIONS *enter_cache(struct SECURITY_CONTEXT *scx,
1429                 ntfs_inode *ni, uid_t uid, gid_t gid, mode_t mode)
1430 #endif
1431 {
1432         struct CACHED_PERMISSIONS *cacheentry;
1433         struct CACHED_PERMISSIONS *cacheblock;
1434         struct PERMISSIONS_CACHE *pcache;
1435         u32 securindex;
1436 #if POSIXACLS
1437         int pxsize;
1438         struct POSIX_SECURITY *pxcached;
1439 #endif
1440         unsigned int index1;
1441         unsigned int index2;
1442         int i;
1443
1444         /* cacheing is only possible if a security_id has been defined */
1445         if (test_nino_flag(ni, v3_Extensions)
1446            && ni->security_id) {
1447                 /*
1448                  *  Immediately test the most frequent situation
1449                  *  where the entry exists
1450                  */
1451                 securindex = le32_to_cpu(ni->security_id);
1452                 index1 = securindex >> CACHE_PERMISSIONS_BITS;
1453                 index2 = securindex & ((1 << CACHE_PERMISSIONS_BITS) - 1);
1454                 pcache = *scx->pseccache;
1455                 if (pcache
1456                      && (pcache->head.last >= index1)
1457                      && pcache->cachetable[index1]) {
1458                         cacheentry = &pcache->cachetable[index1][index2];
1459                         cacheentry->uid = uid;
1460                         cacheentry->gid = gid;
1461 #if POSIXACLS
1462                         if (cacheentry->valid && cacheentry->pxdesc)
1463                                 free(cacheentry->pxdesc);
1464                         if (pxdesc) {
1465                                 pxsize = sizeof(struct POSIX_SECURITY)
1466                                         + (pxdesc->acccnt + pxdesc->defcnt)*sizeof(struct POSIX_ACE);
1467                                 pxcached = (struct POSIX_SECURITY*)malloc(pxsize);
1468                                 if (pxcached) {
1469                                         memcpy(pxcached, pxdesc, pxsize);
1470                                         cacheentry->pxdesc = pxcached;
1471                                 } else {
1472                                         cacheentry->valid = 0;
1473                                         cacheentry = (struct CACHED_PERMISSIONS*)NULL;
1474                                 }
1475                                 cacheentry->mode = pxdesc->mode & 07777;
1476                         } else
1477                                 cacheentry->pxdesc = (struct POSIX_SECURITY*)NULL;
1478 #else
1479                         cacheentry->mode = mode & 07777;
1480 #endif
1481                         cacheentry->inh_fileid = const_cpu_to_le32(0);
1482                         cacheentry->inh_dirid = const_cpu_to_le32(0);
1483                         cacheentry->valid = 1;
1484                         pcache->head.p_writes++;
1485                 } else {
1486                         if (!pcache) {
1487                                 /* create the first cache block */
1488                                 pcache = create_caches(scx, securindex);
1489                         } else {
1490                                 if (index1 > pcache->head.last) {
1491                                         resize_cache(scx, securindex);
1492                                         pcache = *scx->pseccache;
1493                                 }
1494                         }
1495                         /* allocate block, if cache table was allocated */
1496                         if (pcache && (index1 <= pcache->head.last)) {
1497                                 cacheblock = (struct CACHED_PERMISSIONS*)
1498                                         malloc(sizeof(struct CACHED_PERMISSIONS)
1499                                                 << CACHE_PERMISSIONS_BITS);
1500                                 pcache->cachetable[index1] = cacheblock;
1501                                 for (i=0; i<(1 << CACHE_PERMISSIONS_BITS); i++)
1502                                         cacheblock[i].valid = 0;
1503                                 cacheentry = &cacheblock[index2];
1504                                 if (cacheentry) {
1505                                         cacheentry->uid = uid;
1506                                         cacheentry->gid = gid;
1507 #if POSIXACLS
1508                                         if (pxdesc) {
1509                                                 pxsize = sizeof(struct POSIX_SECURITY)
1510                                                         + (pxdesc->acccnt + pxdesc->defcnt)*sizeof(struct POSIX_ACE);
1511                                                 pxcached = (struct POSIX_SECURITY*)malloc(pxsize);
1512                                                 if (pxcached) {
1513                                                         memcpy(pxcached, pxdesc, pxsize);
1514                                                         cacheentry->pxdesc = pxcached;
1515                                                 } else {
1516                                                         cacheentry->valid = 0;
1517                                                         cacheentry = (struct CACHED_PERMISSIONS*)NULL;
1518                                                 }
1519                                                 cacheentry->mode = pxdesc->mode & 07777;
1520                                         } else
1521                                                 cacheentry->pxdesc = (struct POSIX_SECURITY*)NULL;
1522 #else
1523                                         cacheentry->mode = mode & 07777;
1524 #endif
1525                                         cacheentry->inh_fileid = const_cpu_to_le32(0);
1526                                         cacheentry->inh_dirid = const_cpu_to_le32(0);
1527                                         cacheentry->valid = 1;
1528                                         pcache->head.p_writes++;
1529                                 }
1530                         } else
1531                                 cacheentry = (struct CACHED_PERMISSIONS*)NULL;
1532                 }
1533         } else {
1534                 cacheentry = (struct CACHED_PERMISSIONS*)NULL;
1535 #if CACHE_LEGACY_SIZE
1536                 if (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY) {
1537                         struct CACHED_PERMISSIONS_LEGACY wanted;
1538                         struct CACHED_PERMISSIONS_LEGACY *legacy;
1539
1540                         wanted.perm.uid = uid;
1541                         wanted.perm.gid = gid;
1542 #if POSIXACLS
1543                         wanted.perm.mode = pxdesc->mode & 07777;
1544                         wanted.perm.inh_fileid = const_cpu_to_le32(0);
1545                         wanted.perm.inh_dirid = const_cpu_to_le32(0);
1546                         wanted.mft_no = ni->mft_no;
1547                         wanted.variable = (void*)pxdesc;
1548                         wanted.varsize = sizeof(struct POSIX_SECURITY)
1549                                         + (pxdesc->acccnt + pxdesc->defcnt)*sizeof(struct POSIX_ACE);
1550 #else
1551                         wanted.perm.mode = mode & 07777;
1552                         wanted.perm.inh_fileid = const_cpu_to_le32(0);
1553                         wanted.perm.inh_dirid = const_cpu_to_le32(0);
1554                         wanted.mft_no = ni->mft_no;
1555                         wanted.variable = (void*)NULL;
1556                         wanted.varsize = 0;
1557 #endif
1558                         legacy = (struct CACHED_PERMISSIONS_LEGACY*)ntfs_enter_cache(
1559                                 scx->vol->legacy_cache, GENERIC(&wanted),
1560                                 (cache_compare)leg_compare);
1561                         if (legacy) {
1562                                 cacheentry = &legacy->perm;
1563 #if POSIXACLS
1564                                 /*
1565                                  * give direct access to the cached pxdesc
1566                                  * in the permissions structure
1567                                  */
1568                                 cacheentry->pxdesc = legacy->variable;
1569 #endif
1570                         }
1571                 }
1572 #endif
1573         }
1574         return (cacheentry);
1575 }
1576
1577 /*
1578  *      Fetch owner, group and permission of a file, if cached
1579  *
1580  *      Beware : do not use the returned entry after a cache update :
1581  *      the cache may be relocated making the returned entry meaningless
1582  *
1583  *      returns the cache entry, or NULL if not available
1584  */
1585
1586 static struct CACHED_PERMISSIONS *fetch_cache(struct SECURITY_CONTEXT *scx,
1587                 ntfs_inode *ni)
1588 {
1589         struct CACHED_PERMISSIONS *cacheentry;
1590         struct PERMISSIONS_CACHE *pcache;
1591         u32 securindex;
1592         unsigned int index1;
1593         unsigned int index2;
1594
1595         /* cacheing is only possible if a security_id has been defined */
1596         cacheentry = (struct CACHED_PERMISSIONS*)NULL;
1597         if (test_nino_flag(ni, v3_Extensions)
1598            && (ni->security_id)) {
1599                 securindex = le32_to_cpu(ni->security_id);
1600                 index1 = securindex >> CACHE_PERMISSIONS_BITS;
1601                 index2 = securindex & ((1 << CACHE_PERMISSIONS_BITS) - 1);
1602                 pcache = *scx->pseccache;
1603                 if (pcache
1604                      && (pcache->head.last >= index1)
1605                      && pcache->cachetable[index1]) {
1606                         cacheentry = &pcache->cachetable[index1][index2];
1607                         /* reject if entry is not valid */
1608                         if (!cacheentry->valid)
1609                                 cacheentry = (struct CACHED_PERMISSIONS*)NULL;
1610                         else
1611                                 pcache->head.p_hits++;
1612                 if (pcache)
1613                         pcache->head.p_reads++;
1614                 }
1615         }
1616 #if CACHE_LEGACY_SIZE
1617         else {
1618                 cacheentry = (struct CACHED_PERMISSIONS*)NULL;
1619                 if (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY) {
1620                         struct CACHED_PERMISSIONS_LEGACY wanted;
1621                         struct CACHED_PERMISSIONS_LEGACY *legacy;
1622
1623                         wanted.mft_no = ni->mft_no;
1624                         wanted.variable = (void*)NULL;
1625                         wanted.varsize = 0;
1626                         legacy = (struct CACHED_PERMISSIONS_LEGACY*)ntfs_fetch_cache(
1627                                 scx->vol->legacy_cache, GENERIC(&wanted),
1628                                 (cache_compare)leg_compare);
1629                         if (legacy) cacheentry = &legacy->perm;
1630                 }
1631         }
1632 #endif
1633 #if POSIXACLS
1634         if (cacheentry && !cacheentry->pxdesc) {
1635                 ntfs_log_error("No Posix descriptor in cache\n");
1636                 cacheentry = (struct CACHED_PERMISSIONS*)NULL;
1637         }
1638 #endif
1639         return (cacheentry);
1640 }
1641
1642 /*
1643  *      Retrieve a security attribute from $Secure
1644  */
1645
1646 static char *retrievesecurityattr(ntfs_volume *vol, SII_INDEX_KEY id)
1647 {
1648         struct SII *psii;
1649         union {
1650                 struct {
1651                         le32 dataoffsl;
1652                         le32 dataoffsh;
1653                 } parts;
1654                 le64 all;
1655         } realign;
1656         int found;
1657         size_t size;
1658         size_t rdsize;
1659         s64 offs;
1660         ntfs_inode *ni;
1661         ntfs_index_context *xsii;
1662         char *securattr;
1663
1664         securattr = (char*)NULL;
1665         ni = vol->secure_ni;
1666         xsii = vol->secure_xsii;
1667         if (ni && xsii) {
1668                 ntfs_index_ctx_reinit(xsii);
1669                 found =
1670                     !ntfs_index_lookup((char*)&id,
1671                                        sizeof(SII_INDEX_KEY), xsii);
1672                 if (found) {
1673                         psii = (struct SII*)xsii->entry;
1674                         size =
1675                             (size_t) le32_to_cpu(psii->datasize)
1676                                  - sizeof(SECURITY_DESCRIPTOR_HEADER);
1677                         /* work around bad alignment problem */
1678                         realign.parts.dataoffsh = psii->dataoffsh;
1679                         realign.parts.dataoffsl = psii->dataoffsl;
1680                         offs = le64_to_cpu(realign.all)
1681                                 + sizeof(SECURITY_DESCRIPTOR_HEADER);
1682
1683                         securattr = (char*)ntfs_malloc(size);
1684                         if (securattr) {
1685                                 rdsize = ntfs_attr_data_read(
1686                                         ni, STREAM_SDS, 4,
1687                                         securattr, size, offs);
1688                                 if ((rdsize != size)
1689                                         || !ntfs_valid_descr(securattr,
1690                                                 rdsize)) {
1691                                         /* error to be logged by caller */
1692                                         free(securattr);
1693                                         securattr = (char*)NULL;
1694                                 }
1695                         }
1696                 } else
1697                         if (errno != ENOENT)
1698                                 ntfs_log_perror("Inconsistency in index $SII");
1699         }
1700         if (!securattr) {
1701                 ntfs_log_error("Failed to retrieve a security descriptor\n");
1702                 errno = EIO;
1703         }
1704         return (securattr);
1705 }
1706
1707 /*
1708  *              Get the security descriptor associated to a file
1709  *
1710  *      Either :
1711  *         - read the security descriptor attribute (v1.x format)
1712  *         - or find the descriptor in $Secure:$SDS (v3.x format)
1713  *
1714  *      in both case, sanity checks are done on the attribute and
1715  *      the descriptor can be assumed safe
1716  *
1717  *      The returned descriptor is dynamically allocated and has to be freed
1718  */
1719
1720 static char *getsecurityattr(ntfs_volume *vol, ntfs_inode *ni)
1721 {
1722         SII_INDEX_KEY securid;
1723         char *securattr;
1724         s64 readallsz;
1725
1726                 /*
1727                  * Warning : in some situations, after fixing by chkdsk,
1728                  * v3_Extensions are marked present (long standard informations)
1729                  * with a default security descriptor inserted in an
1730                  * attribute
1731                  */
1732         if (test_nino_flag(ni, v3_Extensions)
1733                         && vol->secure_ni && ni->security_id) {
1734                         /* get v3.x descriptor in $Secure */
1735                 securid.security_id = ni->security_id;
1736                 securattr = retrievesecurityattr(vol,securid);
1737                 if (!securattr)
1738                         ntfs_log_error("Bad security descriptor for 0x%lx\n",
1739                                         (long)le32_to_cpu(ni->security_id));
1740         } else {
1741                         /* get v1.x security attribute */
1742                 readallsz = 0;
1743                 securattr = ntfs_attr_readall(ni, AT_SECURITY_DESCRIPTOR,
1744                                 AT_UNNAMED, 0, &readallsz);
1745                 if (securattr && !ntfs_valid_descr(securattr, readallsz)) {
1746                         ntfs_log_error("Bad security descriptor for inode %lld\n",
1747                                 (long long)ni->mft_no);
1748                         free(securattr);
1749                         securattr = (char*)NULL;
1750                 }
1751         }
1752         if (!securattr) {
1753                         /*
1754                          * in some situations, there is no security
1755                          * descriptor, and chkdsk does not detect or fix
1756                          * anything. This could be a normal situation.
1757                          * When this happens, simulate a descriptor with
1758                          * minimum rights, so that a real descriptor can
1759                          * be created by chown or chmod
1760                          */
1761                 ntfs_log_error("No security descriptor found for inode %lld\n",
1762                                 (long long)ni->mft_no);
1763                 securattr = ntfs_build_descr(0, 0, adminsid, adminsid);
1764         }
1765         return (securattr);
1766 }
1767
1768 #if POSIXACLS
1769
1770 /*
1771  *              Determine which access types to a file are allowed
1772  *      according to the relation of current process to the file
1773  *
1774  *      Do not call if default_permissions is set
1775  */
1776
1777 static int access_check_posix(struct SECURITY_CONTEXT *scx,
1778                         struct POSIX_SECURITY *pxdesc, mode_t request,
1779                         uid_t uid, gid_t gid)
1780 {
1781         struct POSIX_ACE *pxace;
1782         int userperms;
1783         int groupperms;
1784         int mask;
1785         BOOL somegroup;
1786         BOOL needgroups;
1787         mode_t perms;
1788         int i;
1789
1790         perms = pxdesc->mode;
1791                                         /* owner and root access */
1792         if (!scx->uid || (uid == scx->uid)) {
1793                 if (!scx->uid) {
1794                                         /* root access if owner or other execution */
1795                         if (perms & 0101)
1796                                 perms = 07777;
1797                         else {
1798                                         /* root access if some group execution */
1799                                 groupperms = 0;
1800                                 mask = 7;
1801                                 for (i=pxdesc->acccnt-1; i>=0 ; i--) {
1802                                         pxace = &pxdesc->acl.ace[i];
1803                                         switch (pxace->tag) {
1804                                         case POSIX_ACL_USER_OBJ :
1805                                         case POSIX_ACL_GROUP_OBJ :
1806                                         case POSIX_ACL_GROUP :
1807                                                 groupperms |= pxace->perms;
1808                                                 break;
1809                                         case POSIX_ACL_MASK :
1810                                                 mask = pxace->perms & 7;
1811                                                 break;
1812                                         default :
1813                                                 break;
1814                                         }
1815                                 }
1816                                 perms = (groupperms & mask & 1) | 6;
1817                         }
1818                 } else
1819                         perms &= 07700;
1820         } else {
1821                                 /*
1822                                  * analyze designated users, get mask
1823                                  * and identify whether we need to check
1824                                  * the group memberships. The groups are
1825                                  * not needed when all groups have the
1826                                  * same permissions as other for the
1827                                  * requested modes.
1828                                  */
1829                 userperms = -1;
1830                 groupperms = -1;
1831                 needgroups = FALSE;
1832                 mask = 7;
1833                 for (i=pxdesc->acccnt-1; i>=0 ; i--) {
1834                         pxace = &pxdesc->acl.ace[i];
1835                         switch (pxace->tag) {
1836                         case POSIX_ACL_USER :
1837                                 if ((uid_t)pxace->id == scx->uid)
1838                                         userperms = pxace->perms;
1839                                 break;
1840                         case POSIX_ACL_MASK :
1841                                 mask = pxace->perms & 7;
1842                                 break;
1843                         case POSIX_ACL_GROUP_OBJ :
1844                         case POSIX_ACL_GROUP :
1845                                 if (((pxace->perms & mask) ^ perms)
1846                                     & (request >> 6) & 7)
1847                                         needgroups = TRUE;
1848                                 break;
1849                         default :
1850                                 break;
1851                         }
1852                 }
1853                                         /* designated users */
1854                 if (userperms >= 0)
1855                         perms = (perms & 07000) + (userperms & mask);
1856                 else if (!needgroups)
1857                                 perms &= 07007;
1858                 else {
1859                                         /* owning group */
1860                         if (!(~(perms >> 3) & request & mask)
1861                             && ((gid == scx->gid)
1862                                 || groupmember(scx, scx->uid, gid)))
1863                                 perms &= 07070;
1864                         else {
1865                                         /* other groups */
1866                                 groupperms = -1;
1867                                 somegroup = FALSE;
1868                                 for (i=pxdesc->acccnt-1; i>=0 ; i--) {
1869                                         pxace = &pxdesc->acl.ace[i];
1870                                         if ((pxace->tag == POSIX_ACL_GROUP)
1871                                             && groupmember(scx, uid, pxace->id)) {
1872                                                 if (!(~pxace->perms & request & mask))
1873                                                         groupperms = pxace->perms;
1874                                                 somegroup = TRUE;
1875                                         }
1876                                 }
1877                                 if (groupperms >= 0)
1878                                         perms = (perms & 07000) + (groupperms & mask);
1879                                 else
1880                                         if (somegroup)
1881                                                 perms = 0;
1882                                         else
1883                                                 perms &= 07007;
1884                         }
1885                 }
1886         }
1887         return (perms);
1888 }
1889
1890 /*
1891  *              Get permissions to access a file
1892  *      Takes into account the relation of user to file (owner, group, ...)
1893  *      Do no use as mode of the file
1894  *      Do no call if default_permissions is set
1895  *
1896  *      returns -1 if there is a problem
1897  */
1898
1899 static int ntfs_get_perm(struct SECURITY_CONTEXT *scx,
1900                  ntfs_inode * ni, mode_t request)
1901 {
1902         const SECURITY_DESCRIPTOR_RELATIVE *phead;
1903         const struct CACHED_PERMISSIONS *cached;
1904         char *securattr;
1905         const SID *usid;        /* owner of file/directory */
1906         const SID *gsid;        /* group of file/directory */
1907         uid_t uid;
1908         gid_t gid;
1909         int perm;
1910         BOOL isdir;
1911         struct POSIX_SECURITY *pxdesc;
1912
1913         if (!scx->mapping[MAPUSERS])
1914                 perm = 07777;
1915         else {
1916                 /* check whether available in cache */
1917                 cached = fetch_cache(scx,ni);
1918                 if (cached) {
1919                         uid = cached->uid;
1920                         gid = cached->gid;
1921                         perm = access_check_posix(scx,cached->pxdesc,request,uid,gid);
1922                 } else {
1923                         perm = 0;       /* default to no permission */
1924                         isdir = (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY)
1925                                 != const_cpu_to_le16(0);
1926                         securattr = getsecurityattr(scx->vol, ni);
1927                         if (securattr) {
1928                                 phead = (const SECURITY_DESCRIPTOR_RELATIVE*)
1929                                         securattr;
1930                                 gsid = (const SID*)&
1931                                            securattr[le32_to_cpu(phead->group)];
1932                                 gid = ntfs_find_group(scx->mapping[MAPGROUPS],gsid);
1933 #if OWNERFROMACL
1934                                 usid = ntfs_acl_owner(securattr);
1935                                 pxdesc = ntfs_build_permissions_posix(scx->mapping,securattr,
1936                                                  usid, gsid, isdir);
1937                                 if (pxdesc)
1938                                         perm = pxdesc->mode & 07777;
1939                                 else
1940                                         perm = -1;
1941                                 uid = ntfs_find_user(scx->mapping[MAPUSERS],usid);
1942 #else
1943                                 usid = (const SID*)&
1944                                             securattr[le32_to_cpu(phead->owner)];
1945                                 pxdesc = ntfs_build_permissions_posix(scx,securattr,
1946                                                  usid, gsid, isdir);
1947                                 if (pxdesc)
1948                                         perm = pxdesc->mode & 07777;
1949                                 else
1950                                         perm = -1;
1951                                 if (!perm && ntfs_same_sid(usid, adminsid)) {
1952                                         uid = find_tenant(scx, securattr);
1953                                         if (uid)
1954                                                 perm = 0700;
1955                                 } else
1956                                         uid = ntfs_find_user(scx->mapping[MAPUSERS],usid);
1957 #endif
1958                                 /*
1959                                  *  Create a security id if there were none
1960                                  * and upgrade option is selected
1961                                  */
1962                                 if (!test_nino_flag(ni, v3_Extensions)
1963                                    && (perm >= 0)
1964                                    && (scx->vol->secure_flags
1965                                      & (1 << SECURITY_ADDSECURIDS))) {
1966                                         upgrade_secur_desc(scx->vol,
1967                                                 securattr, ni);
1968                                         /*
1969                                          * fetch owner and group for cacheing
1970                                          * if there is a securid
1971                                          */
1972                                 }
1973                                 if (test_nino_flag(ni, v3_Extensions)
1974                                     && (perm >= 0)) {
1975                                         enter_cache(scx, ni, uid,
1976                                                         gid, pxdesc);
1977                                 }
1978                                 if (pxdesc) {
1979                                         perm = access_check_posix(scx,pxdesc,request,uid,gid);
1980                                         free(pxdesc);
1981                                 }
1982                                 free(securattr);
1983                         } else {
1984                                 perm = -1;
1985                                 uid = gid = 0;
1986                         }
1987                 }
1988         }
1989         return (perm);
1990 }
1991
1992 /*
1993  *              Get a Posix ACL
1994  *
1995  *      returns size or -errno if there is a problem
1996  *      if size was too small, no copy is done and errno is not set,
1997  *      the caller is expected to issue a new call
1998  */
1999
2000 int ntfs_get_posix_acl(struct SECURITY_CONTEXT *scx, ntfs_inode *ni,
2001                         const char *name, char *value, size_t size) 
2002 {
2003         const SECURITY_DESCRIPTOR_RELATIVE *phead;
2004         struct POSIX_SECURITY *pxdesc;
2005         const struct CACHED_PERMISSIONS *cached;
2006         char *securattr;
2007         const SID *usid;        /* owner of file/directory */
2008         const SID *gsid;        /* group of file/directory */
2009         uid_t uid;
2010         gid_t gid;
2011         BOOL isdir;
2012         size_t outsize;
2013
2014         outsize = 0;    /* default to error */
2015         if (!scx->mapping[MAPUSERS])
2016                 errno = ENOTSUP;
2017         else {
2018                         /* check whether available in cache */
2019                 cached = fetch_cache(scx,ni);
2020                 if (cached)
2021                         pxdesc = cached->pxdesc;
2022                 else {
2023                         securattr = getsecurityattr(scx->vol, ni);
2024                         isdir = (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY)
2025                                 != const_cpu_to_le16(0);
2026                         if (securattr) {
2027                                 phead =
2028                                     (const SECURITY_DESCRIPTOR_RELATIVE*)
2029                                                 securattr;
2030                                 gsid = (const SID*)&
2031                                           securattr[le32_to_cpu(phead->group)];
2032 #if OWNERFROMACL
2033                                 usid = ntfs_acl_owner(securattr);
2034 #else
2035                                 usid = (const SID*)&
2036                                           securattr[le32_to_cpu(phead->owner)];
2037 #endif
2038                                 pxdesc = ntfs_build_permissions_posix(scx->mapping,securattr,
2039                                           usid, gsid, isdir);
2040
2041                                         /*
2042                                          * fetch owner and group for cacheing
2043                                          */
2044                                 if (pxdesc) {
2045                                 /*
2046                                  *  Create a security id if there were none
2047                                  * and upgrade option is selected
2048                                  */
2049                                         if (!test_nino_flag(ni, v3_Extensions)
2050                                            && (scx->vol->secure_flags
2051                                              & (1 << SECURITY_ADDSECURIDS))) {
2052                                                 upgrade_secur_desc(scx->vol,
2053                                                          securattr, ni);
2054                                         }
2055 #if OWNERFROMACL
2056                                         uid = ntfs_find_user(scx->mapping[MAPUSERS],usid);
2057 #else
2058                                         if (!(pxdesc->mode & 07777)
2059                                             && ntfs_same_sid(usid, adminsid)) {
2060                                                 uid = find_tenant(scx,
2061                                                                 securattr);
2062                                         } else
2063                                                 uid = ntfs_find_user(scx->mapping[MAPUSERS],usid);
2064 #endif
2065                                         gid = ntfs_find_group(scx->mapping[MAPGROUPS],gsid);
2066                                         if (pxdesc->tagsset & POSIX_ACL_EXTENSIONS)
2067                                         enter_cache(scx, ni, uid,
2068                                                         gid, pxdesc);
2069                                 }
2070                                 free(securattr);
2071                         } else
2072                                 pxdesc = (struct POSIX_SECURITY*)NULL;
2073                 }
2074
2075                 if (pxdesc) {
2076                         if (ntfs_valid_posix(pxdesc)) {
2077                                 if (!strcmp(name,"system.posix_acl_default")) {
2078                                         if (ni->mrec->flags
2079                                                     & MFT_RECORD_IS_DIRECTORY)
2080                                                 outsize = sizeof(struct POSIX_ACL)
2081                                                         + pxdesc->defcnt*sizeof(struct POSIX_ACE);
2082                                         else {
2083                                         /*
2084                                          * getting default ACL from plain file :
2085                                          * return EACCES if size > 0 as
2086                                          * indicated in the man, but return ok
2087                                          * if size == 0, so that ls does not
2088                                          * display an error
2089                                          */
2090                                                 if (size > 0) {
2091                                                         outsize = 0;
2092                                                         errno = EACCES;
2093                                                 } else
2094                                                         outsize = sizeof(struct POSIX_ACL);
2095                                         }
2096                                         if (outsize && (outsize <= size)) {
2097                                                 memcpy(value,&pxdesc->acl,sizeof(struct POSIX_ACL));
2098                                                 memcpy(&value[sizeof(struct POSIX_ACL)],
2099                                                         &pxdesc->acl.ace[pxdesc->firstdef],
2100                                                         outsize-sizeof(struct POSIX_ACL));
2101                                         }
2102                                 } else {
2103                                         outsize = sizeof(struct POSIX_ACL)
2104                                                 + pxdesc->acccnt*sizeof(struct POSIX_ACE);
2105                                         if (outsize <= size)
2106                                                 memcpy(value,&pxdesc->acl,outsize);
2107                                 }
2108                         } else {
2109                                 outsize = 0;
2110                                 errno = EIO;
2111                                 ntfs_log_error("Invalid Posix ACL built\n");
2112                         }
2113                         if (!cached)
2114                                 free(pxdesc);
2115                 } else
2116                         outsize = 0;
2117         }
2118         return (outsize ? (int)outsize : -errno);
2119 }
2120
2121 #else /* POSIXACLS */
2122
2123
2124 /*
2125  *              Get permissions to access a file
2126  *      Takes into account the relation of user to file (owner, group, ...)
2127  *      Do no use as mode of the file
2128  *
2129  *      returns -1 if there is a problem
2130  */
2131
2132 static int ntfs_get_perm(struct SECURITY_CONTEXT *scx,
2133                 ntfs_inode *ni, mode_t request)
2134 {
2135         const SECURITY_DESCRIPTOR_RELATIVE *phead;
2136         const struct CACHED_PERMISSIONS *cached;
2137         char *securattr;
2138         const SID *usid;        /* owner of file/directory */
2139         const SID *gsid;        /* group of file/directory */
2140         BOOL isdir;
2141         uid_t uid;
2142         gid_t gid;
2143         int perm;
2144
2145         if (!scx->mapping[MAPUSERS] || (!scx->uid && !(request & S_IEXEC)))
2146                 perm = 07777;
2147         else {
2148                 /* check whether available in cache */
2149                 cached = fetch_cache(scx,ni);
2150                 if (cached) {
2151                         perm = cached->mode;
2152                         uid = cached->uid;
2153                         gid = cached->gid;
2154                 } else {
2155                         perm = 0;       /* default to no permission */
2156                         isdir = (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY)
2157                                 != const_cpu_to_le16(0);
2158                         securattr = getsecurityattr(scx->vol, ni);
2159                         if (securattr) {
2160                                 phead = (const SECURITY_DESCRIPTOR_RELATIVE*)
2161                                         securattr;
2162                                 gsid = (const SID*)&
2163                                            securattr[le32_to_cpu(phead->group)];
2164                                 gid = ntfs_find_group(scx->mapping[MAPGROUPS],gsid);
2165 #if OWNERFROMACL
2166                                 usid = ntfs_acl_owner(securattr);
2167                                 perm = ntfs_build_permissions(securattr,
2168                                                  usid, gsid, isdir);
2169                                 uid = ntfs_find_user(scx->mapping[MAPUSERS],usid);
2170 #else
2171                                 usid = (const SID*)&
2172                                             securattr[le32_to_cpu(phead->owner)];
2173                                 perm = ntfs_build_permissions(securattr,
2174                                                  usid, gsid, isdir);
2175                                 if (!perm && ntfs_same_sid(usid, adminsid)) {
2176                                         uid = find_tenant(scx, securattr);
2177                                         if (uid)
2178                                                 perm = 0700;
2179                                 } else
2180                                         uid = ntfs_find_user(scx->mapping[MAPUSERS],usid);
2181 #endif
2182                                 /*
2183                                  *  Create a security id if there were none
2184                                  * and upgrade option is selected
2185                                  */
2186                                 if (!test_nino_flag(ni, v3_Extensions)
2187                                    && (perm >= 0)
2188                                    && (scx->vol->secure_flags
2189                                      & (1 << SECURITY_ADDSECURIDS))) {
2190                                         upgrade_secur_desc(scx->vol,
2191                                                 securattr, ni);
2192                                         /*
2193                                          * fetch owner and group for cacheing
2194                                          * if there is a securid
2195                                          */
2196                                 }
2197                                 if (test_nino_flag(ni, v3_Extensions)
2198                                     && (perm >= 0)) {
2199                                         enter_cache(scx, ni, uid,
2200                                                         gid, perm);
2201                                 }
2202                                 free(securattr);
2203                         } else {
2204                                 perm = -1;
2205                                 uid = gid = 0;
2206                         }
2207                 }
2208                 if (perm >= 0) {
2209                         if (!scx->uid) {
2210                                 /* root access and execution */
2211                                 if (perm & 0111)
2212                                         perm = 07777;
2213                                 else
2214                                         perm = 0;
2215                         } else
2216                                 if (uid == scx->uid)
2217                                         perm &= 07700;
2218                                 else
2219                                 /*
2220                                  * avoid checking group membership
2221                                  * when the requested perms for group
2222                                  * are the same as perms for other
2223                                  */
2224                                         if ((gid == scx->gid)
2225                                           || ((((perm >> 3) ^ perm)
2226                                                 & (request >> 6) & 7)
2227                                             && groupmember(scx, scx->uid, gid)))
2228                                                 perm &= 07070;
2229                                         else
2230                                                 perm &= 07007;
2231                 }
2232         }
2233         return (perm);
2234 }
2235
2236 #endif /* POSIXACLS */
2237
2238 /*
2239  *              Get an NTFS ACL
2240  *
2241  *      Returns size or -errno if there is a problem
2242  *      if size was too small, no copy is done and errno is not set,
2243  *      the caller is expected to issue a new call
2244  */
2245
2246 int ntfs_get_ntfs_acl(struct SECURITY_CONTEXT *scx, ntfs_inode *ni,
2247                         char *value, size_t size)
2248 {
2249         char *securattr;
2250         size_t outsize;
2251
2252         outsize = 0;    /* default to no data and no error */
2253         securattr = getsecurityattr(scx->vol, ni);
2254         if (securattr) {
2255                 outsize = ntfs_attr_size(securattr);
2256                 if (outsize <= size) {
2257                         memcpy(value,securattr,outsize);
2258                 }
2259                 free(securattr);
2260         }
2261         return (outsize ? (int)outsize : -errno);
2262 }
2263
2264 /*
2265  *              Get owner, group and permissions in an stat structure
2266  *      returns permissions, or -1 if there is a problem
2267  */
2268
2269 int ntfs_get_owner_mode(struct SECURITY_CONTEXT *scx,
2270                 ntfs_inode * ni, struct stat *stbuf)
2271 {
2272         const SECURITY_DESCRIPTOR_RELATIVE *phead;
2273         char *securattr;
2274         const SID *usid;        /* owner of file/directory */
2275         const SID *gsid;        /* group of file/directory */
2276         const struct CACHED_PERMISSIONS *cached;
2277         int perm;
2278         BOOL isdir;
2279 #if POSIXACLS
2280         struct POSIX_SECURITY *pxdesc;
2281 #endif
2282
2283         if (!scx->mapping[MAPUSERS])
2284                 perm = 07777;
2285         else {
2286                         /* check whether available in cache */
2287                 cached = fetch_cache(scx,ni);
2288                 if (cached) {
2289                         perm = cached->mode;
2290                         stbuf->st_uid = cached->uid;
2291                         stbuf->st_gid = cached->gid;
2292                         stbuf->st_mode = (stbuf->st_mode & ~07777) + perm;
2293                 } else {
2294                         perm = -1;      /* default to error */
2295                         isdir = (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY)
2296                                 != const_cpu_to_le16(0);
2297                         securattr = getsecurityattr(scx->vol, ni);
2298                         if (securattr) {
2299                                 phead =
2300                                     (const SECURITY_DESCRIPTOR_RELATIVE*)
2301                                                 securattr;
2302                                 gsid = (const SID*)&
2303                                           securattr[le32_to_cpu(phead->group)];
2304 #if OWNERFROMACL
2305                                 usid = ntfs_acl_owner(securattr);
2306 #else
2307                                 usid = (const SID*)&
2308                                           securattr[le32_to_cpu(phead->owner)];
2309 #endif
2310 #if POSIXACLS
2311                                 pxdesc = ntfs_build_permissions_posix(scx->mapping, securattr,
2312                                           usid, gsid, isdir);
2313                                 if (pxdesc)
2314                                         perm = pxdesc->mode & 07777;
2315                                 else
2316                                         perm = -1;
2317 #else
2318                                 perm = ntfs_build_permissions(securattr,
2319                                           usid, gsid, isdir);
2320 #endif
2321                                         /*
2322                                          * fetch owner and group for cacheing
2323                                          */
2324                                 if (perm >= 0) {
2325                                 /*
2326                                  *  Create a security id if there were none
2327                                  * and upgrade option is selected
2328                                  */
2329                                         if (!test_nino_flag(ni, v3_Extensions)
2330                                            && (scx->vol->secure_flags
2331                                              & (1 << SECURITY_ADDSECURIDS))) {
2332                                                 upgrade_secur_desc(scx->vol,
2333                                                          securattr, ni);
2334                                         }
2335 #if OWNERFROMACL
2336                                         stbuf->st_uid = ntfs_find_user(scx->mapping[MAPUSERS],usid);
2337 #else
2338                                         if (!perm && ntfs_same_sid(usid, adminsid)) {
2339                                                 stbuf->st_uid = 
2340                                                         find_tenant(scx,
2341                                                                 securattr);
2342                                                 if (stbuf->st_uid)
2343                                                         perm = 0700;
2344                                         } else
2345                                                 stbuf->st_uid = ntfs_find_user(scx->mapping[MAPUSERS],usid);
2346 #endif
2347                                         stbuf->st_gid = ntfs_find_group(scx->mapping[MAPGROUPS],gsid);
2348                                         stbuf->st_mode =
2349                                             (stbuf->st_mode & ~07777) + perm;
2350 #if POSIXACLS
2351                                         enter_cache(scx, ni, stbuf->st_uid,
2352                                                 stbuf->st_gid, pxdesc);
2353                                         free(pxdesc);
2354 #else
2355                                         enter_cache(scx, ni, stbuf->st_uid,
2356                                                 stbuf->st_gid, perm);
2357 #endif
2358                                 }
2359                                 free(securattr);
2360                         }
2361                 }
2362         }
2363         return (perm);
2364 }
2365
2366 #if POSIXACLS
2367
2368 /*
2369  *              Get the base for a Posix inheritance and
2370  *      build an inherited Posix descriptor
2371  */
2372
2373 static struct POSIX_SECURITY *inherit_posix(struct SECURITY_CONTEXT *scx,
2374                         ntfs_inode *dir_ni, mode_t mode, BOOL isdir)
2375 {
2376         const struct CACHED_PERMISSIONS *cached;
2377         const SECURITY_DESCRIPTOR_RELATIVE *phead;
2378         struct POSIX_SECURITY *pxdesc;
2379         struct POSIX_SECURITY *pydesc;
2380         char *securattr;
2381         const SID *usid;
2382         const SID *gsid;
2383         uid_t uid;
2384         gid_t gid;
2385
2386         pydesc = (struct POSIX_SECURITY*)NULL;
2387                 /* check whether parent directory is available in cache */
2388         cached = fetch_cache(scx,dir_ni);
2389         if (cached) {
2390                 uid = cached->uid;
2391                 gid = cached->gid;
2392                 pxdesc = cached->pxdesc;
2393                 if (pxdesc) {
2394                         pydesc = ntfs_build_inherited_posix(pxdesc,mode,
2395                                         scx->umask,isdir);
2396                 }
2397         } else {
2398                 securattr = getsecurityattr(scx->vol, dir_ni);
2399                 if (securattr) {
2400                         phead = (const SECURITY_DESCRIPTOR_RELATIVE*)
2401                                 securattr;
2402                         gsid = (const SID*)&
2403                                    securattr[le32_to_cpu(phead->group)];
2404                         gid = ntfs_find_group(scx->mapping[MAPGROUPS],gsid);
2405 #if OWNERFROMACL
2406                         usid = ntfs_acl_owner(securattr);
2407                         pxdesc = ntfs_build_permissions_posix(scx->mapping,securattr,
2408                                                  usid, gsid, TRUE);
2409                         uid = ntfs_find_user(scx->mapping[MAPUSERS],usid);
2410 #else
2411                         usid = (const SID*)&
2412                                     securattr[le32_to_cpu(phead->owner)];
2413                         pxdesc = ntfs_build_permissions_posix(scx->mapping,securattr,
2414                                                  usid, gsid, TRUE);
2415                         if (pxdesc && ntfs_same_sid(usid, adminsid)) {
2416                                 uid = find_tenant(scx, securattr);
2417                         } else
2418                                 uid = ntfs_find_user(scx->mapping[MAPUSERS],usid);
2419 #endif
2420                         if (pxdesc) {
2421                                 /*
2422                                  *  Create a security id if there were none
2423                                  * and upgrade option is selected
2424                                  */
2425                                 if (!test_nino_flag(dir_ni, v3_Extensions)
2426                                    && (scx->vol->secure_flags
2427                                      & (1 << SECURITY_ADDSECURIDS))) {
2428                                         upgrade_secur_desc(scx->vol,
2429                                                 securattr, dir_ni);
2430                                         /*
2431                                          * fetch owner and group for cacheing
2432                                          * if there is a securid
2433                                          */
2434                                 }
2435                                 if (test_nino_flag(dir_ni, v3_Extensions)) {
2436                                         enter_cache(scx, dir_ni, uid,
2437                                                         gid, pxdesc);
2438                                 }
2439                                 pydesc = ntfs_build_inherited_posix(pxdesc,
2440                                         mode, scx->umask, isdir);
2441                                 free(pxdesc);
2442                         }
2443                         free(securattr);
2444                 }
2445         }
2446         return (pydesc);
2447 }
2448
2449 /*
2450  *              Allocate a security_id for a file being created
2451  *      
2452  *      Returns zero if not possible (NTFS v3.x required)
2453  */
2454
2455 le32 ntfs_alloc_securid(struct SECURITY_CONTEXT *scx,
2456                 uid_t uid, gid_t gid, ntfs_inode *dir_ni,
2457                 mode_t mode, BOOL isdir)
2458 {
2459 #if !FORCE_FORMAT_v1x
2460         const struct CACHED_SECURID *cached;
2461         struct CACHED_SECURID wanted;
2462         struct POSIX_SECURITY *pxdesc;
2463         char *newattr;
2464         int newattrsz;
2465         const SID *usid;
2466         const SID *gsid;
2467         BIGSID defusid;
2468         BIGSID defgsid;
2469         le32 securid;
2470 #endif
2471
2472         securid = const_cpu_to_le32(0);
2473
2474 #if !FORCE_FORMAT_v1x
2475
2476         pxdesc = inherit_posix(scx, dir_ni, mode, isdir);
2477         if (pxdesc) {
2478                 /* check whether target securid is known in cache */
2479
2480                 wanted.uid = uid;
2481                 wanted.gid = gid;
2482                 wanted.dmode = pxdesc->mode & mode & 07777;
2483                 if (isdir) wanted.dmode |= 0x10000;
2484                 wanted.variable = (void*)pxdesc;
2485                 wanted.varsize = sizeof(struct POSIX_SECURITY)
2486                                 + (pxdesc->acccnt + pxdesc->defcnt)*sizeof(struct POSIX_ACE);
2487                 cached = (const struct CACHED_SECURID*)ntfs_fetch_cache(
2488                                 scx->vol->securid_cache, GENERIC(&wanted),
2489                                 (cache_compare)compare);
2490                         /* quite simple, if we are lucky */
2491                 if (cached)
2492                         securid = cached->securid;
2493
2494                         /* not in cache : make sure we can create ids */
2495
2496                 if (!cached && (scx->vol->major_ver >= 3)) {
2497                         usid = ntfs_find_usid(scx->mapping[MAPUSERS],uid,(SID*)&defusid);
2498                         gsid = ntfs_find_gsid(scx->mapping[MAPGROUPS],gid,(SID*)&defgsid);
2499                         if (!usid || !gsid) {
2500                                 ntfs_log_error("File created by an unmapped user/group %d/%d\n",
2501                                                 (int)uid, (int)gid);
2502                                 usid = gsid = adminsid;
2503                         }
2504                         newattr = ntfs_build_descr_posix(scx->mapping, pxdesc,
2505                                         isdir, usid, gsid);
2506                         if (newattr) {
2507                                 newattrsz = ntfs_attr_size(newattr);
2508                                 securid = setsecurityattr(scx->vol,
2509                                         (const SECURITY_DESCRIPTOR_RELATIVE*)newattr,
2510                                         newattrsz);
2511                                 if (securid) {
2512                                         /* update cache, for subsequent use */
2513                                         wanted.securid = securid;
2514                                         ntfs_enter_cache(scx->vol->securid_cache,
2515                                                         GENERIC(&wanted),
2516                                                         (cache_compare)compare);
2517                                 }
2518                                 free(newattr);
2519                         } else {
2520                                 /*
2521                                  * could not build new security attribute
2522                                  * errno set by ntfs_build_descr()
2523                                  */
2524                         }
2525                 }
2526         free(pxdesc);
2527         }
2528 #endif
2529         return (securid);
2530 }
2531
2532 /*
2533  *              Apply Posix inheritance to a newly created file
2534  *      (for NTFS 1.x only : no securid)
2535  */
2536
2537 int ntfs_set_inherited_posix(struct SECURITY_CONTEXT *scx,
2538                 ntfs_inode *ni, uid_t uid, gid_t gid,
2539                 ntfs_inode *dir_ni, mode_t mode)
2540 {
2541         struct POSIX_SECURITY *pxdesc;
2542         char *newattr;
2543         const SID *usid;
2544         const SID *gsid;
2545         BIGSID defusid;
2546         BIGSID defgsid;
2547         BOOL isdir;
2548         int res;
2549
2550         res = -1;
2551         isdir = (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY) != const_cpu_to_le16(0);
2552         pxdesc = inherit_posix(scx, dir_ni, mode, isdir);
2553         if (pxdesc) {
2554                 usid = ntfs_find_usid(scx->mapping[MAPUSERS],uid,(SID*)&defusid);
2555                 gsid = ntfs_find_gsid(scx->mapping[MAPGROUPS],gid,(SID*)&defgsid);
2556                 if (!usid || !gsid) {
2557                         ntfs_log_error("File created by an unmapped user/group %d/%d\n",
2558                                         (int)uid, (int)gid);
2559                         usid = gsid = adminsid;
2560                 }
2561                 newattr = ntfs_build_descr_posix(scx->mapping, pxdesc,
2562                                         isdir, usid, gsid);
2563                 if (newattr) {
2564                                 /* Adjust Windows read-only flag */
2565                         res = update_secur_descr(scx->vol, newattr, ni);
2566                         if (!res && !isdir) {
2567                                 if (mode & S_IWUSR)
2568                                         ni->flags &= ~FILE_ATTR_READONLY;
2569                                 else
2570                                         ni->flags |= FILE_ATTR_READONLY;
2571                         }
2572 #if CACHE_LEGACY_SIZE
2573                         /* also invalidate legacy cache */
2574                         if (isdir && !ni->security_id) {
2575                                 struct CACHED_PERMISSIONS_LEGACY legacy;
2576
2577                                 legacy.mft_no = ni->mft_no;
2578                                 legacy.variable = pxdesc;
2579                                 legacy.varsize = sizeof(struct POSIX_SECURITY)
2580                                         + (pxdesc->acccnt + pxdesc->defcnt)*sizeof(struct POSIX_ACE);
2581                                 ntfs_invalidate_cache(scx->vol->legacy_cache,
2582                                                 GENERIC(&legacy),
2583                                                 (cache_compare)leg_compare,0);
2584                         }
2585 #endif
2586                         free(newattr);
2587
2588                 } else {
2589                         /*
2590                          * could not build new security attribute
2591                          * errno set by ntfs_build_descr()
2592                          */
2593                 }
2594         }
2595         return (res);
2596 }
2597
2598 #else
2599
2600 le32 ntfs_alloc_securid(struct SECURITY_CONTEXT *scx,
2601                 uid_t uid, gid_t gid, mode_t mode, BOOL isdir)
2602 {
2603 #if !FORCE_FORMAT_v1x
2604         const struct CACHED_SECURID *cached;
2605         struct CACHED_SECURID wanted;
2606         char *newattr;
2607         int newattrsz;
2608         const SID *usid;
2609         const SID *gsid;
2610         BIGSID defusid;
2611         BIGSID defgsid;
2612         le32 securid;
2613 #endif
2614
2615         securid = const_cpu_to_le32(0);
2616
2617 #if !FORCE_FORMAT_v1x
2618                 /* check whether target securid is known in cache */
2619
2620         wanted.uid = uid;
2621         wanted.gid = gid;
2622         wanted.dmode = mode & 07777;
2623         if (isdir) wanted.dmode |= 0x10000;
2624         wanted.variable = (void*)NULL;
2625         wanted.varsize = 0;
2626         cached = (const struct CACHED_SECURID*)ntfs_fetch_cache(
2627                         scx->vol->securid_cache, GENERIC(&wanted),
2628                         (cache_compare)compare);
2629                 /* quite simple, if we are lucky */
2630         if (cached)
2631                 securid = cached->securid;
2632
2633                 /* not in cache : make sure we can create ids */
2634
2635         if (!cached && (scx->vol->major_ver >= 3)) {
2636                 usid = ntfs_find_usid(scx->mapping[MAPUSERS],uid,(SID*)&defusid);
2637                 gsid = ntfs_find_gsid(scx->mapping[MAPGROUPS],gid,(SID*)&defgsid);
2638                 if (!usid || !gsid) {
2639                         ntfs_log_error("File created by an unmapped user/group %d/%d\n",
2640                                         (int)uid, (int)gid);
2641                         usid = gsid = adminsid;
2642                 }
2643                 newattr = ntfs_build_descr(mode, isdir, usid, gsid);
2644                 if (newattr) {
2645                         newattrsz = ntfs_attr_size(newattr);
2646                         securid = setsecurityattr(scx->vol,
2647                                 (const SECURITY_DESCRIPTOR_RELATIVE*)newattr,
2648                                 newattrsz);
2649                         if (securid) {
2650                                 /* update cache, for subsequent use */
2651                                 wanted.securid = securid;
2652                                 ntfs_enter_cache(scx->vol->securid_cache,
2653                                                 GENERIC(&wanted),
2654                                                 (cache_compare)compare);
2655                         }
2656                         free(newattr);
2657                 } else {
2658                         /*
2659                          * could not build new security attribute
2660                          * errno set by ntfs_build_descr()
2661                          */
2662                 }
2663         }
2664 #endif
2665         return (securid);
2666 }
2667
2668 #endif
2669
2670 /*
2671  *              Update ownership and mode of a file, reusing an existing
2672  *      security descriptor when possible
2673  *      
2674  *      Returns zero if successful
2675  */
2676
2677 #if POSIXACLS
2678 int ntfs_set_owner_mode(struct SECURITY_CONTEXT *scx, ntfs_inode *ni,
2679                 uid_t uid, gid_t gid, mode_t mode,
2680                 struct POSIX_SECURITY *pxdesc)
2681 #else
2682 int ntfs_set_owner_mode(struct SECURITY_CONTEXT *scx, ntfs_inode *ni,
2683                 uid_t uid, gid_t gid, mode_t mode)
2684 #endif
2685 {
2686         int res;
2687         const struct CACHED_SECURID *cached;
2688         struct CACHED_SECURID wanted;
2689         char *newattr;
2690         const SID *usid;
2691         const SID *gsid;
2692         BIGSID defusid;
2693         BIGSID defgsid;
2694         BOOL isdir;
2695
2696         res = 0;
2697
2698                 /* check whether target securid is known in cache */
2699
2700         isdir = (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY) != const_cpu_to_le16(0);
2701         wanted.uid = uid;
2702         wanted.gid = gid;
2703         wanted.dmode = mode & 07777;
2704         if (isdir) wanted.dmode |= 0x10000;
2705 #if POSIXACLS
2706         wanted.variable = (void*)pxdesc;
2707         if (pxdesc)
2708                 wanted.varsize = sizeof(struct POSIX_SECURITY)
2709                         + (pxdesc->acccnt + pxdesc->defcnt)*sizeof(struct POSIX_ACE);
2710         else
2711                 wanted.varsize = 0;
2712 #else
2713         wanted.variable = (void*)NULL;
2714         wanted.varsize = 0;
2715 #endif
2716         if (test_nino_flag(ni, v3_Extensions)) {
2717                 cached = (const struct CACHED_SECURID*)ntfs_fetch_cache(
2718                                 scx->vol->securid_cache, GENERIC(&wanted),
2719                                 (cache_compare)compare);
2720                         /* quite simple, if we are lucky */
2721                 if (cached) {
2722                         ni->security_id = cached->securid;
2723                         NInoSetDirty(ni);
2724                 }
2725         } else cached = (struct CACHED_SECURID*)NULL;
2726
2727         if (!cached) {
2728                         /*
2729                          * Do not use usid and gsid from former attributes,
2730                          * but recompute them to get repeatable results
2731                          * which can be kept in cache.
2732                          */
2733                 usid = ntfs_find_usid(scx->mapping[MAPUSERS],uid,(SID*)&defusid);
2734                 gsid = ntfs_find_gsid(scx->mapping[MAPGROUPS],gid,(SID*)&defgsid);
2735                 if (!usid || !gsid) {
2736                         ntfs_log_error("File made owned by an unmapped user/group %d/%d\n",
2737                                 uid, gid);
2738                         usid = gsid = adminsid;
2739                 }
2740 #if POSIXACLS
2741                 if (pxdesc)
2742                         newattr = ntfs_build_descr_posix(scx->mapping, pxdesc,
2743                                          isdir, usid, gsid);
2744                 else
2745                         newattr = ntfs_build_descr(mode,
2746                                          isdir, usid, gsid);
2747 #else
2748                 newattr = ntfs_build_descr(mode,
2749                                          isdir, usid, gsid);
2750 #endif
2751                 if (newattr) {
2752                         res = update_secur_descr(scx->vol, newattr, ni);
2753                         if (!res) {
2754                                 /* adjust Windows read-only flag */
2755                                 if (!isdir) {
2756                                         if (mode & S_IWUSR)
2757                                                 ni->flags &= ~FILE_ATTR_READONLY;
2758                                         else
2759                                                 ni->flags |= FILE_ATTR_READONLY;
2760                                         NInoFileNameSetDirty(ni);
2761                                 }
2762                                 /* update cache, for subsequent use */
2763                                 if (test_nino_flag(ni, v3_Extensions)) {
2764                                         wanted.securid = ni->security_id;
2765                                         ntfs_enter_cache(scx->vol->securid_cache,
2766                                                         GENERIC(&wanted),
2767                                                         (cache_compare)compare);
2768                                 }
2769 #if CACHE_LEGACY_SIZE
2770                                 /* also invalidate legacy cache */
2771                                 if (isdir && !ni->security_id) {
2772                                         struct CACHED_PERMISSIONS_LEGACY legacy;
2773
2774                                         legacy.mft_no = ni->mft_no;
2775 #if POSIXACLS
2776                                         legacy.variable = wanted.variable;
2777                                         legacy.varsize = wanted.varsize;
2778 #else
2779                                         legacy.variable = (void*)NULL;
2780                                         legacy.varsize = 0;
2781 #endif
2782                                         ntfs_invalidate_cache(scx->vol->legacy_cache,
2783                                                 GENERIC(&legacy),
2784                                                 (cache_compare)leg_compare,0);
2785                                 }
2786 #endif
2787                         }
2788                         free(newattr);
2789                 } else {
2790                         /*
2791                          * could not build new security attribute
2792                          * errno set by ntfs_build_descr()
2793                          */
2794                         res = -1;
2795                 }
2796         }
2797         return (res);
2798 }
2799
2800 /*
2801  *              Check whether user has ownership rights on a file
2802  *
2803  *      Returns TRUE if allowed
2804  *              if not, errno tells why
2805  */
2806
2807 BOOL ntfs_allowed_as_owner(struct SECURITY_CONTEXT *scx, ntfs_inode *ni)
2808 {
2809         const struct CACHED_PERMISSIONS *cached;
2810         char *oldattr;
2811         const SID *usid;
2812         uid_t processuid;
2813         uid_t uid;
2814         BOOL gotowner;
2815         int allowed;
2816
2817         processuid = scx->uid;
2818 /* TODO : use CAP_FOWNER process capability */
2819         /*
2820          * Always allow for root
2821          * Also always allow if no mapping has been defined
2822          */
2823         if (!scx->mapping[MAPUSERS] || !processuid)
2824                 allowed = TRUE;
2825         else {
2826                 gotowner = FALSE; /* default */
2827                 /* get the owner, either from cache or from old attribute  */
2828                 cached = fetch_cache(scx, ni);
2829                 if (cached) {
2830                         uid = cached->uid;
2831                         gotowner = TRUE;
2832                 } else {
2833                         oldattr = getsecurityattr(scx->vol, ni);
2834                         if (oldattr) {
2835 #if OWNERFROMACL
2836                                 usid = ntfs_acl_owner(oldattr);
2837 #else
2838                                 const SECURITY_DESCRIPTOR_RELATIVE *phead;
2839
2840                                 phead = (const SECURITY_DESCRIPTOR_RELATIVE*)
2841                                                                 oldattr;
2842                                 usid = (const SID*)&oldattr
2843                                                 [le32_to_cpu(phead->owner)];
2844 #endif
2845                                 uid = ntfs_find_user(scx->mapping[MAPUSERS],
2846                                                 usid);
2847                                 gotowner = TRUE;
2848                                 free(oldattr);
2849                         }
2850                 }
2851                 allowed = FALSE;
2852                 if (gotowner) {
2853 /* TODO : use CAP_FOWNER process capability */
2854                         if (!processuid || (processuid == uid))
2855                                 allowed = TRUE;
2856                         else
2857                                 errno = EPERM;
2858                 }
2859         }
2860         return (allowed);
2861 }
2862
2863 #ifdef HAVE_SETXATTR    /* extended attributes interface required */
2864
2865 #if POSIXACLS
2866
2867 /*
2868  *              Set a new access or default Posix ACL to a file
2869  *              (or remove ACL if no input data)
2870  *      Validity of input data is checked after merging
2871  *
2872  *      Returns 0, or -1 if there is a problem which errno describes
2873  */
2874
2875 int ntfs_set_posix_acl(struct SECURITY_CONTEXT *scx, ntfs_inode *ni,
2876                         const char *name, const char *value, size_t size,
2877                         int flags)
2878 {
2879         const SECURITY_DESCRIPTOR_RELATIVE *phead;
2880         const struct CACHED_PERMISSIONS *cached;
2881         char *oldattr;
2882         uid_t processuid;
2883         const SID *usid;
2884         const SID *gsid;
2885         uid_t uid;
2886         uid_t gid;
2887         int res;
2888         BOOL isdir;
2889         BOOL deflt;
2890         BOOL exist;
2891         int count;
2892         struct POSIX_SECURITY *oldpxdesc;
2893         struct POSIX_SECURITY *newpxdesc;
2894
2895         /* get the current pxsec, either from cache or from old attribute  */
2896         res = -1;
2897         deflt = !strcmp(name,"system.posix_acl_default");
2898         if (size)
2899                 count = (size - sizeof(struct POSIX_ACL)) / sizeof(struct POSIX_ACE);
2900         else
2901                 count = 0;
2902         isdir = (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY) != const_cpu_to_le16(0);
2903         newpxdesc = (struct POSIX_SECURITY*)NULL;
2904         if ((!value
2905                 || (((const struct POSIX_ACL*)value)->version == POSIX_VERSION))
2906             && (!deflt || isdir || (!size && !value))) {
2907                 cached = fetch_cache(scx, ni);
2908                 if (cached) {
2909                         uid = cached->uid;
2910                         gid = cached->gid;
2911                         oldpxdesc = cached->pxdesc;
2912                         if (oldpxdesc) {
2913                                 newpxdesc = ntfs_replace_acl(oldpxdesc,
2914                                                 (const struct POSIX_ACL*)value,count,deflt);
2915                                 }
2916                 } else {
2917                         oldattr = getsecurityattr(scx->vol, ni);
2918                         if (oldattr) {
2919                                 phead = (const SECURITY_DESCRIPTOR_RELATIVE*)oldattr;
2920 #if OWNERFROMACL
2921                                 usid = ntfs_acl_owner(oldattr);
2922 #else
2923                                 usid = (const SID*)&oldattr[le32_to_cpu(phead->owner)];
2924 #endif
2925                                 gsid = (const SID*)&oldattr[le32_to_cpu(phead->group)];
2926                                 uid = ntfs_find_user(scx->mapping[MAPUSERS],usid);
2927                                 gid = ntfs_find_group(scx->mapping[MAPGROUPS],gsid);
2928                                 oldpxdesc = ntfs_build_permissions_posix(scx->mapping,
2929                                         oldattr, usid, gsid, isdir);
2930                                 if (oldpxdesc) {
2931                                         if (deflt)
2932                                                 exist = oldpxdesc->defcnt > 0;
2933                                         else
2934                                                 exist = oldpxdesc->acccnt > 3;
2935                                         if ((exist && (flags & XATTR_CREATE))
2936                                           || (!exist && (flags & XATTR_REPLACE))) {
2937                                                 errno = (exist ? EEXIST : ENODATA);
2938                                         } else {
2939                                                 newpxdesc = ntfs_replace_acl(oldpxdesc,
2940                                                         (const struct POSIX_ACL*)value,count,deflt);
2941                                         }
2942                                         free(oldpxdesc);
2943                                 }
2944                                 free(oldattr);
2945                         }
2946                 }
2947         } else
2948                 errno = EINVAL;
2949
2950         if (newpxdesc) {
2951                 processuid = scx->uid;
2952 /* TODO : use CAP_FOWNER process capability */
2953                 if (!processuid || (uid == processuid)) {
2954                                 /*
2955                                  * clear setgid if file group does
2956                                  * not match process group
2957                                  */
2958                         if (processuid && (gid != scx->gid)
2959                             && !groupmember(scx, scx->uid, gid)) {
2960                                 newpxdesc->mode &= ~S_ISGID;
2961                         }
2962                         res = ntfs_set_owner_mode(scx, ni, uid, gid,
2963                                 newpxdesc->mode, newpxdesc);
2964                 } else
2965                         errno = EPERM;
2966                 free(newpxdesc);
2967         }
2968         return (res ? -1 : 0);
2969 }
2970
2971 /*
2972  *              Remove a default Posix ACL from a file
2973  *
2974  *      Returns 0, or -1 if there is a problem which errno describes
2975  */
2976
2977 int ntfs_remove_posix_acl(struct SECURITY_CONTEXT *scx, ntfs_inode *ni,
2978                         const char *name)
2979 {
2980         return (ntfs_set_posix_acl(scx, ni, name,
2981                         (const char*)NULL, 0, 0));
2982 }
2983
2984 #endif
2985
2986 /*
2987  *              Set a new NTFS ACL to a file
2988  *
2989  *      Returns 0, or -1 if there is a problem
2990  */
2991
2992 int ntfs_set_ntfs_acl(struct SECURITY_CONTEXT *scx, ntfs_inode *ni,
2993                         const char *value, size_t size, int flags)
2994 {
2995         char *attr;
2996         int res;
2997
2998         res = -1;
2999         if ((size > 0)
3000            && !(flags & XATTR_CREATE)
3001            && ntfs_valid_descr(value,size)
3002            && (ntfs_attr_size(value) == size)) {
3003                         /* need copying in order to write */
3004                 attr = (char*)ntfs_malloc(size);
3005                 if (attr) {
3006                         memcpy(attr,value,size);
3007                         res = update_secur_descr(scx->vol, attr, ni);
3008                         /*
3009                          * No need to invalidate standard caches :
3010                          * the relation between a securid and
3011                          * the associated protection is unchanged,
3012                          * only the relation between a file and
3013                          * its securid and protection is changed.
3014                          */
3015 #if CACHE_LEGACY_SIZE
3016                         /*
3017                          * we must however invalidate the legacy
3018                          * cache, which is based on inode numbers.
3019                          * For safety, invalidate even if updating
3020                          * failed.
3021                          */
3022                         if ((ni->mrec->flags & MFT_RECORD_IS_DIRECTORY)
3023                            && !ni->security_id) {
3024                                 struct CACHED_PERMISSIONS_LEGACY legacy;
3025
3026                                 legacy.mft_no = ni->mft_no;
3027                                 legacy.variable = (char*)NULL;
3028                                 legacy.varsize = 0;
3029                                 ntfs_invalidate_cache(scx->vol->legacy_cache,
3030                                         GENERIC(&legacy),
3031                                         (cache_compare)leg_compare,0);
3032                         }
3033 #endif
3034                         free(attr);
3035                 } else
3036                         errno = ENOMEM;
3037         } else
3038                 errno = EINVAL;
3039         return (res ? -1 : 0);
3040 }
3041
3042 #endif /* HAVE_SETXATTR */
3043
3044 /*
3045  *              Set new permissions to a file
3046  *      Checks user mapping has been defined before request for setting
3047  *
3048  *      rejected if request is not originated by owner or root
3049  *
3050  *      returns 0 on success
3051  *              -1 on failure, with errno = EIO
3052  */
3053
3054 int ntfs_set_mode(struct SECURITY_CONTEXT *scx, ntfs_inode *ni, mode_t mode)
3055 {
3056         const SECURITY_DESCRIPTOR_RELATIVE *phead;
3057         const struct CACHED_PERMISSIONS *cached;
3058         char *oldattr;
3059         const SID *usid;
3060         const SID *gsid;
3061         uid_t processuid;
3062         uid_t uid;
3063         uid_t gid;
3064         int res;
3065 #if POSIXACLS
3066         BOOL isdir;
3067         int pxsize;
3068         const struct POSIX_SECURITY *oldpxdesc;
3069         struct POSIX_SECURITY *newpxdesc = (struct POSIX_SECURITY*)NULL;
3070 #endif
3071
3072         /* get the current owner, either from cache or from old attribute  */
3073         res = 0;
3074         cached = fetch_cache(scx, ni);
3075         if (cached) {
3076                 uid = cached->uid;
3077                 gid = cached->gid;
3078 #if POSIXACLS
3079                 oldpxdesc = cached->pxdesc;
3080                 if (oldpxdesc) {
3081                                 /* must copy before merging */
3082                         pxsize = sizeof(struct POSIX_SECURITY)
3083                                 + (oldpxdesc->acccnt + oldpxdesc->defcnt)*sizeof(struct POSIX_ACE);
3084                         newpxdesc = (struct POSIX_SECURITY*)malloc(pxsize);
3085                         if (newpxdesc) {
3086                                 memcpy(newpxdesc, oldpxdesc, pxsize);
3087                                 if (ntfs_merge_mode_posix(newpxdesc, mode))
3088                                         res = -1;
3089                         } else
3090                                 res = -1;
3091                 } else
3092                         newpxdesc = (struct POSIX_SECURITY*)NULL;
3093 #endif
3094         } else {
3095                 oldattr = getsecurityattr(scx->vol, ni);
3096                 if (oldattr) {
3097                         phead = (const SECURITY_DESCRIPTOR_RELATIVE*)oldattr;
3098 #if OWNERFROMACL
3099                         usid = ntfs_acl_owner(oldattr);
3100 #else
3101                         usid = (const SID*)&oldattr[le32_to_cpu(phead->owner)];
3102 #endif
3103                         gsid = (const SID*)&oldattr[le32_to_cpu(phead->group)];
3104                         uid = ntfs_find_user(scx->mapping[MAPUSERS],usid);
3105                         gid = ntfs_find_group(scx->mapping[MAPGROUPS],gsid);
3106 #if POSIXACLS
3107                         isdir = (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY) != const_cpu_to_le16(0);
3108                         newpxdesc = ntfs_build_permissions_posix(scx->mapping,
3109                                 oldattr, usid, gsid, isdir);
3110                         if (!newpxdesc || ntfs_merge_mode_posix(newpxdesc, mode))
3111                                 res = -1;
3112 #endif
3113                         free(oldattr);
3114                 } else
3115                         res = -1;
3116         }
3117
3118         if (!res) {
3119                 processuid = scx->uid;
3120 /* TODO : use CAP_FOWNER process capability */
3121                 if (!processuid || (uid == processuid)) {
3122                                 /*
3123                                  * clear setgid if file group does
3124                                  * not match process group
3125                                  */
3126                         if (processuid && (gid != scx->gid)
3127                             && !groupmember(scx, scx->uid, gid))
3128                                 mode &= ~S_ISGID;
3129 #if POSIXACLS
3130                         if (newpxdesc) {
3131                                 newpxdesc->mode = mode;
3132                                 res = ntfs_set_owner_mode(scx, ni, uid, gid,
3133                                         mode, newpxdesc);
3134                         } else
3135                                 res = ntfs_set_owner_mode(scx, ni, uid, gid,
3136                                         mode, newpxdesc);
3137 #else
3138                         res = ntfs_set_owner_mode(scx, ni, uid, gid, mode);
3139 #endif
3140                 } else {
3141                         errno = EPERM;
3142                         res = -1;       /* neither owner nor root */
3143                 }
3144         } else {
3145                 /*
3146                  * Should not happen : a default descriptor is generated
3147                  * by getsecurityattr() when there are none
3148                  */
3149                 ntfs_log_error("File has no security descriptor\n");
3150                 res = -1;
3151                 errno = EIO;
3152         }
3153 #if POSIXACLS
3154         if (newpxdesc) free(newpxdesc);
3155 #endif
3156         return (res ? -1 : 0);
3157 }
3158
3159 /*
3160  *      Create a default security descriptor for files whose descriptor
3161  *      cannot be inherited
3162  */
3163
3164 int ntfs_sd_add_everyone(ntfs_inode *ni)
3165 {
3166         /* JPA SECURITY_DESCRIPTOR_ATTR *sd; */
3167         SECURITY_DESCRIPTOR_RELATIVE *sd;
3168         ACL *acl;
3169         ACCESS_ALLOWED_ACE *ace;
3170         SID *sid;
3171         int ret, sd_len;
3172         
3173         /* Create SECURITY_DESCRIPTOR attribute (everyone has full access). */
3174         /*
3175          * Calculate security descriptor length. We have 2 sub-authorities in
3176          * owner and group SIDs, but structure SID contain only one, so add
3177          * 4 bytes to every SID.
3178          */
3179         sd_len = sizeof(SECURITY_DESCRIPTOR_ATTR) + 2 * (sizeof(SID) + 4) +
3180                 sizeof(ACL) + sizeof(ACCESS_ALLOWED_ACE); 
3181         sd = (SECURITY_DESCRIPTOR_RELATIVE*)ntfs_calloc(sd_len);
3182         if (!sd)
3183                 return -1;
3184         
3185         sd->revision = SECURITY_DESCRIPTOR_REVISION;
3186         sd->control = SE_DACL_PRESENT | SE_SELF_RELATIVE;
3187         
3188         sid = (SID*)((u8*)sd + sizeof(SECURITY_DESCRIPTOR_ATTR));
3189         sid->revision = SID_REVISION;
3190         sid->sub_authority_count = 2;
3191         sid->sub_authority[0] = const_cpu_to_le32(SECURITY_BUILTIN_DOMAIN_RID);
3192         sid->sub_authority[1] = const_cpu_to_le32(DOMAIN_ALIAS_RID_ADMINS);
3193         sid->identifier_authority.value[5] = 5;
3194         sd->owner = cpu_to_le32((u8*)sid - (u8*)sd);
3195         
3196         sid = (SID*)((u8*)sid + sizeof(SID) + 4); 
3197         sid->revision = SID_REVISION;
3198         sid->sub_authority_count = 2;
3199         sid->sub_authority[0] = const_cpu_to_le32(SECURITY_BUILTIN_DOMAIN_RID);
3200         sid->sub_authority[1] = const_cpu_to_le32(DOMAIN_ALIAS_RID_ADMINS);
3201         sid->identifier_authority.value[5] = 5;
3202         sd->group = cpu_to_le32((u8*)sid - (u8*)sd);
3203         
3204         acl = (ACL*)((u8*)sid + sizeof(SID) + 4);
3205         acl->revision = ACL_REVISION;
3206         acl->size = const_cpu_to_le16(sizeof(ACL) + sizeof(ACCESS_ALLOWED_ACE));
3207         acl->ace_count = const_cpu_to_le16(1);
3208         sd->dacl = cpu_to_le32((u8*)acl - (u8*)sd);
3209         
3210         ace = (ACCESS_ALLOWED_ACE*)((u8*)acl + sizeof(ACL));
3211         ace->type = ACCESS_ALLOWED_ACE_TYPE;
3212         ace->flags = OBJECT_INHERIT_ACE | CONTAINER_INHERIT_ACE;
3213         ace->size = const_cpu_to_le16(sizeof(ACCESS_ALLOWED_ACE));
3214         ace->mask = const_cpu_to_le32(0x1f01ff); /* FIXME */
3215         ace->sid.revision = SID_REVISION;
3216         ace->sid.sub_authority_count = 1;
3217         ace->sid.sub_authority[0] = const_cpu_to_le32(0);
3218         ace->sid.identifier_authority.value[5] = 1;
3219
3220         ret = ntfs_attr_add(ni, AT_SECURITY_DESCRIPTOR, AT_UNNAMED, 0, (u8*)sd,
3221                             sd_len);
3222         if (ret)
3223                 ntfs_log_perror("Failed to add initial SECURITY_DESCRIPTOR");
3224         
3225         free(sd);
3226         return ret;
3227 }
3228
3229 /*
3230  *              Check whether user can access a file in a specific way
3231  *
3232  *      Returns 1 if access is allowed, including user is root or no
3233  *                user mapping defined
3234  *              2 if sticky and accesstype is S_IWRITE + S_IEXEC + S_ISVTX
3235  *              0 and sets errno if there is a problem or if access
3236  *                is not allowed
3237  *
3238  *      This is used for Posix ACL and checking creation of DOS file names
3239  */
3240
3241 int ntfs_allowed_access(struct SECURITY_CONTEXT *scx,
3242                 ntfs_inode *ni,
3243                 int accesstype) /* access type required (S_Ixxx values) */
3244 {
3245         int perm;
3246         int res;
3247         int allow;
3248         struct stat stbuf;
3249
3250         /*
3251          * Always allow for root unless execution is requested.
3252          * (was checked by fuse until kernel 2.6.29)
3253          * Also always allow if no mapping has been defined
3254          */
3255         if (!scx->mapping[MAPUSERS]
3256             || (!scx->uid
3257                 && (!(accesstype & S_IEXEC)
3258                     || (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY))))
3259                 allow = 1;
3260         else {
3261                 perm = ntfs_get_perm(scx, ni, accesstype);
3262                 if (perm >= 0) {
3263                         res = EACCES;
3264                         switch (accesstype) {
3265                         case S_IEXEC:
3266                                 allow = (perm & (S_IXUSR | S_IXGRP | S_IXOTH)) != 0;
3267                                 break;
3268                         case S_IWRITE:
3269                                 allow = (perm & (S_IWUSR | S_IWGRP | S_IWOTH)) != 0;
3270                                 break;
3271                         case S_IWRITE + S_IEXEC:
3272                                 allow = ((perm & (S_IWUSR | S_IWGRP | S_IWOTH)) != 0)
3273                                     && ((perm & (S_IXUSR | S_IXGRP | S_IXOTH)) != 0);
3274                                 break;
3275                         case S_IREAD:
3276                                 allow = (perm & (S_IRUSR | S_IRGRP | S_IROTH)) != 0;
3277                                 break;
3278                         case S_IREAD + S_IEXEC:
3279                                 allow = ((perm & (S_IRUSR | S_IRGRP | S_IROTH)) != 0)
3280                                     && ((perm & (S_IXUSR | S_IXGRP | S_IXOTH)) != 0);
3281                                 break;
3282                         case S_IREAD + S_IWRITE:
3283                                 allow = ((perm & (S_IRUSR | S_IRGRP | S_IROTH)) != 0)
3284                                     && ((perm & (S_IWUSR | S_IWGRP | S_IWOTH)) != 0);
3285                                 break;
3286                         case S_IWRITE + S_IEXEC + S_ISVTX:
3287                                 if (perm & S_ISVTX) {
3288                                         if ((ntfs_get_owner_mode(scx,ni,&stbuf) >= 0)
3289                                             && (stbuf.st_uid == scx->uid))
3290                                                 allow = 1;
3291                                         else
3292                                                 allow = 2;
3293                                 } else
3294                                         allow = ((perm & (S_IWUSR | S_IWGRP | S_IWOTH)) != 0)
3295                                             && ((perm & (S_IXUSR | S_IXGRP | S_IXOTH)) != 0);
3296                                 break;
3297                         case S_IREAD + S_IWRITE + S_IEXEC:
3298                                 allow = ((perm & (S_IRUSR | S_IRGRP | S_IROTH)) != 0)
3299                                     && ((perm & (S_IWUSR | S_IWGRP | S_IWOTH)) != 0)
3300                                     && ((perm & (S_IXUSR | S_IXGRP | S_IXOTH)) != 0);
3301                                 break;
3302                         default :
3303                                 res = EINVAL;
3304                                 allow = 0;
3305                                 break;
3306                         }
3307                         if (!allow)
3308                                 errno = res;
3309                 } else
3310                         allow = 0;
3311         }
3312         return (allow);
3313 }
3314
3315 #if 0 /* not needed any more */
3316
3317 /*
3318  *              Check whether user can access the parent directory
3319  *      of a file in a specific way
3320  *
3321  *      Returns true if access is allowed, including user is root and
3322  *              no user mapping defined
3323  *      
3324  *      Sets errno if there is a problem or if not allowed
3325  *
3326  *      This is used for Posix ACL and checking creation of DOS file names
3327  */
3328
3329 BOOL old_ntfs_allowed_dir_access(struct SECURITY_CONTEXT *scx,
3330                 const char *path, int accesstype)
3331 {
3332         int allow;
3333         char *dirpath;
3334         char *name;
3335         ntfs_inode *ni;
3336         ntfs_inode *dir_ni;
3337         struct stat stbuf;
3338
3339         allow = 0;
3340         dirpath = strdup(path);
3341         if (dirpath) {
3342                 /* the root of file system is seen as a parent of itself */
3343                 /* is that correct ? */
3344                 name = strrchr(dirpath, '/');
3345                 *name = 0;
3346                 dir_ni = ntfs_pathname_to_inode(scx->vol, NULL, dirpath);
3347                 if (dir_ni) {
3348                         allow = ntfs_allowed_access(scx,
3349                                  dir_ni, accesstype);
3350                         ntfs_inode_close(dir_ni);
3351                                 /*
3352                                  * for an not-owned sticky directory, have to
3353                                  * check whether file itself is owned
3354                                  */
3355                         if ((accesstype == (S_IWRITE + S_IEXEC + S_ISVTX))
3356                            && (allow == 2)) {
3357                                 ni = ntfs_pathname_to_inode(scx->vol, NULL,
3358                                          path);
3359                                 allow = FALSE;
3360                                 if (ni) {
3361                                         allow = (ntfs_get_owner_mode(scx,ni,&stbuf) >= 0)
3362                                                 && (stbuf.st_uid == scx->uid);
3363                                 ntfs_inode_close(ni);
3364                                 }
3365                         }
3366                 }
3367                 free(dirpath);
3368         }
3369         return (allow);         /* errno is set if not allowed */
3370 }
3371
3372 #endif
3373
3374 /*
3375  *              Define a new owner/group to a file
3376  *
3377  *      returns zero if successful
3378  */
3379
3380 int ntfs_set_owner(struct SECURITY_CONTEXT *scx, ntfs_inode *ni,
3381                         uid_t uid, gid_t gid)
3382 {
3383         const SECURITY_DESCRIPTOR_RELATIVE *phead;
3384         const struct CACHED_PERMISSIONS *cached;
3385         char *oldattr;
3386         const SID *usid;
3387         const SID *gsid;
3388         uid_t fileuid;
3389         uid_t filegid;
3390         mode_t mode;
3391         int perm;
3392         BOOL isdir;
3393         int res;
3394 #if POSIXACLS
3395         struct POSIX_SECURITY *pxdesc;
3396         BOOL pxdescbuilt = FALSE;
3397 #endif
3398
3399         res = 0;
3400         /* get the current owner and mode from cache or security attributes */
3401         oldattr = (char*)NULL;
3402         cached = fetch_cache(scx,ni);
3403         if (cached) {
3404                 fileuid = cached->uid;
3405                 filegid = cached->gid;
3406                 mode = cached->mode;
3407 #if POSIXACLS
3408                 pxdesc = cached->pxdesc;
3409                 if (!pxdesc)
3410                         res = -1;
3411 #endif
3412         } else {
3413                 fileuid = 0;
3414                 filegid = 0;
3415                 mode = 0;
3416                 oldattr = getsecurityattr(scx->vol, ni);
3417                 if (oldattr) {
3418                         isdir = (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY)
3419                                 != const_cpu_to_le16(0);
3420                         phead = (const SECURITY_DESCRIPTOR_RELATIVE*)
3421                                 oldattr;
3422                         gsid = (const SID*)
3423                                 &oldattr[le32_to_cpu(phead->group)];
3424 #if OWNERFROMACL
3425                         usid = ntfs_acl_owner(oldattr);
3426 #else
3427                         usid = (const SID*)
3428                                 &oldattr[le32_to_cpu(phead->owner)];
3429 #endif
3430 #if POSIXACLS
3431                         pxdesc = ntfs_build_permissions_posix(scx->mapping, oldattr,
3432                                         usid, gsid, isdir);
3433                         if (pxdesc) {
3434                                 pxdescbuilt = TRUE;
3435                                 fileuid = ntfs_find_user(scx->mapping[MAPUSERS],usid);
3436                                 filegid = ntfs_find_group(scx->mapping[MAPGROUPS],gsid);
3437                                 mode = perm = pxdesc->mode;
3438                         } else
3439                                 res = -1;
3440 #else
3441                         mode = perm = ntfs_build_permissions(oldattr,
3442                                          usid, gsid, isdir);
3443                         if (perm >= 0) {
3444                                 fileuid = ntfs_find_user(scx->mapping[MAPUSERS],usid);
3445                                 filegid = ntfs_find_group(scx->mapping[MAPGROUPS],gsid);
3446                         } else
3447                                 res = -1;
3448 #endif
3449                         free(oldattr);
3450                 } else
3451                         res = -1;
3452         }
3453         if (!res) {
3454                 /* check requested by root */
3455                 /* or chgrp requested by owner to an owned group */
3456                 if (!scx->uid
3457                    || ((((int)uid < 0) || (uid == fileuid))
3458                       && ((gid == scx->gid) || groupmember(scx, scx->uid, gid))
3459                       && (fileuid == scx->uid))) {
3460                         /* replace by the new usid and gsid */
3461                         /* or reuse old gid and sid for cacheing */
3462                         if ((int)uid < 0)
3463                                 uid = fileuid;
3464                         if ((int)gid < 0)
3465                                 gid = filegid;
3466                         /* clear setuid and setgid if owner has changed */
3467                         /* unless request originated by root */
3468                         if (uid && (fileuid != uid))
3469                                 mode &= 01777;
3470 #if POSIXACLS
3471                         res = ntfs_set_owner_mode(scx, ni, uid, gid, 
3472                                 mode, pxdesc);
3473 #else
3474                         res = ntfs_set_owner_mode(scx, ni, uid, gid, mode);
3475 #endif
3476                 } else {
3477                         res = -1;       /* neither owner nor root */
3478                         errno = EPERM;
3479                 }
3480 #if POSIXACLS
3481                 if (pxdescbuilt)
3482                         free(pxdesc);
3483 #endif
3484         } else {
3485                 /*
3486                  * Should not happen : a default descriptor is generated
3487                  * by getsecurityattr() when there are none
3488                  */
3489                 ntfs_log_error("File has no security descriptor\n");
3490                 res = -1;
3491                 errno = EIO;
3492         }
3493         return (res ? -1 : 0);
3494 }
3495
3496 /*
3497  *              Define new owner/group and mode to a file
3498  *
3499  *      returns zero if successful
3500  */
3501
3502 int ntfs_set_ownmod(struct SECURITY_CONTEXT *scx, ntfs_inode *ni,
3503                         uid_t uid, gid_t gid, const mode_t mode)
3504 {
3505         const struct CACHED_PERMISSIONS *cached;
3506         char *oldattr;
3507         uid_t fileuid;
3508         uid_t filegid;
3509         int res;
3510 #if POSIXACLS
3511         const SECURITY_DESCRIPTOR_RELATIVE *phead;
3512         const SID *usid;
3513         const SID *gsid;
3514         BOOL isdir;
3515         const struct POSIX_SECURITY *oldpxdesc;
3516         struct POSIX_SECURITY *newpxdesc = (struct POSIX_SECURITY*)NULL;
3517         int pxsize;
3518 #endif
3519
3520         res = 0;
3521         /* get the current owner and mode from cache or security attributes */
3522         oldattr = (char*)NULL;
3523         cached = fetch_cache(scx,ni);
3524         if (cached) {
3525                 fileuid = cached->uid;
3526                 filegid = cached->gid;
3527 #if POSIXACLS
3528                 oldpxdesc = cached->pxdesc;
3529                 if (oldpxdesc) {
3530                                 /* must copy before merging */
3531                         pxsize = sizeof(struct POSIX_SECURITY)
3532                                 + (oldpxdesc->acccnt + oldpxdesc->defcnt)*sizeof(struct POSIX_ACE);
3533                         newpxdesc = (struct POSIX_SECURITY*)malloc(pxsize);
3534                         if (newpxdesc) {
3535                                 memcpy(newpxdesc, oldpxdesc, pxsize);
3536                                 if (ntfs_merge_mode_posix(newpxdesc, mode))
3537                                         res = -1;
3538                         } else
3539                                 res = -1;
3540                 }
3541 #endif
3542         } else {
3543                 fileuid = 0;
3544                 filegid = 0;
3545                 oldattr = getsecurityattr(scx->vol, ni);
3546                 if (oldattr) {
3547 #if POSIXACLS
3548                         isdir = (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY)
3549                                 != const_cpu_to_le16(0);
3550                         phead = (const SECURITY_DESCRIPTOR_RELATIVE*)
3551                                 oldattr;
3552                         gsid = (const SID*)
3553                                 &oldattr[le32_to_cpu(phead->group)];
3554 #if OWNERFROMACL
3555                         usid = ntfs_acl_owner(oldattr);
3556 #else
3557                         usid = (const SID*)
3558                                 &oldattr[le32_to_cpu(phead->owner)];
3559 #endif
3560                         newpxdesc = ntfs_build_permissions_posix(scx->mapping, oldattr,
3561                                         usid, gsid, isdir);
3562                         if (!newpxdesc || ntfs_merge_mode_posix(newpxdesc, mode))
3563                                 res = -1;
3564                         else {
3565                                 fileuid = ntfs_find_user(scx->mapping[MAPUSERS],usid);
3566                                 filegid = ntfs_find_group(scx->mapping[MAPGROUPS],gsid);
3567                         }
3568 #endif
3569                         free(oldattr);
3570                 } else
3571                         res = -1;
3572         }
3573         if (!res) {
3574                 /* check requested by root */
3575                 /* or chgrp requested by owner to an owned group */
3576                 if (!scx->uid
3577                    || ((((int)uid < 0) || (uid == fileuid))
3578                       && ((gid == scx->gid) || groupmember(scx, scx->uid, gid))
3579                       && (fileuid == scx->uid))) {
3580                         /* replace by the new usid and gsid */
3581                         /* or reuse old gid and sid for cacheing */
3582                         if ((int)uid < 0)
3583                                 uid = fileuid;
3584                         if ((int)gid < 0)
3585                                 gid = filegid;
3586 #if POSIXACLS
3587                         res = ntfs_set_owner_mode(scx, ni, uid, gid, 
3588                                 mode, newpxdesc);
3589 #else
3590                         res = ntfs_set_owner_mode(scx, ni, uid, gid, mode);
3591 #endif
3592                 } else {
3593                         res = -1;       /* neither owner nor root */
3594                         errno = EPERM;
3595                 }
3596         } else {
3597                 /*
3598                  * Should not happen : a default descriptor is generated
3599                  * by getsecurityattr() when there are none
3600                  */
3601                 ntfs_log_error("File has no security descriptor\n");
3602                 res = -1;
3603                 errno = EIO;
3604         }
3605 #if POSIXACLS
3606         free(newpxdesc);
3607 #endif
3608         return (res ? -1 : 0);
3609 }
3610
3611 /*
3612  *              Build a security id for a descriptor inherited from
3613  *      parent directory the Windows way
3614  */
3615
3616 static le32 build_inherited_id(struct SECURITY_CONTEXT *scx,
3617                         const char *parentattr, BOOL fordir)
3618 {
3619         const SECURITY_DESCRIPTOR_RELATIVE *pphead;
3620         const ACL *ppacl;
3621         const SID *usid;
3622         const SID *gsid;
3623         BIGSID defusid;
3624         BIGSID defgsid;
3625         int offpacl;
3626         int offowner;
3627         int offgroup;
3628         SECURITY_DESCRIPTOR_RELATIVE *pnhead;
3629         ACL *pnacl;
3630         int parentattrsz;
3631         char *newattr;
3632         int newattrsz;
3633         int aclsz;
3634         int usidsz;
3635         int gsidsz;
3636         int pos;
3637         le32 securid;
3638
3639         parentattrsz = ntfs_attr_size(parentattr);
3640         pphead = (const SECURITY_DESCRIPTOR_RELATIVE*)parentattr;
3641         if (scx->mapping[MAPUSERS]) {
3642                 usid = ntfs_find_usid(scx->mapping[MAPUSERS], scx->uid, (SID*)&defusid);
3643                 gsid = ntfs_find_gsid(scx->mapping[MAPGROUPS], scx->gid, (SID*)&defgsid);
3644                 if (!usid)
3645                         usid = adminsid;
3646                 if (!gsid)
3647                         gsid = adminsid;
3648         } else {
3649                 /*
3650                  * If there is no user mapping, we have to copy owner
3651                  * and group from parent directory.
3652                  * Windows never has to do that, because it can always
3653                  * rely on a user mapping
3654                  */
3655                 offowner = le32_to_cpu(pphead->owner);
3656                 usid = (const SID*)&parentattr[offowner];
3657                 offgroup = le32_to_cpu(pphead->group);
3658                 gsid = (const SID*)&parentattr[offgroup];
3659         }
3660                 /*
3661                  * new attribute is smaller than parent's
3662                  * except for differences in SIDs which appear in
3663                  * owner, group and possible grants and denials in
3664                  * generic creator-owner and creator-group ACEs.
3665                  * For directories, an ACE may be duplicated for
3666                  * access and inheritance, so we double the count.
3667                  */
3668         usidsz = ntfs_sid_size(usid);
3669         gsidsz = ntfs_sid_size(gsid);
3670         newattrsz = parentattrsz + 3*usidsz + 3*gsidsz;
3671         if (fordir)
3672                 newattrsz *= 2;
3673         newattr = (char*)ntfs_malloc(newattrsz);
3674         if (newattr) {
3675                 pnhead = (SECURITY_DESCRIPTOR_RELATIVE*)newattr;
3676                 pnhead->revision = SECURITY_DESCRIPTOR_REVISION;
3677                 pnhead->alignment = 0;
3678                 pnhead->control = SE_SELF_RELATIVE;
3679                 pos = sizeof(SECURITY_DESCRIPTOR_RELATIVE);
3680                         /*
3681                          * locate and inherit DACL
3682                          * do not test SE_DACL_PRESENT (wrong for "DR Watson")
3683                          */
3684                 pnhead->dacl = const_cpu_to_le32(0);
3685                 if (pphead->dacl) {
3686                         offpacl = le32_to_cpu(pphead->dacl);
3687                         ppacl = (const ACL*)&parentattr[offpacl];
3688                         pnacl = (ACL*)&newattr[pos];
3689                         aclsz = ntfs_inherit_acl(ppacl, pnacl, usid, gsid, fordir);
3690                         if (aclsz) {
3691                                 pnhead->dacl = cpu_to_le32(pos);
3692                                 pos += aclsz;
3693                                 pnhead->control |= SE_DACL_PRESENT;
3694                         }
3695                 }
3696                         /*
3697                          * locate and inherit SACL
3698                          */
3699                 pnhead->sacl = const_cpu_to_le32(0);
3700                 if (pphead->sacl) {
3701                         offpacl = le32_to_cpu(pphead->sacl);
3702                         ppacl = (const ACL*)&parentattr[offpacl];
3703                         pnacl = (ACL*)&newattr[pos];
3704                         aclsz = ntfs_inherit_acl(ppacl, pnacl, usid, gsid, fordir);
3705                         if (aclsz) {
3706                                 pnhead->sacl = cpu_to_le32(pos);
3707                                 pos += aclsz;
3708                                 pnhead->control |= SE_SACL_PRESENT;
3709                         }
3710                 }
3711                         /*
3712                          * inherit or redefine owner
3713                          */
3714                 memcpy(&newattr[pos],usid,usidsz);
3715                 pnhead->owner = cpu_to_le32(pos);
3716                 pos += usidsz;
3717                         /*
3718                          * inherit or redefine group
3719                          */
3720                 memcpy(&newattr[pos],gsid,gsidsz);
3721                 pnhead->group = cpu_to_le32(pos);
3722                 pos += usidsz;
3723                 securid = setsecurityattr(scx->vol,
3724                         (SECURITY_DESCRIPTOR_RELATIVE*)newattr, pos);
3725                 free(newattr);
3726         } else
3727                 securid = const_cpu_to_le32(0);
3728         return (securid);
3729 }
3730
3731 /*
3732  *              Get an inherited security id
3733  *
3734  *      For Windows compatibility, the normal initial permission setting
3735  *      may be inherited from the parent directory instead of being
3736  *      defined by the creation arguments.
3737  *
3738  *      The following creates an inherited id for that purpose.
3739  *
3740  *      Note : the owner and group of parent directory are also
3741  *      inherited (which is not the case on Windows) if no user mapping
3742  *      is defined.
3743  *
3744  *      Returns the inherited id, or zero if not possible (eg on NTFS 1.x)
3745  */
3746
3747 le32 ntfs_inherited_id(struct SECURITY_CONTEXT *scx,
3748                         ntfs_inode *dir_ni, BOOL fordir)
3749 {
3750         struct CACHED_PERMISSIONS *cached;
3751         char *parentattr;
3752         le32 securid;
3753
3754         securid = const_cpu_to_le32(0);
3755         cached = (struct CACHED_PERMISSIONS*)NULL;
3756                 /*
3757                  * Try to get inherited id from cache
3758                  */
3759         if (test_nino_flag(dir_ni, v3_Extensions)
3760                         && dir_ni->security_id) {
3761                 cached = fetch_cache(scx, dir_ni);
3762                 if (cached)
3763                         securid = (fordir ? cached->inh_dirid
3764                                         : cached->inh_fileid);
3765         }
3766                 /*
3767                  * Not cached or not available in cache, compute it all
3768                  * Note : if parent directory has no id, it is not cacheable
3769                  */
3770         if (!securid) {
3771                 parentattr = getsecurityattr(scx->vol, dir_ni);
3772                 if (parentattr) {
3773                         securid = build_inherited_id(scx,
3774                                                 parentattr, fordir);
3775                         free(parentattr);
3776                         /*
3777                          * Store the result into cache for further use
3778                          */
3779                         if (securid) {
3780                                 cached = fetch_cache(scx, dir_ni);
3781                                 if (cached) {
3782                                         if (fordir)
3783                                                 cached->inh_dirid = securid;
3784                                         else
3785                                                 cached->inh_fileid = securid;
3786                                 }
3787                         }
3788                 }
3789         }
3790         return (securid);
3791 }
3792
3793 /*
3794  *              Link a group to a member of group
3795  *
3796  *      Returns 0 if OK, -1 (and errno set) if error
3797  */
3798
3799 static int link_single_group(struct MAPPING *usermapping, struct passwd *user,
3800                         gid_t gid)
3801 {
3802         struct group *group;
3803         char **grmem;
3804         int grcnt;
3805         gid_t *groups;
3806         int res;
3807
3808         res = 0;
3809         group = getgrgid(gid);
3810         if (group && group->gr_mem) {
3811                 grcnt = usermapping->grcnt;
3812                 groups = usermapping->groups;
3813                 grmem = group->gr_mem;
3814                 while (*grmem && strcmp(user->pw_name, *grmem))
3815                         grmem++;
3816                 if (*grmem) {
3817                         if (!grcnt)
3818                                 groups = (gid_t*)malloc(sizeof(gid_t));
3819                         else
3820                                 groups = (gid_t*)realloc(groups,
3821                                         (grcnt+1)*sizeof(gid_t));
3822                         if (groups)
3823                                 groups[grcnt++] = gid;
3824                         else {
3825                                 res = -1;
3826                                 errno = ENOMEM;
3827                         }
3828                 }
3829                 usermapping->grcnt = grcnt;
3830                 usermapping->groups = groups;
3831         }
3832         return (res);
3833 }
3834
3835
3836 /*
3837  *              Statically link group to users
3838  *      This is based on groups defined in /etc/group and does not take
3839  *      the groups dynamically set by setgroups() nor any changes in
3840  *      /etc/group into account
3841  *
3842  *      Only mapped groups and root group are linked to mapped users
3843  *
3844  *      Returns 0 if OK, -1 (and errno set) if error
3845  *
3846  */
3847
3848 static int link_group_members(struct SECURITY_CONTEXT *scx)
3849 {
3850         struct MAPPING *usermapping;
3851         struct MAPPING *groupmapping;
3852         struct passwd *user;
3853         int res;
3854
3855         res = 0;
3856         for (usermapping=scx->mapping[MAPUSERS]; usermapping && !res;
3857                         usermapping=usermapping->next) {
3858                 usermapping->grcnt = 0;
3859                 usermapping->groups = (gid_t*)NULL;
3860                 user = getpwuid(usermapping->xid);
3861                 if (user && user->pw_name) {
3862                         for (groupmapping=scx->mapping[MAPGROUPS];
3863                                         groupmapping && !res;
3864                                         groupmapping=groupmapping->next) {
3865                                 if (link_single_group(usermapping, user,
3866                                     groupmapping->xid))
3867                                         res = -1;
3868                                 }
3869                         if (!res && link_single_group(usermapping,
3870                                          user, (gid_t)0))
3871                                 res = -1;
3872                 }
3873         }
3874         return (res);
3875 }
3876
3877 /*
3878  *              Apply default single user mapping
3879  *      returns zero if successful
3880  */
3881
3882 static int ntfs_do_default_mapping(struct SECURITY_CONTEXT *scx,
3883                          uid_t uid, gid_t gid, const SID *usid)
3884 {
3885         struct MAPPING *usermapping;
3886         struct MAPPING *groupmapping;
3887         SID *sid;
3888         int sidsz;
3889         int res;
3890
3891         res = -1;
3892         sidsz = ntfs_sid_size(usid);
3893         sid = (SID*)ntfs_malloc(sidsz);
3894         if (sid) {
3895                 memcpy(sid,usid,sidsz);
3896                 usermapping = (struct MAPPING*)ntfs_malloc(sizeof(struct MAPPING));
3897                 if (usermapping) {
3898                         groupmapping = (struct MAPPING*)ntfs_malloc(sizeof(struct MAPPING));
3899                         if (groupmapping) {
3900                                 usermapping->sid = sid;
3901                                 usermapping->xid = uid;
3902                                 usermapping->next = (struct MAPPING*)NULL;
3903                                 groupmapping->sid = sid;
3904                                 groupmapping->xid = gid;
3905                                 groupmapping->next = (struct MAPPING*)NULL;
3906                                 scx->mapping[MAPUSERS] = usermapping;
3907                                 scx->mapping[MAPGROUPS] = groupmapping;
3908                                 res = 0;
3909                         }
3910                 }
3911         }
3912         return (res);
3913 }
3914
3915 /*
3916  *              Make sure there are no ambiguous mapping
3917  *      Ambiguous mapping may lead to undesired configurations and
3918  *      we had rather be safe until the consequences are understood
3919  */
3920
3921 #if 0 /* not activated for now */
3922
3923 static BOOL check_mapping(const struct MAPPING *usermapping,
3924                 const struct MAPPING *groupmapping)
3925 {
3926         const struct MAPPING *mapping1;
3927         const struct MAPPING *mapping2;
3928         BOOL ambiguous;
3929
3930         ambiguous = FALSE;
3931         for (mapping1=usermapping; mapping1; mapping1=mapping1->next)
3932                 for (mapping2=mapping1->next; mapping2; mapping1=mapping2->next)
3933                         if (ntfs_same_sid(mapping1->sid,mapping2->sid)) {
3934                                 if (mapping1->xid != mapping2->xid)
3935                                         ambiguous = TRUE;
3936                         } else {
3937                                 if (mapping1->xid == mapping2->xid)
3938                                         ambiguous = TRUE;
3939                         }
3940         for (mapping1=groupmapping; mapping1; mapping1=mapping1->next)
3941                 for (mapping2=mapping1->next; mapping2; mapping1=mapping2->next)
3942                         if (ntfs_same_sid(mapping1->sid,mapping2->sid)) {
3943                                 if (mapping1->xid != mapping2->xid)
3944                                         ambiguous = TRUE;
3945                         } else {
3946                                 if (mapping1->xid == mapping2->xid)
3947                                         ambiguous = TRUE;
3948                         }
3949         return (ambiguous);
3950 }
3951
3952 #endif
3953
3954 #if 0 /* not used any more */
3955
3956 /*
3957  *              Try and apply default single user mapping
3958  *      returns zero if successful
3959  */
3960
3961 static int ntfs_default_mapping(struct SECURITY_CONTEXT *scx)
3962 {
3963         const SECURITY_DESCRIPTOR_RELATIVE *phead;
3964         ntfs_inode *ni;
3965         char *securattr;
3966         const SID *usid;
3967         int res;
3968
3969         res = -1;
3970         ni = ntfs_pathname_to_inode(scx->vol, NULL, "/.");
3971         if (ni) {
3972                 securattr = getsecurityattr(scx->vol, ni);
3973                 if (securattr) {
3974                         phead = (const SECURITY_DESCRIPTOR_RELATIVE*)securattr;
3975                         usid = (SID*)&securattr[le32_to_cpu(phead->owner)];
3976                         if (ntfs_is_user_sid(usid))
3977                                 res = ntfs_do_default_mapping(scx,
3978                                                 scx->uid, scx->gid, usid);
3979                         free(securattr);
3980                 }
3981                 ntfs_inode_close(ni);
3982         }
3983         return (res);
3984 }
3985
3986 #endif
3987
3988 /*
3989  *              Basic read from a user mapping file on another volume
3990  */
3991
3992 static int basicread(void *fileid, char *buf, size_t size, off_t offs __attribute__((unused)))
3993 {
3994         return (read(*(int*)fileid, buf, size));
3995 }
3996
3997
3998 /*
3999  *              Read from a user mapping file on current NTFS partition
4000  */
4001
4002 static int localread(void *fileid, char *buf, size_t size, off_t offs)
4003 {
4004         return (ntfs_attr_data_read((ntfs_inode*)fileid,
4005                         AT_UNNAMED, 0, buf, size, offs));
4006 }
4007
4008 /*
4009  *              Build the user mapping
4010  *      - according to a mapping file if defined (or default present),
4011  *      - or try default single user mapping if possible
4012  *
4013  *      The mapping is specific to a mounted device
4014  *      No locking done, mounting assumed non multithreaded
4015  *
4016  *      returns zero if mapping is successful
4017  *      (failure should not be interpreted as an error)
4018  */
4019
4020 int ntfs_build_mapping(struct SECURITY_CONTEXT *scx, const char *usermap_path,
4021                         BOOL allowdef)
4022 {
4023         struct MAPLIST *item;
4024         struct MAPLIST *firstitem;
4025         struct MAPPING *usermapping;
4026         struct MAPPING *groupmapping;
4027         ntfs_inode *ni;
4028         int fd;
4029         static struct {
4030                 u8 revision;
4031                 u8 levels;
4032                 be16 highbase;
4033                 be32 lowbase;
4034                 le32 level1;
4035                 le32 level2;
4036                 le32 level3;
4037                 le32 level4;
4038                 le32 level5;
4039         } defmap = {
4040                 1, 5, const_cpu_to_be16(0), const_cpu_to_be32(5),
4041                 const_cpu_to_le32(21),
4042                 const_cpu_to_le32(DEFSECAUTH1), const_cpu_to_le32(DEFSECAUTH2),
4043                 const_cpu_to_le32(DEFSECAUTH3), const_cpu_to_le32(DEFSECBASE)
4044         } ;
4045
4046         /* be sure not to map anything until done */
4047         scx->mapping[MAPUSERS] = (struct MAPPING*)NULL;
4048         scx->mapping[MAPGROUPS] = (struct MAPPING*)NULL;
4049
4050         if (!usermap_path) usermap_path = MAPPINGFILE;
4051         if (usermap_path[0] == '/') {
4052                 fd = open(usermap_path,O_RDONLY);
4053                 if (fd > 0) {
4054                         firstitem = ntfs_read_mapping(basicread, (void*)&fd);
4055                         close(fd);
4056                 } else
4057                         firstitem = (struct MAPLIST*)NULL;
4058         } else {
4059                 ni = ntfs_pathname_to_inode(scx->vol, NULL, usermap_path);
4060                 if (ni) {
4061                         firstitem = ntfs_read_mapping(localread, ni);
4062                         ntfs_inode_close(ni);
4063                 } else
4064                         firstitem = (struct MAPLIST*)NULL;
4065         }
4066
4067
4068         if (firstitem) {
4069                 usermapping = ntfs_do_user_mapping(firstitem);
4070                 groupmapping = ntfs_do_group_mapping(firstitem);
4071                 if (usermapping && groupmapping) {
4072                         scx->mapping[MAPUSERS] = usermapping;
4073                         scx->mapping[MAPGROUPS] = groupmapping;
4074                 } else
4075                         ntfs_log_error("There were no valid user or no valid group\n");
4076                 /* now we can free the memory copy of input text */
4077                 /* and rely on internal representation */
4078                 while (firstitem) {
4079                         item = firstitem->next;
4080                         free(firstitem);
4081                         firstitem = item;
4082                 }
4083         } else {
4084                         /* no mapping file, try a default mapping */
4085                 if (allowdef) {
4086                         if (!ntfs_do_default_mapping(scx,
4087                                         0, 0, (const SID*)&defmap))
4088                                 ntfs_log_info("Using default user mapping\n");
4089                 }
4090         }
4091         return (!scx->mapping[MAPUSERS] || link_group_members(scx));
4092 }
4093
4094 #ifdef HAVE_SETXATTR    /* extended attributes interface required */
4095
4096 /*
4097  *              Get the ntfs attribute into an extended attribute
4098  *      The attribute is returned according to cpu endianness
4099  */
4100
4101 int ntfs_get_ntfs_attrib(ntfs_inode *ni, char *value, size_t size)
4102 {
4103         u32 attrib;
4104         size_t outsize;
4105
4106         outsize = 0;    /* default to no data and no error */
4107         if (ni) {
4108                 attrib = le32_to_cpu(ni->flags);
4109                 if (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY)
4110                         attrib |= const_le32_to_cpu(FILE_ATTR_DIRECTORY);
4111                 else
4112                         attrib &= ~const_le32_to_cpu(FILE_ATTR_DIRECTORY);
4113                 if (!attrib)
4114                         attrib |= const_le32_to_cpu(FILE_ATTR_NORMAL);
4115                 outsize = sizeof(FILE_ATTR_FLAGS);
4116                 if (size >= outsize) {
4117                         if (value)
4118                                 memcpy(value,&attrib,outsize);
4119                         else
4120                                 errno = EINVAL;
4121                 }
4122         }
4123         return (outsize ? (int)outsize : -errno);
4124 }
4125
4126 /*
4127  *              Get the ntfs attributes from an inode
4128  *      The attributes are returned according to cpu endianness
4129  *
4130  *      Returns the attributes if successful (cannot be zero)
4131  *              0 if failed (errno to tell why)
4132  */
4133
4134 u32 ntfs_get_inode_attributes(ntfs_inode *ni)
4135 {
4136         u32 attrib = -1;
4137
4138         if (ni) {
4139                 attrib = le32_to_cpu(ni->flags);
4140                 if (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY)
4141                         attrib |= const_le32_to_cpu(FILE_ATTR_DIRECTORY);
4142                 else
4143                         attrib &= ~const_le32_to_cpu(FILE_ATTR_DIRECTORY);
4144                 if (!attrib)
4145                         attrib |= const_le32_to_cpu(FILE_ATTR_NORMAL);
4146         } else
4147                 errno = EINVAL;
4148         return (attrib);
4149 }
4150
4151 /*
4152  *              Set the ntfs attributes on an inode
4153  *      The attribute is expected according to cpu endianness
4154  *
4155  *      Returns 0 if successful
4156  *              -1 if failed (errno to tell why)
4157  */
4158
4159 int ntfs_set_inode_attributes(ntfs_inode *ni, u32 attrib)
4160 {
4161         le32 settable;
4162         ATTR_FLAGS dirflags;
4163         int res;
4164
4165         res = -1;
4166         if (ni) {
4167                 settable = FILE_ATTR_SETTABLE;
4168                 res = 0;
4169                 if (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY) {
4170                         /*
4171                          * Accept changing compression for a directory
4172                          * and set index root accordingly
4173                          */
4174                         settable |= FILE_ATTR_COMPRESSED;
4175                         if ((ni->flags ^ cpu_to_le32(attrib))
4176                                      & FILE_ATTR_COMPRESSED) {
4177                                 if (ni->flags & FILE_ATTR_COMPRESSED)
4178                                         dirflags = const_cpu_to_le16(0);
4179                                 else
4180                                         dirflags = ATTR_IS_COMPRESSED;
4181                                 res = ntfs_attr_set_flags(ni, AT_INDEX_ROOT,
4182                                         NTFS_INDEX_I30, 4, dirflags,
4183                                         ATTR_COMPRESSION_MASK);
4184                         }
4185                 }
4186                 if (!res) {
4187                         ni->flags = (ni->flags & ~settable)
4188                                  | (cpu_to_le32(attrib) & settable);
4189                         NInoFileNameSetDirty(ni);
4190                         NInoSetDirty(ni);
4191                 }
4192         } else
4193                 errno = EINVAL;
4194         return (res);
4195 }
4196
4197 /*
4198  *              Return the ntfs attribute into an extended attribute
4199  *      The attribute is expected according to cpu endianness
4200  *
4201  *      Returns 0, or -1 if there is a problem
4202  */
4203
4204 int ntfs_set_ntfs_attrib(ntfs_inode *ni,
4205                         const char *value, size_t size, int flags)
4206 {
4207         u32 attrib;
4208         int res;
4209
4210         res = -1;
4211         if (ni && value && (size >= sizeof(FILE_ATTR_FLAGS))) {
4212                 if (!(flags & XATTR_CREATE)) {
4213                         /* copy to avoid alignment problems */
4214                         memcpy(&attrib,value,sizeof(FILE_ATTR_FLAGS));
4215                         res = ntfs_set_inode_attributes(ni, attrib);
4216                 } else
4217                         errno = EEXIST;
4218         } else
4219                 errno = EINVAL;
4220         return (res ? -1 : 0);
4221 }
4222
4223 #endif /* HAVE_SETXATTR */
4224
4225 /*
4226  *      Open $Secure once for all
4227  *      returns zero if it succeeds
4228  *              non-zero if it fails. This is not an error (on NTFS v1.x)
4229  */
4230
4231
4232 int ntfs_open_secure(ntfs_volume *vol)
4233 {
4234         ntfs_inode *ni;
4235         int res;
4236
4237         res = -1;
4238         vol->secure_ni = (ntfs_inode*)NULL;
4239         vol->secure_xsii = (ntfs_index_context*)NULL;
4240         vol->secure_xsdh = (ntfs_index_context*)NULL;
4241         if (vol->major_ver >= 3) {
4242                         /* make sure this is a genuine $Secure inode 9 */
4243                 ni = ntfs_pathname_to_inode(vol, NULL, "$Secure");
4244                 if (ni && (ni->mft_no == 9)) {
4245                         vol->secure_reentry = 0;
4246                         vol->secure_xsii = ntfs_index_ctx_get(ni,
4247                                                 sii_stream, 4);
4248                         vol->secure_xsdh = ntfs_index_ctx_get(ni,
4249                                                 sdh_stream, 4);
4250                         if (ni && vol->secure_xsii && vol->secure_xsdh) {
4251                                 vol->secure_ni = ni;
4252                                 res = 0;
4253                         }
4254                 }
4255         }
4256         return (res);
4257 }
4258
4259 /*
4260  *              Final cleaning
4261  *      Allocated memory is freed to facilitate the detection of memory leaks
4262  */
4263
4264 void ntfs_close_secure(struct SECURITY_CONTEXT *scx)
4265 {
4266         ntfs_volume *vol;
4267
4268         vol = scx->vol;
4269         if (vol->secure_ni) {
4270                 ntfs_index_ctx_put(vol->secure_xsii);
4271                 ntfs_index_ctx_put(vol->secure_xsdh);
4272                 ntfs_inode_close(vol->secure_ni);
4273                 
4274         }
4275         ntfs_free_mapping(scx->mapping);
4276         free_caches(scx);
4277 }
4278
4279 /*
4280  *              API for direct access to security descriptors
4281  *      based on Win32 API
4282  */
4283
4284
4285 /*
4286  *              Selective feeding of a security descriptor into user buffer
4287  *
4288  *      Returns TRUE if successful
4289  */
4290
4291 static BOOL feedsecurityattr(const char *attr, u32 selection,
4292                 char *buf, u32 buflen, u32 *psize)
4293 {
4294         const SECURITY_DESCRIPTOR_RELATIVE *phead;
4295         SECURITY_DESCRIPTOR_RELATIVE *pnhead;
4296         const ACL *pdacl;
4297         const ACL *psacl;
4298         const SID *pusid;
4299         const SID *pgsid;
4300         unsigned int offdacl;
4301         unsigned int offsacl;
4302         unsigned int offowner;
4303         unsigned int offgroup;
4304         unsigned int daclsz;
4305         unsigned int saclsz;
4306         unsigned int usidsz;
4307         unsigned int gsidsz;
4308         unsigned int size; /* size of requested attributes */
4309         BOOL ok;
4310         unsigned int pos;
4311         unsigned int avail;
4312         le16 control;
4313
4314         avail = 0;
4315         control = SE_SELF_RELATIVE;
4316         phead = (const SECURITY_DESCRIPTOR_RELATIVE*)attr;
4317         size = sizeof(SECURITY_DESCRIPTOR_RELATIVE);
4318
4319                 /* locate DACL if requested and available */
4320         if (phead->dacl && (selection & DACL_SECURITY_INFORMATION)) {
4321                 offdacl = le32_to_cpu(phead->dacl);
4322                 pdacl = (const ACL*)&attr[offdacl];
4323                 daclsz = le16_to_cpu(pdacl->size);
4324                 size += daclsz;
4325                 avail |= DACL_SECURITY_INFORMATION;
4326         } else
4327                 offdacl = daclsz = 0;
4328
4329                 /* locate owner if requested and available */
4330         offowner = le32_to_cpu(phead->owner);
4331         if (offowner && (selection & OWNER_SECURITY_INFORMATION)) {
4332                         /* find end of USID */
4333                 pusid = (const SID*)&attr[offowner];
4334                 usidsz = ntfs_sid_size(pusid);
4335                 size += usidsz;
4336                 avail |= OWNER_SECURITY_INFORMATION;
4337         } else
4338                 offowner = usidsz = 0;
4339
4340                 /* locate group if requested and available */
4341         offgroup = le32_to_cpu(phead->group);
4342         if (offgroup && (selection & GROUP_SECURITY_INFORMATION)) {
4343                         /* find end of GSID */
4344                 pgsid = (const SID*)&attr[offgroup];
4345                 gsidsz = ntfs_sid_size(pgsid);
4346                 size += gsidsz;
4347                 avail |= GROUP_SECURITY_INFORMATION;
4348         } else
4349                 offgroup = gsidsz = 0;
4350
4351                 /* locate SACL if requested and available */
4352         if (phead->sacl && (selection & SACL_SECURITY_INFORMATION)) {
4353                         /* find end of SACL */
4354                 offsacl = le32_to_cpu(phead->sacl);
4355                 psacl = (const ACL*)&attr[offsacl];
4356                 saclsz = le16_to_cpu(psacl->size);
4357                 size += saclsz;
4358                 avail |= SACL_SECURITY_INFORMATION;
4359         } else
4360                 offsacl = saclsz = 0;
4361
4362                 /*
4363                  * Check having enough size in destination buffer
4364                  * (required size is returned nevertheless so that
4365                  * the request can be reissued with adequate size)
4366                  */
4367         if (size > buflen) {
4368                 *psize = size;
4369                 errno = EINVAL;
4370                 ok = FALSE;
4371         } else {
4372                 if (selection & OWNER_SECURITY_INFORMATION)
4373                         control |= phead->control & SE_OWNER_DEFAULTED;
4374                 if (selection & GROUP_SECURITY_INFORMATION)
4375                         control |= phead->control & SE_GROUP_DEFAULTED;
4376                 if (selection & DACL_SECURITY_INFORMATION)
4377                         control |= phead->control
4378                                         & (SE_DACL_PRESENT
4379                                            | SE_DACL_DEFAULTED
4380                                            | SE_DACL_AUTO_INHERITED
4381                                            | SE_DACL_PROTECTED);
4382                 if (selection & SACL_SECURITY_INFORMATION)
4383                         control |= phead->control
4384                                         & (SE_SACL_PRESENT
4385                                            | SE_SACL_DEFAULTED
4386                                            | SE_SACL_AUTO_INHERITED
4387                                            | SE_SACL_PROTECTED);
4388                 /*
4389                  * copy header and feed new flags, even if no detailed data
4390                  */
4391                 memcpy(buf,attr,sizeof(SECURITY_DESCRIPTOR_RELATIVE));
4392                 pnhead = (SECURITY_DESCRIPTOR_RELATIVE*)buf;
4393                 pnhead->control = control;
4394                 pos = sizeof(SECURITY_DESCRIPTOR_RELATIVE);
4395
4396                 /* copy DACL if requested and available */
4397                 if (selection & avail & DACL_SECURITY_INFORMATION) {
4398                         pnhead->dacl = cpu_to_le32(pos);
4399                         memcpy(&buf[pos],&attr[offdacl],daclsz);
4400                         pos += daclsz;
4401                 } else
4402                         pnhead->dacl = const_cpu_to_le32(0);
4403
4404                 /* copy SACL if requested and available */
4405                 if (selection & avail & SACL_SECURITY_INFORMATION) {
4406                         pnhead->sacl = cpu_to_le32(pos);
4407                         memcpy(&buf[pos],&attr[offsacl],saclsz);
4408                         pos += saclsz;
4409                 } else
4410                         pnhead->sacl = const_cpu_to_le32(0);
4411
4412                 /* copy owner if requested and available */
4413                 if (selection & avail & OWNER_SECURITY_INFORMATION) {
4414                         pnhead->owner = cpu_to_le32(pos);
4415                         memcpy(&buf[pos],&attr[offowner],usidsz);
4416                         pos += usidsz;
4417                 } else
4418                         pnhead->owner = const_cpu_to_le32(0);
4419
4420                 /* copy group if requested and available */
4421                 if (selection & avail & GROUP_SECURITY_INFORMATION) {
4422                         pnhead->group = cpu_to_le32(pos);
4423                         memcpy(&buf[pos],&attr[offgroup],gsidsz);
4424                         pos += gsidsz;
4425                 } else
4426                         pnhead->group = const_cpu_to_le32(0);
4427                 if (pos != size)
4428                         ntfs_log_error("Error in security descriptor size\n");
4429                 *psize = size;
4430                 ok = TRUE;
4431         }
4432
4433         return (ok);
4434 }
4435
4436 /*
4437  *              Merge a new security descriptor into the old one
4438  *      and assign to designated file
4439  *
4440  *      Returns TRUE if successful
4441  */
4442
4443 static BOOL mergesecurityattr(ntfs_volume *vol, const char *oldattr,
4444                 const char *newattr, u32 selection, ntfs_inode *ni)
4445 {
4446         const SECURITY_DESCRIPTOR_RELATIVE *oldhead;
4447         const SECURITY_DESCRIPTOR_RELATIVE *newhead;
4448         SECURITY_DESCRIPTOR_RELATIVE *targhead;
4449         const ACL *pdacl;
4450         const ACL *psacl;
4451         const SID *powner;
4452         const SID *pgroup;
4453         int offdacl;
4454         int offsacl;
4455         int offowner;
4456         int offgroup;
4457         unsigned int size;
4458         le16 control;
4459         char *target;
4460         int pos;
4461         int oldattrsz;
4462         int newattrsz;
4463         BOOL ok;
4464
4465         ok = FALSE; /* default return */
4466         oldhead = (const SECURITY_DESCRIPTOR_RELATIVE*)oldattr;
4467         newhead = (const SECURITY_DESCRIPTOR_RELATIVE*)newattr;
4468         oldattrsz = ntfs_attr_size(oldattr);
4469         newattrsz = ntfs_attr_size(newattr);
4470         target = (char*)ntfs_malloc(oldattrsz + newattrsz);
4471         if (target) {
4472                 targhead = (SECURITY_DESCRIPTOR_RELATIVE*)target;
4473                 pos = sizeof(SECURITY_DESCRIPTOR_RELATIVE);
4474                 control = SE_SELF_RELATIVE;
4475                         /*
4476                          * copy new DACL if selected
4477                          * or keep old DACL if any
4478                          */
4479                 if ((selection & DACL_SECURITY_INFORMATION) ?
4480                                 newhead->dacl : oldhead->dacl) {
4481                         if (selection & DACL_SECURITY_INFORMATION) {
4482                                 offdacl = le32_to_cpu(newhead->dacl);
4483                                 pdacl = (const ACL*)&newattr[offdacl];
4484                         } else {
4485                                 offdacl = le32_to_cpu(oldhead->dacl);
4486                                 pdacl = (const ACL*)&oldattr[offdacl];
4487                         }
4488                         size = le16_to_cpu(pdacl->size);
4489                         memcpy(&target[pos], pdacl, size);
4490                         targhead->dacl = cpu_to_le32(pos);
4491                         pos += size;
4492                 } else
4493                         targhead->dacl = const_cpu_to_le32(0);
4494                 if (selection & DACL_SECURITY_INFORMATION) {
4495                         control |= newhead->control
4496                                         & (SE_DACL_PRESENT
4497                                            | SE_DACL_DEFAULTED
4498                                            | SE_DACL_PROTECTED);
4499                         if (newhead->control & SE_DACL_AUTO_INHERIT_REQ)
4500                                 control |= SE_DACL_AUTO_INHERITED;
4501                 } else
4502                         control |= oldhead->control
4503                                         & (SE_DACL_PRESENT
4504                                            | SE_DACL_DEFAULTED
4505                                            | SE_DACL_AUTO_INHERITED
4506                                            | SE_DACL_PROTECTED);
4507                         /*
4508                          * copy new SACL if selected
4509                          * or keep old SACL if any
4510                          */
4511                 if ((selection & SACL_SECURITY_INFORMATION) ?
4512                                 newhead->sacl : oldhead->sacl) {
4513                         if (selection & SACL_SECURITY_INFORMATION) {
4514                                 offsacl = le32_to_cpu(newhead->sacl);
4515                                 psacl = (const ACL*)&newattr[offsacl];
4516                         } else {
4517                                 offsacl = le32_to_cpu(oldhead->sacl);
4518                                 psacl = (const ACL*)&oldattr[offsacl];
4519                         }
4520                         size = le16_to_cpu(psacl->size);
4521                         memcpy(&target[pos], psacl, size);
4522                         targhead->sacl = cpu_to_le32(pos);
4523                         pos += size;
4524                 } else
4525                         targhead->sacl = const_cpu_to_le32(0);
4526                 if (selection & SACL_SECURITY_INFORMATION) {
4527                         control |= newhead->control
4528                                         & (SE_SACL_PRESENT
4529                                            | SE_SACL_DEFAULTED
4530                                            | SE_SACL_PROTECTED);
4531                         if (newhead->control & SE_SACL_AUTO_INHERIT_REQ)
4532                                 control |= SE_SACL_AUTO_INHERITED;
4533                 } else
4534                         control |= oldhead->control
4535                                         & (SE_SACL_PRESENT
4536                                            | SE_SACL_DEFAULTED
4537                                            | SE_SACL_AUTO_INHERITED
4538                                            | SE_SACL_PROTECTED);
4539                         /*
4540                          * copy new OWNER if selected
4541                          * or keep old OWNER if any
4542                          */
4543                 if ((selection & OWNER_SECURITY_INFORMATION) ?
4544                                 newhead->owner : oldhead->owner) {
4545                         if (selection & OWNER_SECURITY_INFORMATION) {
4546                                 offowner = le32_to_cpu(newhead->owner);
4547                                 powner = (const SID*)&newattr[offowner];
4548                         } else {
4549                                 offowner = le32_to_cpu(oldhead->owner);
4550                                 powner = (const SID*)&oldattr[offowner];
4551                         }
4552                         size = ntfs_sid_size(powner);
4553                         memcpy(&target[pos], powner, size);
4554                         targhead->owner = cpu_to_le32(pos);
4555                         pos += size;
4556                 } else
4557                         targhead->owner = const_cpu_to_le32(0);
4558                 if (selection & OWNER_SECURITY_INFORMATION)
4559                         control |= newhead->control & SE_OWNER_DEFAULTED;
4560                 else
4561                         control |= oldhead->control & SE_OWNER_DEFAULTED;
4562                         /*
4563                          * copy new GROUP if selected
4564                          * or keep old GROUP if any
4565                          */
4566                 if ((selection & GROUP_SECURITY_INFORMATION) ?
4567                                 newhead->group : oldhead->group) {
4568                         if (selection & GROUP_SECURITY_INFORMATION) {
4569                                 offgroup = le32_to_cpu(newhead->group);
4570                                 pgroup = (const SID*)&newattr[offgroup];
4571                                 control |= newhead->control
4572                                                  & SE_GROUP_DEFAULTED;
4573                         } else {
4574                                 offgroup = le32_to_cpu(oldhead->group);
4575                                 pgroup = (const SID*)&oldattr[offgroup];
4576                                 control |= oldhead->control
4577                                                  & SE_GROUP_DEFAULTED;
4578                         }
4579                         size = ntfs_sid_size(pgroup);
4580                         memcpy(&target[pos], pgroup, size);
4581                         targhead->group = cpu_to_le32(pos);
4582                         pos += size;
4583                 } else
4584                         targhead->group = const_cpu_to_le32(0);
4585                 if (selection & GROUP_SECURITY_INFORMATION)
4586                         control |= newhead->control & SE_GROUP_DEFAULTED;
4587                 else
4588                         control |= oldhead->control & SE_GROUP_DEFAULTED;
4589                 targhead->revision = SECURITY_DESCRIPTOR_REVISION;
4590                 targhead->alignment = 0;
4591                 targhead->control = control;
4592                 ok = !update_secur_descr(vol, target, ni);
4593                 free(target);
4594         }
4595         return (ok);
4596 }
4597
4598 int ntfs_get_inode_security(ntfs_inode *ni, u32 selection,
4599                             char *buf, u32 buflen, u32 *psize)
4600 {
4601         int res;
4602         char *attr;
4603
4604         res = 0;
4605         if (ni) {
4606                 attr = getsecurityattr(ni->vol, ni);
4607                 if (attr) {
4608                         if (feedsecurityattr(attr, selection,
4609                                         buf, buflen, psize)) {
4610                                 if (test_nino_flag(ni, v3_Extensions)
4611                                     && ni->security_id)
4612                                         res = le32_to_cpu(
4613                                                 ni->security_id);
4614                                 else
4615                                         res = -1;
4616                         }
4617                         free(attr);
4618                 }
4619         } else
4620                 errno = EINVAL;
4621         return (res);
4622 }
4623
4624 int ntfs_set_inode_security(ntfs_inode *ni, u32 selection, const char *attr)
4625 {
4626         const SECURITY_DESCRIPTOR_RELATIVE *phead;
4627         int attrsz;
4628         BOOL missing;
4629         char *oldattr;
4630         int res;
4631
4632         res = -1; /* default return */
4633         if (ni) {
4634                 phead = (const SECURITY_DESCRIPTOR_RELATIVE*)attr;
4635                 attrsz = ntfs_attr_size(attr);
4636                 /* if selected, owner and group must be present or defaulted */
4637                 missing = ((selection & OWNER_SECURITY_INFORMATION)
4638                                 && !phead->owner
4639                                 && !(phead->control & SE_OWNER_DEFAULTED))
4640                         || ((selection & GROUP_SECURITY_INFORMATION)
4641                                 && !phead->group
4642                                 && !(phead->control & SE_GROUP_DEFAULTED));
4643                 if (!missing
4644                     && (phead->control & SE_SELF_RELATIVE)
4645                     && ntfs_valid_descr(attr, attrsz)) {
4646                         oldattr = getsecurityattr(ni->vol, ni);
4647                         if (oldattr) {
4648                                 if (mergesecurityattr(ni->vol, oldattr, attr,
4649                                                         selection, ni)) {
4650                                         res = 0;
4651                                 }
4652                                 free(oldattr);
4653                         }
4654                 } else
4655                         errno = EINVAL;
4656         } else
4657                 errno = EINVAL;
4658         return (res);
4659 }
4660
4661 /*
4662  *              Return the security descriptor of a file
4663  *      This is intended to be similar to GetFileSecurity() from Win32
4664  *      in order to facilitate the development of portable tools
4665  *
4666  *      returns zero if unsuccessful (following Win32 conventions)
4667  *              -1 if no securid
4668  *              the securid if any
4669  *
4670  *  The Win32 API is :
4671  *
4672  *  BOOL WINAPI GetFileSecurity(
4673  *    __in          LPCTSTR lpFileName,
4674  *    __in          SECURITY_INFORMATION RequestedInformation,
4675  *    __out_opt     PSECURITY_DESCRIPTOR pSecurityDescriptor,
4676  *    __in          DWORD nLength,
4677  *    __out         LPDWORD lpnLengthNeeded
4678  *  );
4679  *
4680  */
4681
4682 int ntfs_get_file_security(struct SECURITY_API *scapi,
4683                 const char *path, u32 selection,
4684                 char *buf, u32 buflen, u32 *psize)
4685 {
4686         ntfs_inode *ni;
4687         char *attr;
4688         int res;
4689
4690         res = 0; /* default return */
4691         if (scapi && (scapi->magic == MAGIC_API)) {
4692                 ni = ntfs_pathname_to_inode(scapi->security.vol, NULL, path);
4693                 if (ni) {
4694                         attr = getsecurityattr(scapi->security.vol, ni);
4695                         if (attr) {
4696                                 if (feedsecurityattr(attr,selection,
4697                                                 buf,buflen,psize)) {
4698                                         if (test_nino_flag(ni, v3_Extensions)
4699                                             && ni->security_id)
4700                                                 res = le32_to_cpu(
4701                                                         ni->security_id);
4702                                         else
4703                                                 res = -1;
4704                                 }
4705                                 free(attr);
4706                         }
4707                         ntfs_inode_close(ni);
4708                 } else
4709                         errno = ENOENT;
4710                 if (!res) *psize = 0;
4711         } else
4712                 errno = EINVAL; /* do not clear *psize */
4713         return (res);
4714 }
4715
4716
4717 /*
4718  *              Set the security descriptor of a file or directory
4719  *      This is intended to be similar to SetFileSecurity() from Win32
4720  *      in order to facilitate the development of portable tools
4721  *
4722  *      returns zero if unsuccessful (following Win32 conventions)
4723  *              -1 if no securid
4724  *              the securid if any
4725  *
4726  *  The Win32 API is :
4727  *
4728  *  BOOL WINAPI SetFileSecurity(
4729  *    __in          LPCTSTR lpFileName,
4730  *    __in          SECURITY_INFORMATION SecurityInformation,
4731  *    __in          PSECURITY_DESCRIPTOR pSecurityDescriptor
4732  *  );
4733  */
4734
4735 int ntfs_set_file_security(struct SECURITY_API *scapi,
4736                 const char *path, u32 selection, const char *attr)
4737 {
4738         const SECURITY_DESCRIPTOR_RELATIVE *phead;
4739         ntfs_inode *ni;
4740         int attrsz;
4741         BOOL missing;
4742         char *oldattr;
4743         int res;
4744
4745         res = 0; /* default return */
4746         if (scapi && (scapi->magic == MAGIC_API) && attr) {
4747                 phead = (const SECURITY_DESCRIPTOR_RELATIVE*)attr;
4748                 attrsz = ntfs_attr_size(attr);
4749                 /* if selected, owner and group must be present or defaulted */
4750                 missing = ((selection & OWNER_SECURITY_INFORMATION)
4751                                 && !phead->owner
4752                                 && !(phead->control & SE_OWNER_DEFAULTED))
4753                         || ((selection & GROUP_SECURITY_INFORMATION)
4754                                 && !phead->group
4755                                 && !(phead->control & SE_GROUP_DEFAULTED));
4756                 if (!missing
4757                     && (phead->control & SE_SELF_RELATIVE)
4758                     && ntfs_valid_descr(attr, attrsz)) {
4759                         ni = ntfs_pathname_to_inode(scapi->security.vol,
4760                                 NULL, path);
4761                         if (ni) {
4762                                 oldattr = getsecurityattr(scapi->security.vol,
4763                                                 ni);
4764                                 if (oldattr) {
4765                                         if (mergesecurityattr(
4766                                                 scapi->security.vol,
4767                                                 oldattr, attr,
4768                                                 selection, ni)) {
4769                                                 if (test_nino_flag(ni,
4770                                                             v3_Extensions))
4771                                                         res = le32_to_cpu(
4772                                                             ni->security_id);
4773                                                 else
4774                                                         res = -1;
4775                                         }
4776                                         free(oldattr);
4777                                 }
4778                                 ntfs_inode_close(ni);
4779                         }
4780                 } else
4781                         errno = EINVAL;
4782         } else
4783                 errno = EINVAL;
4784         return (res);
4785 }
4786
4787
4788 /*
4789  *              Return the attributes of a file
4790  *      This is intended to be similar to GetFileAttributes() from Win32
4791  *      in order to facilitate the development of portable tools
4792  *
4793  *      returns -1 if unsuccessful (Win32 : INVALID_FILE_ATTRIBUTES)
4794  *
4795  *  The Win32 API is :
4796  *
4797  *  DWORD WINAPI GetFileAttributes(
4798  *   __in  LPCTSTR lpFileName
4799  *  );
4800  */
4801
4802 int ntfs_get_file_attributes(struct SECURITY_API *scapi, const char *path)
4803 {
4804         ntfs_inode *ni;
4805         s32 attrib;
4806
4807         attrib = -1; /* default return */
4808         if (scapi && (scapi->magic == MAGIC_API) && path) {
4809                 ni = ntfs_pathname_to_inode(scapi->security.vol, NULL, path);
4810                 if (ni) {
4811                         attrib = ntfs_get_inode_attributes(ni);
4812                         ntfs_inode_close(ni);
4813                 } else
4814                         errno = ENOENT;
4815         } else
4816                 errno = EINVAL; /* do not clear *psize */
4817         return (attrib);
4818 }
4819
4820
4821 /*
4822  *              Set attributes to a file or directory
4823  *      This is intended to be similar to SetFileAttributes() from Win32
4824  *      in order to facilitate the development of portable tools
4825  *
4826  *      Only a few flags can be set (same list as Win32)
4827  *
4828  *      returns zero if unsuccessful (following Win32 conventions)
4829  *              nonzero if successful
4830  *
4831  *  The Win32 API is :
4832  *
4833  *  BOOL WINAPI SetFileAttributes(
4834  *    __in  LPCTSTR lpFileName,
4835  *    __in  DWORD dwFileAttributes
4836  *  );
4837  */
4838
4839 BOOL ntfs_set_file_attributes(struct SECURITY_API *scapi,
4840                 const char *path, s32 attrib)
4841 {
4842         ntfs_inode *ni;
4843         int res;
4844
4845         res = 0; /* default return */
4846         if (scapi && (scapi->magic == MAGIC_API) && path) {
4847                 ni = ntfs_pathname_to_inode(scapi->security.vol, NULL, path);
4848                 if (ni) {
4849                         /* Win32 convention here : -1 means successful */
4850                         if (!ntfs_set_inode_attributes(ni, attrib))
4851                                 res = -1;
4852                         if (ntfs_inode_close(ni))
4853                                 res = 0;
4854                 } else
4855                         errno = ENOENT;
4856         }
4857         return (res);
4858 }
4859
4860
4861 BOOL ntfs_read_directory(struct SECURITY_API *scapi,
4862                 const char *path, ntfs_filldir_t callback, void *context)
4863 {
4864         ntfs_inode *ni;
4865         BOOL ok;
4866         s64 pos;
4867
4868         ok = FALSE; /* default return */
4869         if (scapi && (scapi->magic == MAGIC_API) && callback) {
4870                 ni = ntfs_pathname_to_inode(scapi->security.vol, NULL, path);
4871                 if (ni) {
4872                         if (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY) {
4873                                 pos = 0;
4874                                 ntfs_readdir(ni,&pos,context,callback);
4875                                 ok = !ntfs_inode_close(ni);
4876                         } else {
4877                                 ntfs_inode_close(ni);
4878                                 errno = ENOTDIR;
4879                         }
4880                 } else
4881                         errno = ENOENT;
4882         } else
4883                 errno = EINVAL; /* do not clear *psize */
4884         return (ok);
4885 }
4886
4887 /*
4888  *              read $SDS (for auditing security data)
4889  *
4890  *      Returns the number or read bytes, or -1 if there is an error
4891  */
4892
4893 int ntfs_read_sds(struct SECURITY_API *scapi,
4894                 char *buf, u32 size, u32 offset)
4895 {
4896         int got;
4897
4898         got = -1; /* default return */
4899         if (scapi && (scapi->magic == MAGIC_API)) {
4900                 if (scapi->security.vol->secure_ni)
4901                         got = ntfs_attr_data_read(scapi->security.vol->secure_ni,
4902                                 STREAM_SDS, 4, buf, size, offset);
4903                 else
4904                         errno = EOPNOTSUPP;
4905         } else
4906                 errno = EINVAL;
4907         return (got);
4908 }
4909
4910 /*
4911  *              read $SII (for auditing security data)
4912  *
4913  *      Returns next entry, or NULL if there is an error
4914  */
4915
4916 INDEX_ENTRY *ntfs_read_sii(struct SECURITY_API *scapi,
4917                 INDEX_ENTRY *entry)
4918 {
4919         SII_INDEX_KEY key;
4920         INDEX_ENTRY *ret;
4921         BOOL found;
4922         ntfs_index_context *xsii;
4923
4924         ret = (INDEX_ENTRY*)NULL; /* default return */
4925         if (scapi && (scapi->magic == MAGIC_API)) {
4926                 xsii = scapi->security.vol->secure_xsii;
4927                 if (xsii) {
4928                         if (!entry) {
4929                                 key.security_id = const_cpu_to_le32(0);
4930                                 found = !ntfs_index_lookup((char*)&key,
4931                                                 sizeof(SII_INDEX_KEY), xsii);
4932                                 /* not supposed to find */
4933                                 if (!found && (errno == ENOENT))
4934                                         ret = xsii->entry;
4935                         } else
4936                                 ret = ntfs_index_next(entry,xsii);
4937                         if (!ret)
4938                                 errno = ENODATA;
4939                 } else
4940                         errno = EOPNOTSUPP;
4941         } else
4942                 errno = EINVAL;
4943         return (ret);
4944 }
4945
4946 /*
4947  *              read $SDH (for auditing security data)
4948  *
4949  *      Returns next entry, or NULL if there is an error
4950  */
4951
4952 INDEX_ENTRY *ntfs_read_sdh(struct SECURITY_API *scapi,
4953                 INDEX_ENTRY *entry)
4954 {
4955         SDH_INDEX_KEY key;
4956         INDEX_ENTRY *ret;
4957         BOOL found;
4958         ntfs_index_context *xsdh;
4959
4960         ret = (INDEX_ENTRY*)NULL; /* default return */
4961         if (scapi && (scapi->magic == MAGIC_API)) {
4962                 xsdh = scapi->security.vol->secure_xsdh;
4963                 if (xsdh) {
4964                         if (!entry) {
4965                                 key.hash = const_cpu_to_le32(0);
4966                                 key.security_id = const_cpu_to_le32(0);
4967                                 found = !ntfs_index_lookup((char*)&key,
4968                                                 sizeof(SDH_INDEX_KEY), xsdh);
4969                                 /* not supposed to find */
4970                                 if (!found && (errno == ENOENT))
4971                                         ret = xsdh->entry;
4972                         } else
4973                                 ret = ntfs_index_next(entry,xsdh);
4974                         if (!ret)
4975                                 errno = ENODATA;
4976                 } else errno = ENOTSUP;
4977         } else
4978                 errno = EINVAL;
4979         return (ret);
4980 }
4981
4982 /*
4983  *              Get the mapped user SID
4984  *      A buffer of 40 bytes has to be supplied
4985  *
4986  *      returns the size of the SID, or zero and errno set if not found
4987  */
4988
4989 int ntfs_get_usid(struct SECURITY_API *scapi, uid_t uid, char *buf)
4990 {
4991         const SID *usid;
4992         BIGSID defusid;
4993         int size;
4994
4995         size = 0;
4996         if (scapi && (scapi->magic == MAGIC_API)) {
4997                 usid = ntfs_find_usid(scapi->security.mapping[MAPUSERS], uid, (SID*)&defusid);
4998                 if (usid) {
4999                         size = ntfs_sid_size(usid);
5000                         memcpy(buf,usid,size);
5001                 } else
5002                         errno = ENODATA;
5003         } else
5004                 errno = EINVAL;
5005         return (size);
5006 }
5007
5008 /*
5009  *              Get the mapped group SID
5010  *      A buffer of 40 bytes has to be supplied
5011  *
5012  *      returns the size of the SID, or zero and errno set if not found
5013  */
5014
5015 int ntfs_get_gsid(struct SECURITY_API *scapi, gid_t gid, char *buf)
5016 {
5017         const SID *gsid;
5018         BIGSID defgsid;
5019         int size;
5020
5021         size = 0;
5022         if (scapi && (scapi->magic == MAGIC_API)) {
5023                 gsid = ntfs_find_gsid(scapi->security.mapping[MAPGROUPS], gid, (SID*)&defgsid);
5024                 if (gsid) {
5025                         size = ntfs_sid_size(gsid);
5026                         memcpy(buf,gsid,size);
5027                 } else
5028                         errno = ENODATA;
5029         } else
5030                 errno = EINVAL;
5031         return (size);
5032 }
5033
5034 /*
5035  *              Get the user mapped to a SID
5036  *
5037  *      returns the uid, or -1 if not found
5038  */
5039
5040 int ntfs_get_user(struct SECURITY_API *scapi, const SID *usid)
5041 {
5042         int uid;
5043
5044         uid = -1;
5045         if (scapi && (scapi->magic == MAGIC_API) && ntfs_valid_sid(usid)) {
5046                 if (ntfs_same_sid(usid,adminsid))
5047                         uid = 0;
5048                 else {
5049                         uid = ntfs_find_user(scapi->security.mapping[MAPUSERS], usid);
5050                         if (!uid) {
5051                                 uid = -1;
5052                                 errno = ENODATA;
5053                         }
5054                 }
5055         } else
5056                 errno = EINVAL;
5057         return (uid);
5058 }
5059
5060 /*
5061  *              Get the group mapped to a SID
5062  *
5063  *      returns the uid, or -1 if not found
5064  */
5065
5066 int ntfs_get_group(struct SECURITY_API *scapi, const SID *gsid)
5067 {
5068         int gid;
5069
5070         gid = -1;
5071         if (scapi && (scapi->magic == MAGIC_API) && ntfs_valid_sid(gsid)) {
5072                 if (ntfs_same_sid(gsid,adminsid))
5073                         gid = 0;
5074                 else {
5075                         gid = ntfs_find_group(scapi->security.mapping[MAPGROUPS], gsid);
5076                         if (!gid) {
5077                                 gid = -1;
5078                                 errno = ENODATA;
5079                         }
5080                 }
5081         } else
5082                 errno = EINVAL;
5083         return (gid);
5084 }
5085
5086 /*
5087  *              Initializations before calling ntfs_get_file_security()
5088  *      ntfs_set_file_security() and ntfs_read_directory()
5089  *
5090  *      Only allowed for root
5091  *
5092  *      Returns an (obscured) struct SECURITY_API* needed for further calls
5093  *              NULL if not root (EPERM) or device is mounted (EBUSY)
5094  */
5095
5096 struct SECURITY_API *ntfs_initialize_file_security(const char *device,
5097                                 unsigned long flags)
5098 {
5099         ntfs_volume *vol;
5100         unsigned long mntflag;
5101         int mnt;
5102         struct SECURITY_API *scapi;
5103         struct SECURITY_CONTEXT *scx;
5104
5105         scapi = (struct SECURITY_API*)NULL;
5106         mnt = ntfs_check_if_mounted(device, &mntflag);
5107         if (!mnt && !(mntflag & NTFS_MF_MOUNTED) && !getuid()) {
5108                 vol = ntfs_mount(device, flags);
5109                 if (vol) {
5110                         scapi = (struct SECURITY_API*)
5111                                 ntfs_malloc(sizeof(struct SECURITY_API));
5112                         if (!ntfs_volume_get_free_space(vol)
5113                             && scapi) {
5114                                 scapi->magic = MAGIC_API;
5115                                 scapi->seccache = (struct PERMISSIONS_CACHE*)NULL;
5116                                 scx = &scapi->security;
5117                                 scx->vol = vol;
5118                                 scx->uid = getuid();
5119                                 scx->gid = getgid();
5120                                 scx->pseccache = &scapi->seccache;
5121                                 scx->vol->secure_flags = 0;
5122                                         /* accept no mapping and no $Secure */
5123                                 ntfs_build_mapping(scx,(const char*)NULL,TRUE);
5124                                 ntfs_open_secure(vol);
5125                         } else {
5126                                 if (scapi)
5127                                         free(scapi);
5128                                 else
5129                                         errno = ENOMEM;
5130                                 mnt = ntfs_umount(vol,FALSE);
5131                                 scapi = (struct SECURITY_API*)NULL;
5132                         }
5133                 }
5134         } else
5135                 if (getuid())
5136                         errno = EPERM;
5137                 else
5138                         errno = EBUSY;
5139         return (scapi);
5140 }
5141
5142 /*
5143  *              Leaving after ntfs_initialize_file_security()
5144  *
5145  *      Returns FALSE if FAILED
5146  */
5147
5148 BOOL ntfs_leave_file_security(struct SECURITY_API *scapi)
5149 {
5150         int ok;
5151         ntfs_volume *vol;
5152
5153         ok = FALSE;
5154         if (scapi && (scapi->magic == MAGIC_API) && scapi->security.vol) {
5155                 vol = scapi->security.vol;
5156                 ntfs_close_secure(&scapi->security);
5157                 free(scapi);
5158                 if (!ntfs_umount(vol, 0))
5159                         ok = TRUE;
5160         }
5161         return (ok);
5162 }
5163