File: gnulib-nfs-acl.patch

package info (click to toggle)
coreutils 9.7-3
  • links: PTS
  • area: main
  • in suites: forky, sid, trixie
  • size: 67,780 kB
  • sloc: ansic: 243,477; sh: 29,063; perl: 7,908; yacc: 1,858; makefile: 196; python: 47; sed: 16
file content (449 lines) | stat: -rw-r--r-- 18,341 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
Author: Paul Eggert  <eggert@cs.ucla.edu>
Description: addresses spurious cp permission errors
===================================================================
diff --git a/lib/acl.h b/lib/acl.h
index 90fd24e152..e3c134fb41 100644
--- a/lib/acl.h
+++ b/lib/acl.h
@@ -79,6 +79,8 @@ struct aclinfo
 bool acl_errno_valid (int) _GL_ATTRIBUTE_CONST;
 int file_has_acl (char const *, struct stat const *);
 int file_has_aclinfo (char const *restrict, struct aclinfo *restrict, int);
+int fdfile_has_aclinfo (int, char const *restrict,
+                        struct aclinfo *restrict, int);
 
 #if HAVE_LINUX_XATTR_H && HAVE_LISTXATTR
 bool aclinfo_has_xattr (struct aclinfo const *, char const *)
diff --git a/lib/copy-acl.c b/lib/copy-acl.c
index c36f64e51d..2fce6c7d46 100644
--- a/lib/copy-acl.c
+++ b/lib/copy-acl.c
@@ -33,6 +33,7 @@
    a valid file descriptor, use file descriptor operations, else use
    filename based operations on SRC_NAME. Likewise for DEST_DESC and
    DST_NAME.
+   MODE should be the source file's st_mode.
    If access control lists are not available, fchmod the target file to
    MODE.  Also sets the non-permission bits of the destination file
    (S_ISUID, S_ISGID, S_ISVTX) to those from MODE if any are set.
diff --git a/lib/file-has-acl.c b/lib/file-has-acl.c
index 66b920c1ab..a356ee0d0b 100644
--- a/lib/file-has-acl.c
+++ b/lib/file-has-acl.c
@@ -85,6 +85,13 @@ smack_new_label_from_path (MAYBE_UNUSED const char *path,
 {
   return -1;
 }
+static ssize_t
+smack_new_label_from_file (MAYBE_UNUSED int fd,
+                           MAYBE_UNUSED const char *xattr,
+                           MAYBE_UNUSED char **label)
+{
+  return -1;
+}
 # endif
 static bool
 is_smack_enabled (void)
@@ -115,14 +122,16 @@ aclinfo_may_indicate_xattr (struct aclinfo const *ai)
 
 static bool
 has_xattr (char const *xattr, struct aclinfo const *ai,
-           MAYBE_UNUSED char const *restrict name, MAYBE_UNUSED int flags)
+           int fd, char const *restrict name, int flags)
 {
   if (ai && aclinfo_has_xattr (ai, xattr))
     return true;
   else if (!ai || aclinfo_may_indicate_xattr (ai))
     {
-      int ret = ((flags & ACL_SYMLINK_FOLLOW ? getxattr : lgetxattr)
-                (name, xattr, NULL, 0));
+      int ret = (fd < 0
+                 ? ((flags & ACL_SYMLINK_FOLLOW ? getxattr : lgetxattr)
+                    (name, xattr, NULL, 0))
+                 : fgetxattr (fd, xattr, NULL, 0));
       if (0 <= ret || (errno == ERANGE || errno == E2BIG))
         return true;
     }
@@ -145,11 +154,12 @@ aclinfo_has_xattr (struct aclinfo const *ai, char const *xattr)
   return false;
 }
 
-/* Get attributes of the file NAME into AI, if USE_ACL.
+/* Get attributes of the file FD aka NAME into AI, if USE_ACL.
+   Ignore FD if it is negative.
    If FLAGS & ACL_GET_SCONTEXT, also get security context.
    If FLAGS & ACL_SYMLINK_FOLLOW, follow symbolic links.  */
 static void
-get_aclinfo (char const *name, struct aclinfo *ai, int flags)
+get_aclinfo (int fd, char const *name, struct aclinfo *ai, int flags)
 {
   int scontext_err = ENOTSUP;
   ai->buf = ai->u.__gl_acl_ch;
@@ -163,7 +173,9 @@ get_aclinfo (char const *name, struct aclinfo *ai, int flags)
         = (flags & ACL_SYMLINK_FOLLOW ? listxattr : llistxattr);
       while (true)
         {
-          ai->size = lsxattr (name, ai->buf, acl_alloc);
+          ai->size = (fd < 0
+                      ? lsxattr (name, ai->buf, acl_alloc)
+                      : flistxattr (fd, ai->buf, acl_alloc));
           if (0 < ai->size)
             break;
           ai->u.err = ai->size < 0 ? errno : 0;
@@ -171,7 +183,9 @@ get_aclinfo (char const *name, struct aclinfo *ai, int flags)
             break;
 
           /* The buffer was too small.  Find how large it should have been.  */
-          ssize_t size = lsxattr (name, NULL, 0);
+          ssize_t size = (fd < 0
+                          ? lsxattr (name, NULL, 0)
+                          : flistxattr (fd, NULL, 0));
           if (size <= 0)
             {
               ai->size = size;
@@ -214,9 +228,13 @@ get_aclinfo (char const *name, struct aclinfo *ai, int flags)
         {
           if (ai->size < 0 || aclinfo_has_xattr (ai, XATTR_NAME_SMACK))
             {
-              ssize_t r = smack_new_label_from_path (name, "security.SMACK64",
-                                                     flags & ACL_SYMLINK_FOLLOW,
-                                                     &ai->scontext);
+              static char const SMACK64[] = "security.SMACK64";
+              ssize_t r =
+                (fd < 0
+                 ? smack_new_label_from_path (name, SMACK64,
+                                              flags & ACL_SYMLINK_FOLLOW,
+                                              &ai->scontext)
+                 : smack_new_label_from_file (fd, SMACK64, &ai->scontext));
               scontext_err = r < 0 ? errno : 0;
             }
         }
@@ -226,8 +244,10 @@ get_aclinfo (char const *name, struct aclinfo *ai, int flags)
           if (ai->size < 0 || aclinfo_has_xattr (ai, XATTR_NAME_SELINUX))
             {
               ssize_t r =
-                ((flags & ACL_SYMLINK_FOLLOW ? getfilecon : lgetfilecon)
-                 (name, &ai->scontext));
+                (fd < 0
+                 ? ((flags & ACL_SYMLINK_FOLLOW ? getfilecon : lgetfilecon)
+                    (name, &ai->scontext))
+                 : fgetfilecon (fd, &ai->scontext));
               scontext_err = r < 0 ? errno : 0;
 #  ifndef SE_SELINUX_INLINE
               /* Gnulib's selinux-h module is not in use, so getfilecon and
@@ -362,11 +382,13 @@ acl_nfs4_nontrivial (uint32_t *xattr, ssize_t nbytes)
 }
 #endif
 
-#if (!USE_LINUX_XATTR && USE_ACL && HAVE_ACL_GET_FD \
-     && !HAVE_ACL_EXTENDED_FILE && !HAVE_ACL_TYPE_EXTENDED \
-     && !HAVE_ACL_GET_LINK_NP)
-# include <fcntl.h>
-# ifdef O_PATH
+#if (!USE_LINUX_XATTR && USE_ACL && !HAVE_ACL_EXTENDED_FILE \
+     && !HAVE_ACL_TYPE_EXTENDED)
+
+# if HAVE_ACL_GET_FD && !HAVE_ACL_GET_LINK_NP
+#  include <fcntl.h>
+#  ifdef O_PATH
+#   define acl_get_fd_np(fd, type) acl_get_fd (fd)
 
 /* Like acl_get_file, but do not follow symbolic links.  */
 static acl_t
@@ -381,8 +403,24 @@ acl_get_link_np (char const *name, acl_type_t type)
   errno = err;
   return r;
 }
-#  define HAVE_ACL_GET_LINK_NP 1
+#   define HAVE_ACL_GET_LINK_NP 1
+#  endif
 # endif
+
+static acl_t
+acl_get_fdfile (int fd, char const *name, acl_type_t type, int flags)
+{
+  acl_t (*get) (char const *, acl_type_t) = acl_get_file;
+# if HAVE_ACL_GET_LINK_NP /* FreeBSD, NetBSD >= 10, Cygwin >= 2.5 */
+  if (0 <= fd)
+    return acl_get_fd_np (fd, type);
+  if (! (flags & ACL_SYMLINK_FOLLOW))
+    get = acl_get_link_np;
+# else
+  /* Ignore FD and FLAGS, unfortunately.  */
+# endif
+  return get (name, type);
+}
 #endif
 
 /* Return 1 if NAME has a nontrivial access control list,
@@ -398,14 +436,35 @@ acl_get_link_np (char const *name, acl_type_t type)
    If the d_type value is not known, use DT_UNKNOWN though this may be less
    efficient.  */
 int
-file_has_aclinfo (MAYBE_UNUSED char const *restrict name,
+file_has_aclinfo (char const *restrict name,
                   struct aclinfo *restrict ai, int flags)
+{
+  return fdfile_has_aclinfo (-1, name, ai, flags);
+}
+
+/* Return 1 if FD aka NAME has a nontrivial access control list,
+   0 if ACLs are not supported, or if NAME has no or only a base ACL,
+   and -1 (setting errno) on error.  Note callers can determine
+   if ACLs are not supported as errno is set in that case also.
+   Ignore FD if it is negative.
+   Set *AI to ACL info regardless of return value.
+   FLAGS should be a <dirent.h> d_type value, optionally ORed with
+     - _GL_DT_NOTDIR if it is known that NAME is not a directory,
+     - ACL_GET_SCONTEXT to retrieve security context and return 1 if present,
+     - ACL_SYMLINK_FOLLOW to follow the link if NAME is a symbolic link;
+       otherwise do not follow them if possible.
+   If the d_type value is not known, use DT_UNKNOWN though this may be less
+   efficient.  */
+int
+fdfile_has_aclinfo (MAYBE_UNUSED int fd,
+                    MAYBE_UNUSED char const *restrict name,
+                    struct aclinfo *restrict ai, int flags)
 {
   MAYBE_UNUSED unsigned char d_type = flags & UCHAR_MAX;
 
 #if USE_LINUX_XATTR
   int initial_errno = errno;
-  get_aclinfo (name, ai, flags);
+  get_aclinfo (fd, name, ai, flags);
 
   if (!aclinfo_may_indicate_xattr (ai) && ai->size <= 0)
     {
@@ -418,11 +477,11 @@ file_has_aclinfo (MAYBE_UNUSED char const *restrict name,
      In earlier Fedora the two types of ACLs were mutually exclusive.
      Attempt to work correctly on both kinds of systems.  */
 
-  if (!has_xattr (XATTR_NAME_NFSV4_ACL, ai, name, flags))
+  if (!has_xattr (XATTR_NAME_NFSV4_ACL, ai, fd, name, flags))
     return
-      (has_xattr (XATTR_NAME_POSIX_ACL_ACCESS, ai, name, flags)
+      (has_xattr (XATTR_NAME_POSIX_ACL_ACCESS, ai, fd, name, flags)
        || ((d_type == DT_DIR || d_type == DT_UNKNOWN)
-           && has_xattr (XATTR_NAME_POSIX_ACL_DEFAULT, ai, name, flags)));
+           && has_xattr (XATTR_NAME_POSIX_ACL_DEFAULT, ai, fd, name, flags)));
 
   /* A buffer large enough to hold any trivial NFSv4 ACL.
      The max length of a trivial NFSv4 ACL is 6 words for owner,
@@ -432,8 +491,10 @@ file_has_aclinfo (MAYBE_UNUSED char const *restrict name,
      everyone is another word to hold "EVERYONE@".  */
   uint32_t buf[2 * (6 + 6 + 7)];
 
-  int ret = ((flags & ACL_SYMLINK_FOLLOW ? getxattr : lgetxattr)
-             (name, XATTR_NAME_NFSV4_ACL, buf, sizeof buf));
+  int ret = (fd < 0
+             ? ((flags & ACL_SYMLINK_FOLLOW ? getxattr : lgetxattr)
+                (name, XATTR_NAME_NFSV4_ACL, buf, sizeof buf))
+             : fgetxattr (fd, XATTR_NAME_NFSV4_ACL, buf, sizeof buf));
   if (ret < 0)
     switch (errno)
       {
@@ -467,20 +528,23 @@ file_has_aclinfo (MAYBE_UNUSED char const *restrict name,
       /* On Linux, acl_extended_file is an optimized function: It only
          makes two calls to getxattr(), one for ACL_TYPE_ACCESS, one for
          ACL_TYPE_DEFAULT.  */
-    ret = ((flags & ACL_SYMLINK_FOLLOW
-            ? acl_extended_file
-            : acl_extended_file_nofollow)
-           (name));
+    ret = (fd < 0
+           ? ((flags & ACL_SYMLINK_FOLLOW
+               ? acl_extended_file
+               : acl_extended_file_nofollow)
+              (name))
+           : acl_extended_fd (fd));
 #   elif HAVE_ACL_TYPE_EXTENDED /* Mac OS X */
     /* On Mac OS X, acl_get_file (name, ACL_TYPE_ACCESS)
        and acl_get_file (name, ACL_TYPE_DEFAULT)
        always return NULL / EINVAL.  There is no point in making
        these two useless calls.  The real ACL is retrieved through
-       acl_get_file (name, ACL_TYPE_EXTENDED).  */
-    acl_t acl = ((flags & ACL_SYMLINK_FOLLOW
-                  ? acl_get_file
-                  : acl_get_link_np)
-                 (name, ACL_TYPE_EXTENDED));
+       ACL_TYPE_EXTENDED.  */
+    acl_t acl =
+      (fd < 0
+       ? ((flags & ACL_SYMLINK_FOLLOW ? acl_get_file : acl_get_link_np)
+          (name, ACL_TYPE_EXTENDED))
+       : acl_get_fd_np (fd, ACL_TYPE_EXTENDED));
     if (acl)
       {
         ret = acl_extended_nontrivial (acl);
@@ -489,13 +553,8 @@ file_has_aclinfo (MAYBE_UNUSED char const *restrict name,
     else
       ret = -1;
 #   else /* FreeBSD, NetBSD >= 10, IRIX, Tru64, Cygwin >= 2.5 */
-    acl_t (*acl_get_file_or_link) (char const *, acl_type_t) = acl_get_file;
-#    if HAVE_ACL_GET_LINK_NP /* FreeBSD, NetBSD >= 10, Cygwin >= 2.5 */
-    if (! (flags & ACL_SYMLINK_FOLLOW))
-      acl_get_file_or_link = acl_get_link_np;
-#    endif
 
-    acl_t acl = acl_get_file_or_link (name, ACL_TYPE_ACCESS);
+    acl_t acl = acl_get_fdfile (fd, name, ACL_TYPE_ACCESS, flags);
     if (acl)
       {
         ret = acl_access_nontrivial (acl);
@@ -517,7 +576,7 @@ file_has_aclinfo (MAYBE_UNUSED char const *restrict name,
             && (d_type == DT_DIR
                 || (d_type == DT_UNKNOWN && !(flags & _GL_DT_NOTDIR))))
           {
-            acl = acl_get_file_or_link (name, ACL_TYPE_DEFAULT);
+            acl = acl_get_fdfile (fd, name, ACL_TYPE_DEFAULT, flags);
             if (acl)
               {
 #     ifdef __CYGWIN__ /* Cygwin >= 2.5 */
@@ -562,7 +621,10 @@ file_has_aclinfo (MAYBE_UNUSED char const *restrict name,
 
       /* Solaris 10 (newer version), which has additional API declared in
          <sys/acl.h> (acl_t) and implemented in libsec (acl_set, acl_trivial,
-         acl_fromtext, ...).  */
+         acl_fromtext, ...).
+
+         Ignore FD, unfortunately.  That is better than mishandling
+         ZFS-style ACLs, as the general case code does.  */
       return acl_trivial (name);
 
 #    else /* Solaris, Cygwin, general case */
@@ -586,7 +648,9 @@ file_has_aclinfo (MAYBE_UNUSED char const *restrict name,
 
         for (;;)
           {
-            count = acl (name, GETACL, alloc, entries);
+            count = (fd < 0
+                     ? acl (name, GETACL, alloc, entries)
+                     : facl (fd, GETACL, alloc, entries));
             if (count < 0 && errno == ENOSPC)
               {
                 /* Increase the size of the buffer.  */
@@ -657,7 +721,9 @@ file_has_aclinfo (MAYBE_UNUSED char const *restrict name,
 
         for (;;)
           {
-            count = acl (name, ACE_GETACL, alloc, entries);
+            count = (fd < 0
+                     ? acl (name, ACE_GETACL, alloc, entries)
+                     : facl (fd, ACE_GETACL, alloc, entries));
             if (count < 0 && errno == ENOSPC)
               {
                 /* Increase the size of the buffer.  */
@@ -722,7 +788,9 @@ file_has_aclinfo (MAYBE_UNUSED char const *restrict name,
         struct acl_entry entries[NACLENTRIES];
         int count;
 
-        count = getacl (name, NACLENTRIES, entries);
+        count = (fd < 0
+                 ? getacl (name, NACLENTRIES, entries)
+                 : fgetacl (fd, NACLENTRIES, entries));
 
         if (count < 0)
           {
@@ -751,7 +819,8 @@ file_has_aclinfo (MAYBE_UNUSED char const *restrict name,
             {
               struct stat statbuf;
 
-              if (stat (name, &statbuf) == -1 && errno != EOVERFLOW)
+              if ((fd < 0 ? stat (name, &statbuf) : fstat (fd, &statbuf)) < 0
+                  && errno != EOVERFLOW)
                 return -1;
 
               return acl_nontrivial (count, entries);
@@ -765,6 +834,7 @@ file_has_aclinfo (MAYBE_UNUSED char const *restrict name,
         struct acl entries[NACLVENTRIES];
         int count;
 
+        /* Ignore FD, unfortunately.  */
         count = acl ((char *) name, ACL_GET, NACLVENTRIES, entries);
 
         if (count < 0)
@@ -809,7 +879,9 @@ file_has_aclinfo (MAYBE_UNUSED char const *restrict name,
           /* The docs say that type being 0 is equivalent to ACL_ANY, but it
              is not true, in AIX 5.3.  */
           type.u64 = ACL_ANY;
-          if (aclx_get (name, 0, &type, aclbuf, &aclsize, &mode) >= 0)
+          if (0 <= (fd < 0
+                    ? aclx_get (name, 0, &type, aclbuf, &aclsize, &mode)
+                    : aclx_fget (fd, 0, &type, aclbuf, &aclsize, &mode)))
             break;
           if (errno == ENOSYS)
             return 0;
@@ -855,7 +927,10 @@ file_has_aclinfo (MAYBE_UNUSED char const *restrict name,
 
       union { struct acl a; char room[4096]; } u;
 
-      if (statacl ((char *) name, STX_NORMAL, &u.a, sizeof (u)) < 0)
+      if ((fd < 0
+           ? statacl ((char *) name, STX_NORMAL, &u.a, sizeof u)
+           : fstatacl (fd, STX_NORMAL, &u.a, sizeof u))
+          < 0)
         return -1;
 
       return acl_nontrivial (&u.a);
@@ -866,6 +941,7 @@ file_has_aclinfo (MAYBE_UNUSED char const *restrict name,
         struct acl entries[NACLENTRIES];
         int count;
 
+        /* Ignore FD, unfortunately.  */
         count = acl ((char *) name, ACL_GET, NACLENTRIES, entries);
 
         if (count < 0)
diff --git a/lib/qcopy-acl.c b/lib/qcopy-acl.c
index ad7966152a..282f4b2d2a 100644
--- a/lib/qcopy-acl.c
+++ b/lib/qcopy-acl.c
@@ -26,6 +26,7 @@
 #if USE_XATTR
 
 # include <attr/libattr.h>
+# include <dirent.h>
 # include <string.h>
 
 # if HAVE_LINUX_XATTR_H
@@ -61,6 +62,7 @@ is_attr_permissions (const char *name, struct error_context *ctx)
    a valid file descriptor, use file descriptor operations, else use
    filename based operations on SRC_NAME. Likewise for DEST_DESC and
    DST_NAME.
+   MODE should be the source file's st_mode.
    If access control lists are not available, fchmod the target file to
    MODE.  Also sets the non-permission bits of the destination file
    (S_ISUID, S_ISGID, S_ISVTX) to those from MODE if any are set.
@@ -86,10 +88,29 @@ qcopy_acl (const char *src_name, int source_desc, const char *dst_name,
      Functions attr_copy_* return 0 in case we copied something OR nothing
      to copy */
   if (ret == 0)
-    ret = source_desc <= 0 || dest_desc <= 0
-      ? attr_copy_file (src_name, dst_name, is_attr_permissions, NULL)
-      : attr_copy_fd (src_name, source_desc, dst_name, dest_desc,
-                      is_attr_permissions, NULL);
+    {
+      ret = source_desc <= 0 || dest_desc <= 0
+        ? attr_copy_file (src_name, dst_name, is_attr_permissions, NULL)
+        : attr_copy_fd (src_name, source_desc, dst_name, dest_desc,
+                        is_attr_permissions, NULL);
+
+      /* Copying can fail with EOPNOTSUPP even when the source
+         permissions are trivial (Bug#78328).  Don't report an error
+         in this case, as the chmod_or_fchmod suffices.  */
+      if (ret < 0 && errno == EOPNOTSUPP)
+        {
+          /* fdfile_has_aclinfo cares only about DT_DIR, _GL_DT_NOTDIR,
+             and DT_LNK (but DT_LNK is not possible here),
+             so use _GL_DT_NOTDIR | DT_UNKNOWN for other file types.  */
+          int flags = S_ISDIR (mode) ? DT_DIR : _GL_DT_NOTDIR | DT_UNKNOWN;
+
+          struct aclinfo ai;
+          if (!fdfile_has_aclinfo (source_desc, src_name, &ai, flags))
+            ret = 0;
+          aclinfo_free (&ai);
+          errno = EOPNOTSUPP;
+        }
+    }
 #else
   /* no XATTR, so we proceed the old dusty way */
   struct permission_context ctx;