Add support for new flavours of ACLs on AIX. (Untested.)
[gnulib.git] / tests / test-sameacls.c
1 /* Test whether two files have the same ACLs.
2    Copyright (C) 2008 Free Software Foundation, Inc.
3
4    This program is free software: you can redistribute it and/or modify
5    it under the terms of the GNU General Public License as published by
6    the Free Software Foundation; either version 3 of the License, or
7    (at your option) any later version.
8
9    This program is distributed in the hope that it will be useful,
10    but WITHOUT ANY WARRANTY; without even the implied warranty of
11    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12    GNU General Public License for more details.
13
14    You should have received a copy of the GNU General Public License
15    along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
16
17 /* Written by Bruno Haible <bruno@clisp.org>, 2008.  */
18
19 #include <config.h>
20
21 #include <errno.h>
22 #include <stdio.h>
23 #include <stdlib.h>
24 #include <string.h>
25 #include <sys/stat.h>
26
27 #if HAVE_ACL_GET_FILE || HAVE_ACL || HAVE_ACLX_GET || HAVE_STATACL
28 # include <sys/types.h>
29 # include <sys/acl.h>
30 #endif
31
32 #include "progname.h"
33 #include "read-file.h"
34 #include "xalloc.h"
35
36 #define ASSERT(expr) \
37   do                                                                         \
38     {                                                                        \
39       if (!(expr))                                                           \
40         {                                                                    \
41           fprintf (stderr, "%s:%d: assertion failed\n", __FILE__, __LINE__); \
42           fflush (stderr);                                                   \
43           abort ();                                                          \
44         }                                                                    \
45     }                                                                        \
46   while (0)
47
48 int
49 main (int argc, char *argv[])
50 {
51   const char *file1;
52   const char *file2;
53
54   set_program_name (argv[0]);
55
56   ASSERT (argc == 3);
57
58   file1 = argv[1];
59   file2 = argv[2];
60
61   /* Compare the contents of the two files.  */
62   {
63     size_t size1;
64     char *contents1;
65     size_t size2;
66     char *contents2;
67
68     contents1 = read_file (file1, &size1);
69     if (contents1 == NULL)
70       {
71         fprintf (stderr, "error reading file %s: errno = %d\n", file1, errno);
72         fflush (stderr);
73         abort ();
74       }
75     contents2 = read_file (file2, &size2);
76     if (contents2 == NULL)
77       {
78         fprintf (stderr, "error reading file %s: errno = %d\n", file2, errno);
79         fflush (stderr);
80         abort ();
81       }
82
83     if (size2 != size1)
84       {
85         fprintf (stderr, "files %s and %s have different sizes\n",
86                  file1, file2);
87         fflush (stderr);
88         abort ();
89       }
90     if (memcmp (contents1, contents2, size1) != 0)
91       {
92         fprintf (stderr, "files %s and %s have different contents\n",
93                  file1, file2);
94         fflush (stderr);
95         abort ();
96       }
97   }
98
99   /* Compare the access permissions of the two files, including ACLs.  */
100   {
101     struct stat statbuf1;
102     struct stat statbuf2;
103
104     if (stat (file1, &statbuf1) < 0)
105       {
106         fprintf (stderr, "error accessing file %s: errno = %d\n", file1, errno);
107         fflush (stderr);
108         abort ();
109       }
110     if (stat (file2, &statbuf2) < 0)
111       {
112         fprintf (stderr, "error accessing file %s: errno = %d\n", file2, errno);
113         fflush (stderr);
114         abort ();
115       }
116     if (statbuf1.st_mode != statbuf2.st_mode)
117       {
118         fprintf (stderr, "files %s and %s have different access modes: %03o and %03o\n",
119                  file1, file2,
120                 (unsigned int) statbuf1.st_mode, (unsigned int) statbuf2.st_mode);
121         return 1;
122       }
123   }
124   {
125 #if HAVE_ACL_GET_FILE /* Linux, FreeBSD, MacOS X, IRIX, Tru64 */
126     static const int types[] =
127       {
128         ACL_TYPE_ACCESS
129 # if HAVE_ACL_TYPE_EXTENDED /* MacOS X */
130         , ACL_TYPE_EXTENDED
131 # endif
132       };
133     int t;
134
135     for (t = 0; t < sizeof (types) / sizeof (types[0]); t++)
136       {
137         int type = types[t];
138         acl_t acl1;
139         char *text1;
140         int errno1;
141         acl_t acl2;
142         char *text2;
143         int errno2;
144
145         acl1 = acl_get_file (file1, type);
146         if (acl1 == (acl_t)NULL)
147           {
148             text1 = NULL;
149             errno1 = errno;
150           }
151         else
152           {
153             text1 = acl_to_text (acl1, NULL);
154             if (text1 == NULL)
155               errno1 = errno;
156             else
157               errno1 = 0;
158           }
159         acl2 = acl_get_file (file2, type);
160         if (acl2 == (acl_t)NULL)
161           {
162             text2 = NULL;
163             errno2 = errno;
164           }
165         else
166           {
167             text2 = acl_to_text (acl2, NULL);
168             if (text2 == NULL)
169               errno2 = errno;
170             else
171               errno2 = 0;
172           }
173
174         if (acl1 != (acl_t)NULL)
175           {
176             if (acl2 != (acl_t)NULL)
177               {
178                 if (text1 != NULL)
179                   {
180                     if (text2 != NULL)
181                       {
182                         if (strcmp (text1, text2) != 0)
183                           {
184                             fprintf (stderr, "files %s and %s have different ACLs:\n%s\n%s\n",
185                                      file1, file2, text1, text2);
186                             return 1;
187                           }
188                       }
189                     else
190                       {
191                         fprintf (stderr, "file %s has a valid ACL, but file %s has an invalid ACL\n",
192                                  file1, file2);
193                         return 1;
194                       }
195                   }
196                 else
197                   {
198                     if (text2 != NULL)
199                       {
200                         fprintf (stderr, "file %s has an invalid ACL, but file %s has a valid ACL\n",
201                                  file1, file2);
202                         return 1;
203                       }
204                     else
205                       {
206                         if (errno1 != errno2)
207                           {
208                             fprintf (stderr, "files %s and %s have differently invalid ACLs, errno = %d vs. %d\n",
209                                      file1, file2, errno1, errno2);
210                             return 1;
211                           }
212                       }
213                   }
214               }
215             else
216               {
217                 fprintf (stderr, "file %s has an ACL, but file %s has no ACL\n",
218                          file1, file2);
219                 return 1;
220               }
221           }
222         else
223           {
224             if (acl2 != (acl_t)NULL)
225               {
226                 fprintf (stderr, "file %s has no ACL, but file %s has an ACL\n",
227                          file1, file2);
228                 return 1;
229               }
230           }
231       }
232 #elif HAVE_ACL && defined GETACL /* Solaris, Cygwin, not HP-UX */
233   int count1;
234   int count2;
235
236   count1 = acl (file1, GETACLCNT, 0, NULL);
237   count2 = acl (file2, GETACLCNT, 0, NULL);
238
239   if (count1 < 0)
240     {
241       fprintf (stderr, "error accessing the ACLs of file %s\n", file1);
242       fflush (stderr);
243       abort ();
244     }
245   if (count2 < 0)
246     {
247       fprintf (stderr, "error accessing the ACLs of file %s\n", file2);
248       fflush (stderr);
249       abort ();
250     }
251   if (count1 != count2)
252     {
253       fprintf (stderr, "files %s and %s have different number of ACLs: %d and %d\n",
254                file1, file2, count1, count2);
255       return 1;
256     }
257   else
258     {
259       aclent_t *entries1 = XNMALLOC (count1, aclent_t);
260       aclent_t *entries2 = XNMALLOC (count2, aclent_t);
261       int i;
262
263       if (acl (file1, GETACL, count1, entries1) < count1)
264         {
265           fprintf (stderr, "error retrieving the ACLs of file %s\n", file1);
266           fflush (stderr);
267           abort ();
268         }
269       if (acl (file2, GETACL, count2, entries2) < count1)
270         {
271           fprintf (stderr, "error retrieving the ACLs of file %s\n", file2);
272           fflush (stderr);
273           abort ();
274         }
275       for (i = 0; i < count1; i++)
276         {
277           if (entries1[i].a_type != entries2[i].a_type)
278             {
279               fprintf (stderr, "files %s and %s: different ACL entry #%d: different types %d and %d\n",
280                        file1, file2, i, entries1[i].a_type, entries2[i].a_type);
281               return 1;
282             }
283           if (entries1[i].a_id != entries2[i].a_id)
284             {
285               fprintf (stderr, "files %s and %s: different ACL entry #%d: different ids %d and %d\n",
286                        file1, file2, i, (int)entries1[i].a_id, (int)entries2[i].a_id);
287               return 1;
288             }
289           if (entries1[i].a_perm != entries2[i].a_perm)
290             {
291               fprintf (stderr, "files %s and %s: different ACL entry #%d: different permissions %03o and %03o\n",
292                        file1, file2, i, (unsigned int) entries1[i].a_perm, (unsigned int) entries2[i].a_perm);
293               return 1;
294             }
295         }
296     }
297 # ifdef ACE_GETACL
298   count1 = acl (file1, ACE_GETACLCNT, 0, NULL);
299   if (count1 < 0 && errno == EINVAL)
300     count1 = 0;
301   count2 = acl (file2, ACE_GETACLCNT, 0, NULL);
302   if (count2 < 0 && errno == EINVAL)
303     count2 = 0;
304   if (count1 < 0)
305     {
306       fprintf (stderr, "error accessing the ACE-ACLs of file %s\n", file1);
307       fflush (stderr);
308       abort ();
309     }
310   if (count2 < 0)
311     {
312       fprintf (stderr, "error accessing the ACE-ACLs of file %s\n", file2);
313       fflush (stderr);
314       abort ();
315     }
316   if (count1 != count2)
317     {
318       fprintf (stderr, "files %s and %s have different number of ACE-ACLs: %d and %d\n",
319                file1, file2, count1, count2);
320       return 1;
321     }
322   else if (count1 > 0)
323     {
324       ace_t *entries1 = XNMALLOC (count1, ace_t);
325       ace_t *entries2 = XNMALLOC (count2, ace_t);
326       int i;
327
328       if (acl (file1, ACE_GETACL, count1, entries1) < count1)
329         {
330           fprintf (stderr, "error retrieving the ACE-ACLs of file %s\n", file1);
331           fflush (stderr);
332           abort ();
333         }
334       if (acl (file2, ACE_GETACL, count2, entries2) < count1)
335         {
336           fprintf (stderr, "error retrieving the ACE-ACLs of file %s\n", file2);
337           fflush (stderr);
338           abort ();
339         }
340       for (i = 0; i < count1; i++)
341         {
342           if (entries1[i].a_type != entries2[i].a_type)
343             {
344               fprintf (stderr, "files %s and %s: different ACE-ACL entry #%d: different types %d and %d\n",
345                        file1, file2, i, entries1[i].a_type, entries2[i].a_type);
346               return 1;
347             }
348           if (entries1[i].a_who != entries2[i].a_who)
349             {
350               fprintf (stderr, "files %s and %s: different ACE-ACL entry #%d: different ids %d and %d\n",
351                        file1, file2, i, (int)entries1[i].a_who, (int)entries2[i].a_who);
352               return 1;
353             }
354           if (entries1[i].a_access_mask != entries2[i].a_access_mask)
355             {
356               fprintf (stderr, "files %s and %s: different ACE-ACL entry #%d: different access masks %03o and %03o\n",
357                        file1, file2, i, (unsigned int) entries1[i].a_access_mask, (unsigned int) entries2[i].a_access_mask);
358               return 1;
359             }
360           if (entries1[i].a_flags != entries2[i].a_flags)
361             {
362               fprintf (stderr, "files %s and %s: different ACE-ACL entry #%d: different flags 0x%x and 0x%x\n",
363                        file1, file2, i, (unsigned int) entries1[i].a_flags, (unsigned int) entries2[i].a_flags);
364               return 1;
365             }
366         }
367     }
368 # endif
369 #elif HAVE_GETACL /* HP-UX */
370   int count1;
371   int count2;
372
373   count1 = getacl (file1, 0, NULL);
374   if (count1 < 0 && (errno == ENOSYS || errno == EOPNOTSUPP))
375     count1 = 0;
376   count2 = getacl (file2, 0, NULL);
377   if (count2 < 0 && (errno == ENOSYS || errno == EOPNOTSUPP))
378     count2 = 0;
379
380   if (count1 < 0)
381     {
382       fprintf (stderr, "error accessing the ACLs of file %s\n", file1);
383       fflush (stderr);
384       abort ();
385     }
386   if (count2 < 0)
387     {
388       fprintf (stderr, "error accessing the ACLs of file %s\n", file2);
389       fflush (stderr);
390       abort ();
391     }
392   if (count1 != count2)
393     {
394       fprintf (stderr, "files %s and %s have different number of ACLs: %d and %d\n",
395                file1, file2, count1, count2);
396       return 1;
397     }
398   else if (count1 > 0)
399     {
400       struct acl_entry *entries1 = XNMALLOC (count1, struct acl_entry);
401       struct acl_entry *entries2 = XNMALLOC (count2, struct acl_entry);
402       int i;
403
404       if (getacl (file1, count1, entries1) < count1)
405         {
406           fprintf (stderr, "error retrieving the ACLs of file %s\n", file1);
407           fflush (stderr);
408           abort ();
409         }
410       if (getacl (file2, count2, entries2) < count1)
411         {
412           fprintf (stderr, "error retrieving the ACLs of file %s\n", file2);
413           fflush (stderr);
414           abort ();
415         }
416       for (i = 0; i < count1; i++)
417         {
418           if (entries1[i].uid != entries2[i].uid)
419             {
420               fprintf (stderr, "files %s and %s: different ACL entry #%d: different uids %d and %d\n",
421                        file1, file2, i, (int)entries1[i].uid, (int)entries2[i].uid);
422               return 1;
423             }
424           if (entries1[i].gid != entries2[i].gid)
425             {
426               fprintf (stderr, "files %s and %s: different ACL entry #%d: different gids %d and %d\n",
427                        file1, file2, i, (int)entries1[i].gid, (int)entries2[i].gid);
428               return 1;
429             }
430           if (entries1[i].mode != entries2[i].mode)
431             {
432               fprintf (stderr, "files %s and %s: different ACL entry #%d: different permissions %03o and %03o\n",
433                        file1, file2, i, (unsigned int) entries1[i].mode, (unsigned int) entries2[i].mode);
434               return 1;
435             }
436         }
437     }
438 #elif HAVE_ACLX_GET /* AIX */
439   acl_type_t type1;
440   char acl1[1000];
441   size_t aclsize1 = sizeof (acl1);
442   mode_t mode1;
443   char text1[1000];
444   acl_type_t type2;
445   char acl2[1000];
446   size_t aclsize2 = sizeof (acl2);
447   mode_t mode2;
448   char text2[1000];
449
450   /* The docs say that type1 being 0 is equivalent to ACL_ANY, but it is not
451      true, in AIX 5.3.  */
452   type1.u64 = ACL_ANY;
453   if (aclx_get (file1, 0, &type1, acl1, &aclsize1, &mode1) < 0)
454     {
455       fprintf (stderr, "error accessing the ACLs of file %s\n", file1);
456       fflush (stderr);
457       abort ();
458     }
459   if (aclx_printStr (text1, sizeof (text1), acl1, aclsize1, type1, file1, 0) < 0)
460     {
461       fprintf (stderr, "cannot convert the ACLs of file %s to text\n", file1);
462       fflush (stderr);
463       abort ();
464     }
465
466   /* The docs say that type1 being 0 is equivalent to ACL_ANY, but it is not
467      true, in AIX 5.3.  */
468   type2.u64 = ACL_ANY;
469   if (aclx_get (file2, 0, &type2, acl2, &aclsize2, &mode2) < 0)
470     {
471       fprintf (stderr, "error accessing the ACLs of file %s\n", file2);
472       fflush (stderr);
473       abort ();
474     }
475   if (aclx_printStr (text2, sizeof (text2), acl2, aclsize2, type2, file2, 0) < 0)
476     {
477       fprintf (stderr, "cannot convert the ACLs of file %s to text\n", file2);
478       fflush (stderr);
479       abort ();
480     }
481
482   if (strcmp (text1, text2) != 0)
483     {
484       fprintf (stderr, "files %s and %s have different ACLs:\n%s\n%s\n",
485                file1, file2, text1, text2);
486       return 1;
487     }
488 #elif HAVE_STATACL /* older AIX */
489   union { struct acl a; char room[4096]; } acl1;
490   union { struct acl a; char room[4096]; } acl2;
491   unsigned int i;
492
493   if (statacl (file1, STX_NORMAL, &acl1.a, sizeof (acl1)) < 0)
494     {
495       fprintf (stderr, "error accessing the ACLs of file %s\n", file1);
496       fflush (stderr);
497       abort ();
498     }
499   if (statacl (file2, STX_NORMAL, &acl2.a, sizeof (acl2)) < 0)
500     {
501       fprintf (stderr, "error accessing the ACLs of file %s\n", file2);
502       fflush (stderr);
503       abort ();
504     }
505
506   if (acl1.a.acl_len != acl2.a.acl_len)
507     {
508       fprintf (stderr, "files %s and %s have different ACL lengths: %u and %u\n",
509                file1, file2, acl1.a.acl_len, acl2.a.acl_len);
510       return 1;
511     }
512   if (acl1.a.acl_mode != acl2.a.acl_mode)
513     {
514       fprintf (stderr, "files %s and %s have different ACL modes: %03o and %03o\n",
515                file1, file2, acl1.a.acl_mode, acl2.a.acl_mode);
516       return 1;
517     }
518   if (acl1.a.u_access != acl2.a.u_access
519       || acl1.a.g_access != acl2.a.g_access
520       || acl1.a.o_access != acl2.a.o_access)
521     {
522       fprintf (stderr, "files %s and %s have different ACL access masks: %03o %03o %03o and %03o %03o %03o\n",
523                file1, file2,
524                acl1.a.u_access, acl1.a.g_access, acl1.a.o_access,
525                acl2.a.u_access, acl2.a.g_access, acl2.a.o_access);
526       return 1;
527     }
528   if (memcmp (acl1.a.acl_ext, acl2.a.acl_ext, acl1.a.acl_len) != 0)
529     {
530       fprintf (stderr, "files %s and %s have different ACL entries\n",
531                file1, file2);
532       return 1;
533     }
534 #endif
535   }
536
537   return 0;
538 }