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