]> git.eshelyaron.com Git - emacs.git/commitdiff
Add support for preserving ACL entries of files.
authorRomain Francoise <romain@orebokech.com>
Sun, 16 Dec 2012 18:22:27 +0000 (19:22 +0100)
committerRomain Francoise <romain@orebokech.com>
Sun, 16 Dec 2012 18:22:27 +0000 (19:22 +0100)
* configure.ac (acl): New option.
(HAVE_POSIX_ACL): Test for POSIX ACL support.  This is typically
provided by libacl on GNU/Linux.

* fileio.c (Ffile_acl, Fset_file_acl): New functions.
(Fcopy_file): Change last arg to `preserve_extended_attributes'
and copy ACL entries of file in addition to SELinux context if
set.
(syms_of_fileio): Add `file-acl' and `set-file-acl'.

* Makefile.in (LIBACL_LIBS): New macro.
(LIBES): Use it.

* files.el (file-extended-attributes)
(set-file-extended-attributes): New functions.
(backup-buffer): Use them to handle both SELinux context and ACL
entries.
(backup-buffer-copy): Work with an alist of extended attributes,
rather than an SELinux context.
(basic-save-buffer-2): Ditto.

* files.texi (File Attributes): Document ACL support and new
`file-acl' function.
(Changing Files): Mention argument name change of `copy-file' and
document new function `set-file-acl'.

ChangeLog
configure.ac
doc/lispref/ChangeLog
doc/lispref/files.texi
etc/NEWS
lisp/ChangeLog
lisp/files.el
src/ChangeLog
src/Makefile.in
src/fileio.c

index f60ac180f6b8ee78f02422020384aef3eec4f35b..9f56370dc3ca8bd16eaa59598dfe1c4b919281c7 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,9 @@
+2012-12-16  Romain Francoise  <romain@orebokech.com>
+
+       * configure.ac (acl): New option.
+       (HAVE_POSIX_ACL): Test for POSIX ACL support.  This is typically
+       provided by libacl on GNU/Linux.
+
 2012-12-14  Paul Eggert  <eggert@cs.ucla.edu>
 
        Fix permissions bugs with setgid directories etc. (Bug#13125)
index 1a7f78326eb27c3c09acce213ac1110bc45939fc..3c8be79e24c612941ad091a3a3e8a1c4d59cf70f 100644 (file)
@@ -184,6 +184,7 @@ OPTION_DEFAULT_ON([dbus],[don't compile with D-Bus support])
 OPTION_DEFAULT_ON([gconf],[don't compile with GConf support])
 OPTION_DEFAULT_ON([gsettings],[don't compile with GSettings support])
 OPTION_DEFAULT_ON([selinux],[don't compile with SELinux support])
+OPTION_DEFAULT_ON([acl],[don't compile with ACL support])
 OPTION_DEFAULT_ON([gnutls],[don't use -lgnutls for SSL/TLS support])
 OPTION_DEFAULT_ON([inotify],[don't compile with inotify (file-watch) support])
 
@@ -2197,6 +2198,23 @@ if test "$ac_cv_func_inotify_init1" = yes; then
   AC_DEFINE(HAVE_INOTIFY, 1, [Define to 1 to use inotify.])
 fi
 
+dnl POSIX ACL support: provided by libacl on GNU/Linux, by libc on FreeBSD.
+HAVE_POSIX_ACL=no
+LIBACL_LIBS=
+if test "${with_acl}" = "yes"; then
+  AC_CHECK_LIB([acl], [acl_set_file], HAVE_POSIX_ACL=yes, HAVE_POSIX_ACL=no)
+  if test "$HAVE_POSIX_ACL" = yes; then
+    AC_DEFINE(HAVE_POSIX_ACL, 1, [Define to 1 if using POSIX ACL support.])
+    LIBACL_LIBS=-lacl
+  else
+    AC_CHECK_FUNC(acl_set_file, HAVE_POSIX_ACL=yes, HAVE_POSIX_ACL=no)
+    if test "$HAVE_POSIX_ACL" = yes; then
+      AC_DEFINE(HAVE_POSIX_ACL, 1, [Define to 1 if using POSIX ACL support.])
+    fi
+ fi
+fi
+AC_SUBST(LIBACL_LIBS)
+
 dnl Do not put whitespace before the #include statements below.
 dnl Older compilers (eg sunos4 cc) choke on it.
 HAVE_XAW3D=no
index 8a99f8c9c4073a2739583b437a2603465927c28c..922147844bd02d273c7d2c2a116d33d5415a7fd9 100644 (file)
@@ -1,3 +1,10 @@
+2012-12-16  Romain Francoise  <romain@orebokech.com>
+
+       * files.texi (File Attributes): Document ACL support and new
+       `file-acl' function.
+       (Changing Files): Mention argument name change of `copy-file' and
+       document new function `set-file-acl'.
+
 2012-12-14  Paul Eggert  <eggert@cs.ucla.edu>
 
        Fix permissions bugs with setgid directories etc. (Bug#13125)
index c0c2221a394b6d5cf16ffa9a5e35d76f35be3d75..93bee96192944f4d2b6d8c0316c1d3d1007ba45e 100644 (file)
@@ -1352,6 +1352,36 @@ not support SELinux, or if Emacs was not compiled with SELinux
 support, then the return value is @code{(nil nil nil nil)}.
 @end defun
 
+@cindex access control list
+@cindex ACL entries
+  If Emacs has been compiled with @dfn{ACL} (access control list)
+support, you can use the function @code{file-acl} to retrieve a file's
+ACL entries.  The format is platform-specific; on GNU/Linux and BSD,
+Emacs uses the POSIX ACL interface.  For the function
+@code{set-file-acl}, see @ref{Changing Files}.
+
+@defun file-acl filename
+This function returns the ACL entries of the file @var{filename}.
+The return value is a string containing the textual representation of
+the ACL entries, like the following:
+
+@example
+@group
+user::rw-
+group::r--
+group:gnu:rwx
+mask::rwx
+other::r--
+@end group
+@end example
+
+If the file does not exist or is inaccessible, or if Emacs was unable to
+determine the ACL entries, then the return value is @code{nil}.  The
+latter can happen for local files if Emacs was not compiled with ACL
+support, or for remote files if the file handler returns nil for the
+file's ACL entries.
+@end defun
+
 @node Locating Files
 @subsection How to Locate Files in Standard Places
 @cindex locate file in path
@@ -1541,9 +1571,10 @@ non-@code{nil}, we attempt to copy the user and group ownership of the
 file.  This works only on some operating systems, and only if you have
 the correct permissions to do so.
 
-If the optional argument @var{preserve-selinux} is non-@code{nil}, and
-Emacs has been compiled with SELinux support, this function attempts
-to copy the file's SELinux context (@pxref{File Attributes}).
+If the optional argument @var{preserve-extended-attributes} is
+non-@code{nil}, and Emacs has been built with the appropriate support,
+this function attempts to copy the file's extended attributes, such as
+its SELinux context and ACL entries (@pxref{File Attributes}).
 @end deffn
 
 @deffn Command make-symbolic-link filename newname  &optional ok-if-exists
@@ -1684,6 +1715,13 @@ nothing if SELinux is disabled, or if Emacs was compiled without
 SELinux support.
 @end defun
 
+@defun set-file-acl filename acl-string
+This function sets the ACL entries of the file @var{filename} to
+@var{acl-string}.  @xref{File Attributes}, for a brief description of
+ACLs.  The @var{acl-string} argument should be a string containing the
+textual representation of the desired ACL entries.
+@end defun
+
 @node File Names
 @section File Names
 @cindex file names
index 0063aedb0db232094011c9217ed3390c07b8f8f4..1c82b7609461e8fe29fa92da129877e3cabf8715 100644 (file)
--- a/etc/NEWS
+++ b/etc/NEWS
@@ -22,6 +22,12 @@ so we will look at it and add it to the manual.
 
 \f
 * Installation Changes in Emacs 24.4
+
+** Emacs can be compiled with POSIX ACL support.
+This happens by default if a suitable support library is found at
+build time, like libacl on GNU/Linux.  To prevent this, use the
+configure option `--without-acl'.
+
 * Startup Changes in Emacs 24.4
 * Changes in Emacs 24.4
 
@@ -33,6 +39,14 @@ if there is one.
 This unfinished feature was introduced by accident in Emacs 23.1;
 simply disabling Transient Mark mode does the same thing.
 
+** ACL support has been added.
++++
+*** Emacs preserves the ACL entries of files when backing up.
++++
+*** New functions `file-acl' and `set-file-acl' get and set the ACL
+entries of a file.  On GNU/Linux the POSIX ACL interface is used via
+libacl.
+
 * Editing Changes in Emacs 24.4
 
 ** New commands `toggle-frame-maximized' and `cycle-frame-maximized',
@@ -149,6 +163,12 @@ special-forms any more.
 VAR was bound to nil which was not tremendously useful and just lead to
 spurious warnings about an unused var.
 
+** The return value of `backup-buffer' has changed.
+The second argument is no longer an SELinux context, instead it is an
+alist of extended attributes as returned by the new function
+`file-extended-attributes'.  The attributes can be applied to another
+file using `set-file-extended-attributes'.
+
 * Lisp changes in Emacs 24.4
 
 ** Support for filesystem notifications.
@@ -186,6 +206,10 @@ used in place of the 9th element of `file-attributes'.
 
 ** New functions `group-gid' and `group-real-gid'.
 
+** The 6th argument to `copy-file' has been renamed to
+`preserve-extended-attributes' as it now handles both SELinux context
+and ACL entries.
+
 * Changes in Emacs 24.4 on non-free operating systems
 
 +++
index e56b44ba52506e803b5c62da332461ef04719cd2..0d65baebee2d77b6b9b095b47c50e684c7ae5eed 100644 (file)
@@ -1,3 +1,13 @@
+2012-12-16  Romain Francoise  <romain@orebokech.com>
+
+       * files.el (file-extended-attributes)
+       (set-file-extended-attributes): New functions.
+       (backup-buffer): Use them to handle both SELinux context and ACL
+       entries.
+       (backup-buffer-copy): Work with an alist of extended attributes,
+       rather than an SELinux context.
+       (basic-save-buffer-2): Ditto.
+
 2012-12-16  Timo Myyrä  <timo.myyra@gmail.com>
 
        * battery.el (battery-bsd-apm): New function.
index 7974f73a24880ee2182fb269e6512c910395cd8c..3f29468e2d1ce64bdf28344d58cf23b032a99b7d 100644 (file)
@@ -3879,6 +3879,27 @@ Interactively, confirmation is required unless you supply a prefix argument."
   ;; the one at the old location.
   (vc-find-file-hook))
 \f
+(defun file-extended-attributes (filename)
+  "Return an alist of extended attributes of file FILENAME.
+
+Extended attributes are platform-specific metadata about the file,
+such as SELinux context, list of ACL entries, etc."
+  `((acl . ,(file-acl filename))
+    (selinux-context . ,(file-selinux-context filename))))
+
+(defun set-file-extended-attributes (filename attributes)
+  "Set extended attributes of file FILENAME to ATTRIBUTES.
+
+ATTRIBUTES must be an alist of file attributes as returned by
+`file-extended-attributes'."
+  (dolist (elt attributes)
+    (let ((attr (car elt))
+         (val (cdr elt)))
+      (cond ((eq attr 'acl)
+            (set-file-acl filename val))
+           ((eq attr 'selinux-context)
+            (set-file-selinux-context filename val))))))
+\f
 (defun backup-buffer ()
   "Make a backup of the disk file visited by the current buffer, if appropriate.
 This is normally done before saving the buffer the first time.
@@ -3888,13 +3909,14 @@ variable `make-backup-files'.  If it's done by renaming, then the file is
 no longer accessible under its old name.
 
 The value is non-nil after a backup was made by renaming.
-It has the form (MODES SELINUXCONTEXT BACKUPNAME).
+It has the form (MODES EXTENDED-ATTRIBUTES BACKUPNAME).
 MODES is the result of `file-modes' on the original
 file; this means that the caller, after saving the buffer, should change
 the modes of the new file to agree with the old modes.
-SELINUXCONTEXT is the result of `file-selinux-context' on the original
-file; this means that the caller, after saving the buffer, should change
-the SELinux context of the new file to agree with the old context.
+EXTENDED-ATTRIBUTES is the result of `file-extended-attributes'
+on the original file; this means that the caller, after saving
+the buffer, should change the extended attributes of the new file
+to agree with the old attributes.
 BACKUPNAME is the backup file name, which is the old file renamed."
   (if (and make-backup-files (not backup-inhibited)
           (not buffer-backed-up)
@@ -3923,7 +3945,8 @@ BACKUPNAME is the backup file name, which is the old file renamed."
                                (y-or-n-p (format "Delete excess backup versions of %s? "
                                                  real-file-name)))))
                      (modes (file-modes buffer-file-name))
-                     (context (file-selinux-context buffer-file-name)))
+                     (extended-attributes
+                      (file-extended-attributes buffer-file-name)))
                  ;; Actually write the back up file.
                  (condition-case ()
                      (if (or file-precious-flag
@@ -3943,10 +3966,13 @@ BACKUPNAME is the backup file name, which is the old file renamed."
                                                   (<= (nth 2 attr) backup-by-copying-when-privileged-mismatch)))
                                          (not (file-ownership-preserved-p
                                                real-file-name t))))))
-                         (backup-buffer-copy real-file-name backupname modes context)
+                         (backup-buffer-copy real-file-name
+                                             backupname modes
+                                             extended-attributes)
                        ;; rename-file should delete old backup.
                        (rename-file real-file-name backupname t)
-                       (setq setmodes (list modes context backupname)))
+                       (setq setmodes (list modes extended-attributes
+                                            backupname)))
                    (file-error
                     ;; If trouble writing the backup, write it in
                     ;; .emacs.d/%backup%.
@@ -3954,7 +3980,8 @@ BACKUPNAME is the backup file name, which is the old file renamed."
                     (message "Cannot write backup file; backing up in %s"
                              backupname)
                     (sleep-for 1)
-                    (backup-buffer-copy real-file-name backupname modes context)))
+                    (backup-buffer-copy real-file-name backupname
+                                        modes extended-attributes)))
                  (setq buffer-backed-up t)
                  ;; Now delete the old versions, if desired.
                  (if delete-old-versions
@@ -3966,7 +3993,7 @@ BACKUPNAME is the backup file name, which is the old file renamed."
                  setmodes)
            (file-error nil))))))
 
-(defun backup-buffer-copy (from-name to-name modes context)
+(defun backup-buffer-copy (from-name to-name modes extended-attributes)
   (let ((umask (default-file-modes)))
     (unwind-protect
        (progn
@@ -3994,8 +4021,8 @@ BACKUPNAME is the backup file name, which is the old file renamed."
       (set-default-file-modes umask)))
   (and modes
        (set-file-modes to-name (logand modes #o1777)))
-  (and context
-       (set-file-selinux-context to-name context)))
+  (and extended-attributes
+       (set-file-extended-attributes to-name extended-attributes)))
 
 (defvar file-name-version-regexp
   "\\(?:~\\|\\.~[-[:alnum:]:#@^._]+\\(?:~[[:digit:]]+\\)?~\\)"
@@ -4593,7 +4620,8 @@ Before and after saving the buffer, this function runs
                (condition-case ()
                    (progn
                      (set-file-modes buffer-file-name (car setmodes))
-                     (set-file-selinux-context buffer-file-name (nth 1 setmodes)))
+                     (set-file-extended-attributes buffer-file-name
+                                                   (nth 1 setmodes)))
                  (error nil))))
          ;; If the auto-save file was recent before this command,
          ;; delete it now.
@@ -4606,7 +4634,8 @@ Before and after saving the buffer, this function runs
 ;; This does the "real job" of writing a buffer into its visited file
 ;; and making a backup file.  This is what is normally done
 ;; but inhibited if one of write-file-functions returns non-nil.
-;; It returns a value (MODES SELINUXCONTEXT BACKUPNAME), like backup-buffer.
+;; It returns a value (MODES EXTENDED-ATTRIBUTES BACKUPNAME), like
+;; backup-buffer.
 (defun basic-save-buffer-1 ()
   (prog1
       (if save-buffer-coding-system
@@ -4618,7 +4647,8 @@ Before and after saving the buffer, this function runs
       (setq buffer-file-coding-system-explicit
            (cons last-coding-system-used nil)))))
 
-;; This returns a value (MODES SELINUXCONTEXT BACKUPNAME), like backup-buffer.
+;; This returns a value (MODES EXTENDED-ATTRIBUTES BACKUPNAME), like
+;; backup-buffer.
 (defun basic-save-buffer-2 ()
   (let (tempsetmodes setmodes)
     (if (not (file-writable-p buffer-file-name))
@@ -4693,7 +4723,7 @@ Before and after saving the buffer, this function runs
            (setq setmodes (or setmodes
                               (list (or (file-modes buffer-file-name)
                                         (logand ?\666 umask))
-                                    (file-selinux-context buffer-file-name)
+                                    (file-extended-attributes buffer-file-name)
                                     buffer-file-name)))
            ;; We succeeded in writing the temp file,
            ;; so rename it.
@@ -4705,10 +4735,10 @@ Before and after saving the buffer, this function runs
        (cond ((and tempsetmodes (not setmodes))
               ;; Change the mode back, after writing.
               (setq setmodes (list (file-modes buffer-file-name)
-                                   (file-selinux-context buffer-file-name)
+                                   (file-extended-attributes buffer-file-name)
                                    buffer-file-name))
               (set-file-modes buffer-file-name (logior (car setmodes) 128))
-              (set-file-selinux-context buffer-file-name (nth 1 setmodes)))))
+              (set-file-extended-attributes buffer-file-name (nth 1 setmodes)))))
        (let (success)
          (unwind-protect
              (progn
index 54dcfca6fd528a480df35d26d67cd0560dfaee15..3cf105c80034c62856d84965a8493ce405193e6c 100644 (file)
@@ -1,3 +1,14 @@
+2012-12-16  Romain Francoise  <romain@orebokech.com>
+
+       * fileio.c (Ffile_acl, Fset_file_acl): New functions.
+       (Fcopy_file): Change last arg to `preserve_extended_attributes'
+       and copy ACL entries of file in addition to SELinux context if
+       set.
+       (syms_of_fileio): Add `file-acl' and `set-file-acl'.
+
+       * Makefile.in (LIBACL_LIBS): New macro.
+       (LIBES): Use it.
+
 2012-12-15  Paul Eggert  <eggert@cs.ucla.edu>
 
        * fileio.c (internal_delete_file): Use bool for boolean.
index 5f5fdfdc5eb325bb3fb8eecd98e921ac829075f2..0e91eaecb17801872ce37f79bd5346aaabddac56 100644 (file)
@@ -292,6 +292,8 @@ LIBSELINUX_LIBS = @LIBSELINUX_LIBS@
 LIBGNUTLS_LIBS = @LIBGNUTLS_LIBS@
 LIBGNUTLS_CFLAGS = @LIBGNUTLS_CFLAGS@
 
+LIBACL_LIBS = @LIBACL_LIBS@
+
 LIB_PTHREAD_SIGMASK = @LIB_PTHREAD_SIGMASK@
 
 INTERVALS_H = dispextern.h intervals.h composite.h
@@ -406,7 +408,7 @@ LIBES = $(LIBS) $(W32_LIBS) $(LIBX_BASE) $(LIBIMAGE) \
    $(LIBXML2_LIBS) $(LIBGPM) $(LIBRESOLV) $(LIBS_SYSTEM) \
    $(LIBS_TERMCAP) $(GETLOADAVG_LIBS) $(SETTINGS_LIBS) $(LIBSELINUX_LIBS) \
    $(FREETYPE_LIBS) $(FONTCONFIG_LIBS) $(LIBOTF_LIBS) $(M17N_FLT_LIBS) \
-   $(LIBGNUTLS_LIBS) $(LIB_PTHREAD) $(LIB_PTHREAD_SIGMASK) \
+   $(LIBACL_LIBS) $(LIBGNUTLS_LIBS) $(LIB_PTHREAD) $(LIB_PTHREAD_SIGMASK) \
    $(LIB_GCC) $(LIB_MATH) $(LIB_STANDARD) $(LIB_GCC)
 
 all: emacs$(EXEEXT) $(OTHER_FILES)
index 90626c4af0efe61a1948213363a2f2233ec4c9bc..f1cfe0eb6255899289dc6adc07b30a5f6419a5bd 100644 (file)
@@ -36,6 +36,10 @@ along with GNU Emacs.  If not, see <http://www.gnu.org/licenses/>.  */
 #include <selinux/context.h>
 #endif
 
+#ifdef HAVE_POSIX_ACL
+#include <sys/acl.h>
+#endif
+
 #include <c-ctype.h>
 
 #include "lisp.h"
@@ -236,6 +240,8 @@ static Lisp_Object Qset_file_modes;
 static Lisp_Object Qset_file_times;
 static Lisp_Object Qfile_selinux_context;
 static Lisp_Object Qset_file_selinux_context;
+static Lisp_Object Qfile_acl;
+static Lisp_Object Qset_file_acl;
 static Lisp_Object Qfile_newer_than_file_p;
 Lisp_Object Qinsert_file_contents;
 Lisp_Object Qwrite_region;
@@ -1895,9 +1901,10 @@ A prefix arg makes KEEP-TIME non-nil.
 If PRESERVE-UID-GID is non-nil, we try to transfer the
 uid and gid of FILE to NEWNAME.
 
-If PRESERVE-SELINUX-CONTEXT is non-nil and SELinux is enabled
-on the system, we copy the SELinux context of FILE to NEWNAME.  */)
-  (Lisp_Object file, Lisp_Object newname, Lisp_Object ok_if_already_exists, Lisp_Object keep_time, Lisp_Object preserve_uid_gid, Lisp_Object preserve_selinux_context)
+If PRESERVE-EXTENDED-ATTRIBUTES is non-nil, we try to copy additional
+attributes of FILE to NEWNAME, such as its SELinux context and ACL
+entries (depending on how Emacs was built).  */)
+  (Lisp_Object file, Lisp_Object newname, Lisp_Object ok_if_already_exists, Lisp_Object keep_time, Lisp_Object preserve_uid_gid, Lisp_Object preserve_extended_attributes)
 {
   int ifd, ofd;
   int n;
@@ -1911,6 +1918,9 @@ on the system, we copy the SELinux context of FILE to NEWNAME.  */)
   security_context_t con;
   int conlength = 0;
 #endif
+#ifdef HAVE_POSIX_ACL
+  acl_t acl = NULL;
+#endif
 
   encoded_file = encoded_newname = Qnil;
   GCPRO4 (file, newname, encoded_file, encoded_newname);
@@ -1933,7 +1943,7 @@ on the system, we copy the SELinux context of FILE to NEWNAME.  */)
   if (!NILP (handler))
     RETURN_UNGCPRO (call7 (handler, Qcopy_file, file, newname,
                           ok_if_already_exists, keep_time, preserve_uid_gid,
-                          preserve_selinux_context));
+                          preserve_extended_attributes));
 
   encoded_file = ENCODE_FILE (file);
   encoded_newname = ENCODE_FILE (newname);
@@ -1986,14 +1996,23 @@ on the system, we copy the SELinux context of FILE to NEWNAME.  */)
   if (fstat (ifd, &st) != 0)
     report_file_error ("Input file status", Fcons (file, Qnil));
 
-#if HAVE_LIBSELINUX
-  if (!NILP (preserve_selinux_context) && is_selinux_enabled ())
+  if (!NILP (preserve_extended_attributes))
     {
-      conlength = fgetfilecon (ifd, &con);
-      if (conlength == -1)
-       report_file_error ("Doing fgetfilecon", Fcons (file, Qnil));
-    }
+#if HAVE_LIBSELINUX
+      if (is_selinux_enabled ())
+       {
+         conlength = fgetfilecon (ifd, &con);
+         if (conlength == -1)
+           report_file_error ("Doing fgetfilecon", Fcons (file, Qnil));
+       }
+#endif
+
+#ifdef HAVE_POSIX_ACL
+      acl = acl_get_fd (ifd);
+      if (acl == NULL && errno != ENOTSUP)
+       report_file_error ("Getting ACL", Fcons (file, Qnil));
 #endif
+    }
 
   if (out_st.st_mode != 0
       && st.st_dev == out_st.st_dev && st.st_ino == out_st.st_ino)
@@ -2075,6 +2094,17 @@ on the system, we copy the SELinux context of FILE to NEWNAME.  */)
     }
 #endif
 
+#ifdef HAVE_POSIX_ACL
+  if (acl != NULL)
+    {
+      bool fail = acl_set_fd (ofd, acl) != 0;
+      if (fail && errno != ENOTSUP)
+       report_file_error ("Setting ACL", Fcons (newname, Qnil));
+
+      acl_free (acl);
+    }
+#endif
+
   if (!NILP (keep_time))
     {
       EMACS_TIME atime = get_stat_atime (&st);
@@ -2961,6 +2991,106 @@ compiled with SELinux support.  */)
   return Qnil;
 }
 \f
+DEFUN ("file-acl", Ffile_acl, Sfile_acl, 1, 1, 0,
+       doc: /* Return ACL entries of file named FILENAME, as a string.
+Return nil if file does not exist or is not accessible, or if Emacs
+was unable to determine the ACL entries.  The latter can happen for
+local files if Emacs was not compiled with ACL support, or for remote
+files if the file handler returns nil for the file's ACL entries.  */)
+  (Lisp_Object filename)
+{
+  Lisp_Object absname;
+  Lisp_Object handler;
+#ifdef HAVE_POSIX_ACL
+  acl_t acl;
+  Lisp_Object acl_string;
+  char *str;
+#endif
+
+  absname = expand_and_dir_to_file (filename,
+                                   BVAR (current_buffer, directory));
+
+  /* If the file name has special constructs in it,
+     call the corresponding file handler.  */
+  handler = Ffind_file_name_handler (absname, Qfile_acl);
+  if (!NILP (handler))
+    return call2 (handler, Qfile_acl, absname);
+
+#ifdef HAVE_POSIX_ACL
+  absname = ENCODE_FILE (absname);
+
+  acl = acl_get_file (SSDATA (absname), ACL_TYPE_ACCESS);
+  if (acl == NULL)
+    return Qnil;
+
+  str = acl_to_text (acl, NULL);
+  if (str == NULL)
+    {
+      acl_free (acl);
+      return Qnil;
+    }
+
+  acl_string = build_string (str);
+  acl_free (str);
+  acl_free (acl);
+
+  return acl_string;
+#endif
+
+  return Qnil;
+}
+
+DEFUN ("set-file-acl", Fset_file_acl, Sset_file_acl,
+       2, 2, 0,
+       doc: /* Set ACL of file named FILENAME to ACL-STRING.
+ACL-STRING should contain the textual representation of the ACL
+entries in a format suitable for the platform.
+
+Setting ACL for local files requires Emacs to be built with ACL
+support.  */)
+  (Lisp_Object filename, Lisp_Object acl_string)
+{
+  Lisp_Object absname;
+  Lisp_Object handler;
+#ifdef HAVE_POSIX_ACL
+  Lisp_Object encoded_absname;
+  acl_t acl;
+  bool fail;
+#endif
+
+  absname = Fexpand_file_name (filename, BVAR (current_buffer, directory));
+
+  /* If the file name has special constructs in it,
+     call the corresponding file handler.  */
+  handler = Ffind_file_name_handler (absname, Qset_file_acl);
+  if (!NILP (handler))
+    return call3 (handler, Qset_file_acl, absname, acl_string);
+
+#ifdef HAVE_POSIX_ACL
+  if (STRINGP (acl_string))
+    {
+      acl = acl_from_text (SSDATA (acl_string));
+      if (acl == NULL)
+       {
+         report_file_error ("Converting ACL", Fcons (absname, Qnil));
+         return Qnil;
+       }
+
+      encoded_absname = ENCODE_FILE (absname);
+
+      fail = (acl_set_file (SSDATA (encoded_absname), ACL_TYPE_ACCESS,
+                           acl)
+             != 0);
+      if (fail && errno != ENOTSUP)
+       report_file_error ("Setting ACL", Fcons (absname, Qnil));
+
+      acl_free (acl);
+    }
+#endif
+
+  return Qnil;
+}
+\f
 DEFUN ("file-modes", Ffile_modes, Sfile_modes, 1, 1, 0,
        doc: /* Return mode bits of file named FILENAME, as an integer.
 Return nil, if file does not exist or is not accessible.  */)
@@ -5630,6 +5760,8 @@ syms_of_fileio (void)
   DEFSYM (Qset_file_times, "set-file-times");
   DEFSYM (Qfile_selinux_context, "file-selinux-context");
   DEFSYM (Qset_file_selinux_context, "set-file-selinux-context");
+  DEFSYM (Qfile_acl, "file-acl");
+  DEFSYM (Qset_file_acl, "set-file-acl");
   DEFSYM (Qfile_newer_than_file_p, "file-newer-than-file-p");
   DEFSYM (Qinsert_file_contents, "insert-file-contents");
   DEFSYM (Qwrite_region, "write-region");
@@ -5849,6 +5981,8 @@ This includes interactive calls to `delete-file' and
   defsubr (&Sset_file_modes);
   defsubr (&Sset_file_times);
   defsubr (&Sfile_selinux_context);
+  defsubr (&Sfile_acl);
+  defsubr (&Sset_file_acl);
   defsubr (&Sset_file_selinux_context);
   defsubr (&Sset_default_file_modes);
   defsubr (&Sdefault_file_modes);