1 /* Test whether two files have the same ACLs.
2 Copyright (C) 2008-2020 Free Software Foundation, Inc.
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.
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.
14 You should have received a copy of the GNU General Public License
15 along with this program. If not, see <https://www.gnu.org/licenses/>. */
17 /* Written by Bruno Haible <bruno@clisp.org>, 2008. */
27 #if HAVE_ACL_GET_FILE || HAVE_FACL || HAVE_GETACL || HAVE_ACLX_GET || HAVE_STATACL || HAVE_ACLSORT
28 # include <sys/types.h>
32 # include <sys/types.h>
36 #include "read-file.h"
41 main (int argc
, char *argv
[])
51 /* Compare the contents of the two files. */
58 contents1
= read_file (file1
, 0, &size1
);
59 if (contents1
== NULL
)
61 fprintf (stderr
, "error reading file %s: errno = %d\n", file1
, errno
);
65 contents2
= read_file (file2
, 0, &size2
);
66 if (contents2
== NULL
)
68 fprintf (stderr
, "error reading file %s: errno = %d\n", file2
, errno
);
75 fprintf (stderr
, "files %s and %s have different sizes\n",
80 if (memcmp (contents1
, contents2
, size1
) != 0)
82 fprintf (stderr
, "files %s and %s have different contents\n",
92 /* Compare the access permissions of the two files, including ACLs. */
97 if (stat (file1
, &statbuf1
) < 0)
99 fprintf (stderr
, "error accessing file %s: errno = %d\n", file1
, errno
);
103 if (stat (file2
, &statbuf2
) < 0)
105 fprintf (stderr
, "error accessing file %s: errno = %d\n", file2
, errno
);
109 if (statbuf1
.st_mode
!= statbuf2
.st_mode
)
111 fprintf (stderr
, "files %s and %s have different access modes: %03o and %03o\n",
113 (unsigned int) statbuf1
.st_mode
, (unsigned int) statbuf2
.st_mode
);
118 #if HAVE_ACL_GET_FILE /* Linux, FreeBSD, Mac OS X, IRIX, Tru64 */
119 static const int types
[] =
122 # if HAVE_ACL_TYPE_EXTENDED /* Mac OS X */
128 for (t
= 0; t
< sizeof (types
) / sizeof (types
[0]); t
++)
138 acl1
= acl_get_file (file1
, type
);
139 if (acl1
== (acl_t
)NULL
)
146 text1
= acl_to_text (acl1
, NULL
);
152 acl2
= acl_get_file (file2
, type
);
153 if (acl2
== (acl_t
)NULL
)
160 text2
= acl_to_text (acl2
, NULL
);
167 if (acl1
!= (acl_t
)NULL
)
169 if (acl2
!= (acl_t
)NULL
)
175 if (strcmp (text1
, text2
) != 0)
177 fprintf (stderr
, "files %s and %s have different ACLs:\n%s\n%s\n",
178 file1
, file2
, text1
, text2
);
184 fprintf (stderr
, "file %s has a valid ACL, but file %s has an invalid ACL\n",
193 fprintf (stderr
, "file %s has an invalid ACL, but file %s has a valid ACL\n",
199 if (errno1
!= errno2
)
201 fprintf (stderr
, "files %s and %s have differently invalid ACLs, errno = %d vs. %d\n",
202 file1
, file2
, errno1
, errno2
);
210 fprintf (stderr
, "file %s has an ACL, but file %s has no ACL\n",
217 if (acl2
!= (acl_t
)NULL
)
219 fprintf (stderr
, "file %s has no ACL, but file %s has an ACL\n",
225 if (acl2
!= (acl_t
)NULL
)
228 if (acl1
!= (acl_t
)NULL
)
231 #elif HAVE_FACL && defined GETACL /* Solaris, Cygwin, not HP-UX */
235 count1
= acl (file1
, GETACLCNT
, 0, NULL
);
236 if (count1
< 0 && errno
== ENOSYS
) /* Can happen on Solaris 10 with ZFS */
238 count2
= acl (file2
, GETACLCNT
, 0, NULL
);
239 if (count2
< 0 && errno
== ENOSYS
) /* Can happen on Solaris 10 with ZFS */
244 fprintf (stderr
, "error accessing the ACLs of file %s\n", file1
);
250 fprintf (stderr
, "error accessing the ACLs of file %s\n", file2
);
254 if (count1
!= count2
)
256 fprintf (stderr
, "files %s and %s have different number of ACLs: %d and %d\n",
257 file1
, file2
, count1
, count2
);
262 aclent_t
*entries1
= XNMALLOC (count1
, aclent_t
);
263 aclent_t
*entries2
= XNMALLOC (count2
, aclent_t
);
266 if (count1
> 0 && acl (file1
, GETACL
, count1
, entries1
) < count1
)
268 fprintf (stderr
, "error retrieving the ACLs of file %s\n", file1
);
272 if (count2
> 0 && acl (file2
, GETACL
, count2
, entries2
) < count1
)
274 fprintf (stderr
, "error retrieving the ACLs of file %s\n", file2
);
278 for (i
= 0; i
< count1
; i
++)
280 if (entries1
[i
].a_type
!= entries2
[i
].a_type
)
282 fprintf (stderr
, "files %s and %s: different ACL entry #%d: different types %d and %d\n",
283 file1
, file2
, i
, entries1
[i
].a_type
, entries2
[i
].a_type
);
286 if (entries1
[i
].a_id
!= entries2
[i
].a_id
)
288 fprintf (stderr
, "files %s and %s: different ACL entry #%d: different ids %d and %d\n",
289 file1
, file2
, i
, (int)entries1
[i
].a_id
, (int)entries2
[i
].a_id
);
292 if (entries1
[i
].a_perm
!= entries2
[i
].a_perm
)
294 fprintf (stderr
, "files %s and %s: different ACL entry #%d: different permissions %03o and %03o\n",
295 file1
, file2
, i
, (unsigned int) entries1
[i
].a_perm
, (unsigned int) entries2
[i
].a_perm
);
303 count1
= acl (file1
, ACE_GETACLCNT
, 0, NULL
);
304 if (count1
< 0 && errno
== EINVAL
)
306 count2
= acl (file2
, ACE_GETACLCNT
, 0, NULL
);
307 if (count2
< 0 && errno
== EINVAL
)
311 fprintf (stderr
, "error accessing the ACE-ACLs of file %s\n", file1
);
317 fprintf (stderr
, "error accessing the ACE-ACLs of file %s\n", file2
);
322 ace_t
*entries1
= XNMALLOC (count1
, ace_t
);
323 ace_t
*entries2
= XNMALLOC (count2
, ace_t
);
327 ret
= acl (file1
, ACE_GETACL
, count1
, entries1
);
328 if (ret
< 0 && errno
== EINVAL
)
330 else if (ret
< count1
)
332 fprintf (stderr
, "error retrieving the ACE-ACLs of file %s\n", file1
);
336 ret
= acl (file2
, ACE_GETACL
, count2
, entries2
);
337 if (ret
< 0 && errno
== EINVAL
)
339 else if (ret
< count2
)
341 fprintf (stderr
, "error retrieving the ACE-ACLs of file %s\n", file2
);
346 if (count1
!= count2
)
348 fprintf (stderr
, "files %s and %s have different number of ACE-ACLs: %d and %d\n",
349 file1
, file2
, count1
, count2
);
353 for (i
= 0; i
< count1
; i
++)
355 if (entries1
[i
].a_type
!= entries2
[i
].a_type
)
357 fprintf (stderr
, "files %s and %s: different ACE-ACL entry #%d: different types %d and %d\n",
358 file1
, file2
, i
, entries1
[i
].a_type
, entries2
[i
].a_type
);
361 if (entries1
[i
].a_who
!= entries2
[i
].a_who
)
363 fprintf (stderr
, "files %s and %s: different ACE-ACL entry #%d: different ids %d and %d\n",
364 file1
, file2
, i
, (int)entries1
[i
].a_who
, (int)entries2
[i
].a_who
);
367 if (entries1
[i
].a_access_mask
!= entries2
[i
].a_access_mask
)
369 fprintf (stderr
, "files %s and %s: different ACE-ACL entry #%d: different access masks %03o and %03o\n",
370 file1
, file2
, i
, (unsigned int) entries1
[i
].a_access_mask
, (unsigned int) entries2
[i
].a_access_mask
);
373 if (entries1
[i
].a_flags
!= entries2
[i
].a_flags
)
375 fprintf (stderr
, "files %s and %s: different ACE-ACL entry #%d: different flags 0x%x and 0x%x\n",
376 file1
, file2
, i
, (unsigned int) entries1
[i
].a_flags
, (unsigned int) entries2
[i
].a_flags
);
384 #elif HAVE_GETACL /* HP-UX */
388 count1
= getacl (file1
, 0, NULL
);
390 && (errno
== ENOSYS
|| errno
== EOPNOTSUPP
|| errno
== ENOTSUP
))
392 count2
= getacl (file2
, 0, NULL
);
394 && (errno
== ENOSYS
|| errno
== EOPNOTSUPP
|| errno
== ENOTSUP
))
399 fprintf (stderr
, "error accessing the ACLs of file %s\n", file1
);
405 fprintf (stderr
, "error accessing the ACLs of file %s\n", file2
);
409 if (count1
!= count2
)
411 fprintf (stderr
, "files %s and %s have different number of ACLs: %d and %d\n",
412 file1
, file2
, count1
, count2
);
417 struct acl_entry
*entries1
= XNMALLOC (count1
, struct acl_entry
);
418 struct acl_entry
*entries2
= XNMALLOC (count2
, struct acl_entry
);
421 if (getacl (file1
, count1
, entries1
) < count1
)
423 fprintf (stderr
, "error retrieving the ACLs of file %s\n", file1
);
427 if (getacl (file2
, count2
, entries2
) < count1
)
429 fprintf (stderr
, "error retrieving the ACLs of file %s\n", file2
);
433 for (i
= 0; i
< count1
; i
++)
435 if (entries1
[i
].uid
!= entries2
[i
].uid
)
437 fprintf (stderr
, "files %s and %s: different ACL entry #%d: different uids %d and %d\n",
438 file1
, file2
, i
, (int)entries1
[i
].uid
, (int)entries2
[i
].uid
);
441 if (entries1
[i
].gid
!= entries2
[i
].gid
)
443 fprintf (stderr
, "files %s and %s: different ACL entry #%d: different gids %d and %d\n",
444 file1
, file2
, i
, (int)entries1
[i
].gid
, (int)entries2
[i
].gid
);
447 if (entries1
[i
].mode
!= entries2
[i
].mode
)
449 fprintf (stderr
, "files %s and %s: different ACL entry #%d: different permissions %03o and %03o\n",
450 file1
, file2
, i
, (unsigned int) entries1
[i
].mode
, (unsigned int) entries2
[i
].mode
);
458 # if HAVE_ACLV_H /* HP-UX >= 11.11 */
460 struct acl dummy_entries
[NACLVENTRIES
];
462 count1
= acl ((char *) file1
, ACL_CNT
, NACLVENTRIES
, dummy_entries
);
464 && (errno
== ENOSYS
|| errno
== EOPNOTSUPP
|| errno
== EINVAL
))
466 count2
= acl ((char *) file2
, ACL_CNT
, NACLVENTRIES
, dummy_entries
);
468 && (errno
== ENOSYS
|| errno
== EOPNOTSUPP
|| errno
== EINVAL
))
474 fprintf (stderr
, "error accessing the ACLs of file %s\n", file1
);
480 fprintf (stderr
, "error accessing the ACLs of file %s\n", file2
);
484 if (count1
!= count2
)
486 fprintf (stderr
, "files %s and %s have different number of ACLs: %d and %d\n",
487 file1
, file2
, count1
, count2
);
492 struct acl
*entries1
= XNMALLOC (count1
, struct acl
);
493 struct acl
*entries2
= XNMALLOC (count2
, struct acl
);
496 if (acl ((char *) file1
, ACL_GET
, count1
, entries1
) < count1
)
498 fprintf (stderr
, "error retrieving the ACLs of file %s\n", file1
);
502 if (acl ((char *) file2
, ACL_GET
, count2
, entries2
) < count1
)
504 fprintf (stderr
, "error retrieving the ACLs of file %s\n", file2
);
508 for (i
= 0; i
< count1
; i
++)
510 if (entries1
[i
].a_type
!= entries2
[i
].a_type
)
512 fprintf (stderr
, "files %s and %s: different ACL entry #%d: different types %d and %d\n",
513 file1
, file2
, i
, entries1
[i
].a_type
, entries2
[i
].a_type
);
516 if (entries1
[i
].a_id
!= entries2
[i
].a_id
)
518 fprintf (stderr
, "files %s and %s: different ACL entry #%d: different ids %d and %d\n",
519 file1
, file2
, i
, (int)entries1
[i
].a_id
, (int)entries2
[i
].a_id
);
522 if (entries1
[i
].a_perm
!= entries2
[i
].a_perm
)
524 fprintf (stderr
, "files %s and %s: different ACL entry #%d: different permissions %03o and %03o\n",
525 file1
, file2
, i
, (unsigned int) entries1
[i
].a_perm
, (unsigned int) entries2
[i
].a_perm
);
533 #elif HAVE_ACLX_GET /* AIX */
536 size_t aclsize1
= sizeof (acl1
);
539 size_t textsize1
= sizeof (text1
);
542 size_t aclsize2
= sizeof (acl2
);
545 size_t textsize2
= sizeof (text2
);
547 /* The docs say that type1 being 0 is equivalent to ACL_ANY, but it is not
550 if (aclx_get (file1
, 0, &type1
, acl1
, &aclsize1
, &mode1
) < 0)
556 fprintf (stderr
, "error accessing the ACLs of file %s\n", file1
);
562 if (aclx_printStr (text1
, &textsize1
, acl1
, aclsize1
, type1
, file1
, 0) < 0)
564 fprintf (stderr
, "cannot convert the ACLs of file %s to text\n", file1
);
569 /* The docs say that type2 being 0 is equivalent to ACL_ANY, but it is not
572 if (aclx_get (file2
, 0, &type2
, acl2
, &aclsize2
, &mode2
) < 0)
578 fprintf (stderr
, "error accessing the ACLs of file %s\n", file2
);
584 if (aclx_printStr (text2
, &textsize2
, acl2
, aclsize2
, type2
, file2
, 0) < 0)
586 fprintf (stderr
, "cannot convert the ACLs of file %s to text\n", file2
);
591 if (strcmp (text1
, text2
) != 0)
593 fprintf (stderr
, "files %s and %s have different ACLs:\n%s\n%s\n",
594 file1
, file2
, text1
, text2
);
597 #elif HAVE_STATACL /* older AIX */
598 union { struct acl a
; char room
[4096]; } acl1
;
599 union { struct acl a
; char room
[4096]; } acl2
;
602 if (statacl (file1
, STX_NORMAL
, &acl1
.a
, sizeof (acl1
)) < 0)
604 fprintf (stderr
, "error accessing the ACLs of file %s\n", file1
);
608 if (statacl (file2
, STX_NORMAL
, &acl2
.a
, sizeof (acl2
)) < 0)
610 fprintf (stderr
, "error accessing the ACLs of file %s\n", file2
);
615 if (acl1
.a
.acl_len
!= acl2
.a
.acl_len
)
617 fprintf (stderr
, "files %s and %s have different ACL lengths: %u and %u\n",
618 file1
, file2
, acl1
.a
.acl_len
, acl2
.a
.acl_len
);
621 if (acl1
.a
.acl_mode
!= acl2
.a
.acl_mode
)
623 fprintf (stderr
, "files %s and %s have different ACL modes: %03o and %03o\n",
624 file1
, file2
, acl1
.a
.acl_mode
, acl2
.a
.acl_mode
);
627 if (acl1
.a
.u_access
!= acl2
.a
.u_access
628 || acl1
.a
.g_access
!= acl2
.a
.g_access
629 || acl1
.a
.o_access
!= acl2
.a
.o_access
)
631 fprintf (stderr
, "files %s and %s have different ACL access masks: %03o %03o %03o and %03o %03o %03o\n",
633 acl1
.a
.u_access
, acl1
.a
.g_access
, acl1
.a
.o_access
,
634 acl2
.a
.u_access
, acl2
.a
.g_access
, acl2
.a
.o_access
);
637 if (memcmp (acl1
.a
.acl_ext
, acl2
.a
.acl_ext
, acl1
.a
.acl_len
) != 0)
639 fprintf (stderr
, "files %s and %s have different ACL entries\n",
643 #elif HAVE_ACLSORT /* NonStop Kernel */
647 count1
= acl ((char *) file1
, ACL_CNT
, NACLENTRIES
, NULL
);
648 count2
= acl ((char *) file2
, ACL_CNT
, NACLENTRIES
, NULL
);
652 fprintf (stderr
, "error accessing the ACLs of file %s\n", file1
);
658 fprintf (stderr
, "error accessing the ACLs of file %s\n", file2
);
662 if (count1
!= count2
)
664 fprintf (stderr
, "files %s and %s have different number of ACLs: %d and %d\n",
665 file1
, file2
, count1
, count2
);
670 struct acl
*entries1
= XNMALLOC (count1
, struct acl
);
671 struct acl
*entries2
= XNMALLOC (count2
, struct acl
);
674 if (acl ((char *) file1
, ACL_GET
, count1
, entries1
) < count1
)
676 fprintf (stderr
, "error retrieving the ACLs of file %s\n", file1
);
680 if (acl ((char *) file2
, ACL_GET
, count2
, entries2
) < count1
)
682 fprintf (stderr
, "error retrieving the ACLs of file %s\n", file2
);
686 for (i
= 0; i
< count1
; i
++)
688 if (entries1
[i
].a_type
!= entries2
[i
].a_type
)
690 fprintf (stderr
, "files %s and %s: different ACL entry #%d: different types %d and %d\n",
691 file1
, file2
, i
, entries1
[i
].a_type
, entries2
[i
].a_type
);
694 if (entries1
[i
].a_id
!= entries2
[i
].a_id
)
696 fprintf (stderr
, "files %s and %s: different ACL entry #%d: different ids %d and %d\n",
697 file1
, file2
, i
, (int)entries1
[i
].a_id
, (int)entries2
[i
].a_id
);
700 if (entries1
[i
].a_perm
!= entries2
[i
].a_perm
)
702 fprintf (stderr
, "files %s and %s: different ACL entry #%d: different permissions %03o and %03o\n",
703 file1
, file2
, i
, (unsigned int) entries1
[i
].a_perm
, (unsigned int) entries2
[i
].a_perm
);