From mboxrd@z Thu Jan 1 00:00:00 1970 Path: news.gmane.org!not-for-mail From: Romain Francoise Newsgroups: gmane.emacs.devel Subject: [PATCH] POSIX ACL support Date: Sun, 18 Nov 2012 23:27:22 +0100 Organization: orebokech dot com Message-ID: <878v9yr1h1.fsf@silenus.orebokech.com> NNTP-Posting-Host: plane.gmane.org Mime-Version: 1.0 Content-Type: text/plain X-Trace: ger.gmane.org 1353277656 11183 80.91.229.3 (18 Nov 2012 22:27:36 GMT) X-Complaints-To: usenet@ger.gmane.org NNTP-Posting-Date: Sun, 18 Nov 2012 22:27:36 +0000 (UTC) To: emacs-devel@gnu.org Original-X-From: emacs-devel-bounces+ged-emacs-devel=m.gmane.org@gnu.org Sun Nov 18 23:27:46 2012 Return-path: Envelope-to: ged-emacs-devel@m.gmane.org Original-Received: from lists.gnu.org ([208.118.235.17]) by plane.gmane.org with esmtp (Exim 4.69) (envelope-from ) id 1TaDL5-0008Kh-UR for ged-emacs-devel@m.gmane.org; Sun, 18 Nov 2012 23:27:44 +0100 Original-Received: from localhost ([::1]:38088 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1TaDKv-0008Pe-O2 for ged-emacs-devel@m.gmane.org; Sun, 18 Nov 2012 17:27:33 -0500 Original-Received: from eggs.gnu.org ([208.118.235.92]:47219) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1TaDKq-0008PH-4A for emacs-devel@gnu.org; Sun, 18 Nov 2012 17:27:31 -0500 Original-Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1TaDKm-0002Nj-WE for emacs-devel@gnu.org; Sun, 18 Nov 2012 17:27:27 -0500 Original-Received: from stringer.orebokech.com ([88.190.240.207]:53907) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1TaDKm-0002NV-In for emacs-devel@gnu.org; Sun, 18 Nov 2012 17:27:24 -0500 Original-Received: from silenus.orebokech.com (silenus [192.168.1.4]) by stringer.orebokech.com (Postfix) with ESMTP id 527AA18802DA for ; Sun, 18 Nov 2012 23:27:22 +0100 (CET) Original-Received: by silenus.orebokech.com (Postfix, from userid 1000) id 159E1A023C; Sun, 18 Nov 2012 23:27:22 +0100 (CET) X-detected-operating-system: by eggs.gnu.org: GNU/Linux 2.6.x X-Received-From: 88.190.240.207 X-BeenThere: emacs-devel@gnu.org X-Mailman-Version: 2.1.14 Precedence: list List-Id: "Emacs development discussions." List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: emacs-devel-bounces+ged-emacs-devel=m.gmane.org@gnu.org Original-Sender: emacs-devel-bounces+ged-emacs-devel=m.gmane.org@gnu.org Xref: news.gmane.org gmane.emacs.devel:154938 Archived-At: Hi, The following patch adds support for preserving POSIX ACL entries of files, which at the moment are lost when Emacs saves a buffer (unless `backup-by-copying' is set). This is a similar problem as preserving the SELinux context, and as a consequence the implementation is also very similar. Only the "portable" interface is used, I tested this patch successfully on GNU/Linux (using libacl) and FreeBSD. Comments welcome! Thanks, diff --git a/configure.ac b/configure.ac index 1884cc7..fb5d8dd 100644 --- a/configure.ac +++ b/configure.ac @@ -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]) ## For the times when you want to build Emacs but don't have @@ -2151,6 +2152,24 @@ fi AC_SUBST(LIBGNUTLS_LIBS) AC_SUBST(LIBGNUTLS_CFLAGS) +dnl On GNU/Linux, ACL support is provided by libacl. On BSD, +dnl ACL support is directly provided by libc. +HAVE_ACL=no +LIBACL_LIBS= +if test "${with_acl}" = "yes"; then + AC_CHECK_LIB([acl], [acl_set_file], HAVE_ACL=yes, HAVE_ACL=no) + if test "$HAVE_ACL" = yes; then + AC_DEFINE(HAVE_ACL, 1, [Define to 1 if using ACL support.]) + LIBACL_LIBS=-lacl + else + AC_CHECK_FUNC(acl_set_file, HAVE_ACL=yes, HAVE_ACL=no) + if test "$HAVE_ACL" = yes; then + AC_DEFINE(HAVE_ACL, 1, [Define to 1 if using 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 @@ -4435,6 +4454,7 @@ echo " Does Emacs use -ldbus? ${HAVE_DBUS}" echo " Does Emacs use -lgconf? ${HAVE_GCONF}" echo " Does Emacs use GSettings? ${HAVE_GSETTINGS}" echo " Does Emacs use -lselinux? ${HAVE_LIBSELINUX}" +echo " Does Emacs use ACL support? ${HAVE_ACL}" echo " Does Emacs use -lgnutls? ${HAVE_GNUTLS}" echo " Does Emacs use -lxml2? ${HAVE_LIBXML2}" diff --git a/doc/lispref/files.texi b/doc/lispref/files.texi index a5710c7..03fe290 100644 --- a/doc/lispref/files.texi +++ b/doc/lispref/files.texi @@ -1350,6 +1350,24 @@ 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 POSIX ACL + Several operating systems, like GNU/Linux and BSD, support +fine-grained discretionary access rights for files and directories using +the POSIX ACL interface. If Emacs has been compiled with ACL support, +you can use the function @code{file-posix-acl} to retrieve a file's ACL +entries. For the function @code{set-file-posix-acl}, see @ref{Changing +Files}. + +@defun file-posix-acl filename +This function returns the POSIX ACL entries of the file @var{filename}. +The return value is a string containing the textual representation of +the ACL entries. See the @file{acl} man page for details about this +representation. + +If the file does not exist or is inaccessible, of if Emacs was not +compiled with ACL support, then the return value is @code{nil}. +@end defun + @node Locating Files @subsection How to Locate Files in Standard Places @cindex locate file in path @@ -1542,6 +1560,10 @@ 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-posix-acl} is non-@code{nil}, and +Emacs has been compiled with ACL support, this function attempts to copy +the file's POSIX ACL entries (@pxref{File Attributes}). @end deffn @deffn Command make-symbolic-link filename newname &optional ok-if-exists @@ -1682,6 +1704,14 @@ nothing if SELinux is disabled, or if Emacs was compiled without SELinux support. @end defun +@defun set-file-posix-acl filename acl-string +This function sets the POSIX ACL entries of the file @var{filename} to +@var{acl-string}. @xref{File Attributes}, for a brief description of +POSIX ACLs. The @var{acl-string} argument should be a string containing +the textual representation of the desired ACL entries. The function +does nothing if Emacs was compiled without ACL support. +@end defun + @node File Names @section File Names @cindex file names diff --git a/lisp/files.el b/lisp/files.el index 8e8a178..e507894 100644 --- a/lisp/files.el +++ b/lisp/files.el @@ -3878,13 +3878,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 SELINUXCONTEXT ACL 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. +Similarly, ACL is the result of `file-posix-acl' on the original file. BACKUPNAME is the backup file name, which is the old file renamed." (if (and make-backup-files (not backup-inhibited) (not buffer-backed-up) @@ -3913,7 +3914,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))) + (context (file-selinux-context buffer-file-name)) + (acl (file-posix-acl buffer-file-name))) ;; Actually write the back up file. (condition-case () (if (or file-precious-flag @@ -3933,10 +3935,10 @@ BACKUPNAME is the backup file name, which is the old file renamed." (<= (nth 2 attr) backup-by-copying-when-privileged-mismatch))) (or (nth 9 attr) (not (file-ownership-preserved-p real-file-name))))))) - (backup-buffer-copy real-file-name backupname modes context) + (backup-buffer-copy real-file-name backupname modes context acl) ;; rename-file should delete old backup. (rename-file real-file-name backupname t) - (setq setmodes (list modes context backupname))) + (setq setmodes (list modes context acl backupname))) (file-error ;; If trouble writing the backup, write it in ;; .emacs.d/%backup%. @@ -3944,7 +3946,7 @@ 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 context acl))) (setq buffer-backed-up t) ;; Now delete the old versions, if desired. (if delete-old-versions @@ -3956,7 +3958,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 context acl) (let ((umask (default-file-modes))) (unwind-protect (progn @@ -3985,7 +3987,9 @@ BACKUPNAME is the backup file name, which is the old file renamed." (and modes (set-file-modes to-name (logand modes #o1777))) (and context - (set-file-selinux-context to-name context))) + (set-file-selinux-context to-name context)) + (and acl + (set-file-posix-acl to-name acl))) (defvar file-name-version-regexp "\\(?:~\\|\\.~[-[:alnum:]:#@^._]+\\(?:~[[:digit:]]+\\)?~\\)" @@ -4561,7 +4565,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-selinux-context buffer-file-name (nth 1 setmodes)) + (set-file-posix-acl buffer-file-name (nth 2 setmodes))) (error nil)))) ;; If the auto-save file was recent before this command, ;; delete it now. @@ -4574,7 +4579,7 @@ 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 SELINUXCONTEXT ACL BACKUPNAME), like backup-buffer. (defun basic-save-buffer-1 () (prog1 (if save-buffer-coding-system @@ -4586,7 +4591,7 @@ 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 SELINUXCONTEXT ACL BACKUPNAME), like backup-buffer. (defun basic-save-buffer-2 () (let (tempsetmodes setmodes) (if (not (file-writable-p buffer-file-name)) @@ -4659,9 +4664,10 @@ Before and after saving the buffer, this function runs ;; Since we have created an entirely new file, ;; make sure it gets the right permission bits set. (setq setmodes (or setmodes - (list (or (file-modes buffer-file-name) + (list (or (file-modes buffer-file-name) (logand ?\666 umask)) (file-selinux-context buffer-file-name) + (file-posix-acl buffer-file-name) buffer-file-name))) ;; We succeeded in writing the temp file, ;; so rename it. @@ -4674,9 +4680,11 @@ Before and after saving the buffer, this function runs ;; Change the mode back, after writing. (setq setmodes (list (file-modes buffer-file-name) (file-selinux-context buffer-file-name) + (file-posix-acl 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-selinux-context buffer-file-name (nth 1 setmodes)) + (set-file-posix-acl (nth 2 setmodes))))) (let (success) (unwind-protect (progn @@ -4690,7 +4698,7 @@ Before and after saving the buffer, this function runs ;; the backup by renaming, undo the backing-up. (and setmodes (not success) (progn - (rename-file (nth 2 setmodes) buffer-file-name t) + (rename-file (nth 3 setmodes) buffer-file-name t) (setq buffer-backed-up nil)))))) setmodes)) diff --git a/src/Makefile.in b/src/Makefile.in index d034ad0..060d62a 100644 --- a/src/Makefile.in +++ b/src/Makefile.in @@ -283,6 +283,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 @@ -398,7 +400,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) diff --git a/src/fileio.c b/src/fileio.c index 572f6d8..7654668 100644 --- a/src/fileio.c +++ b/src/fileio.c @@ -36,6 +36,10 @@ along with GNU Emacs. If not, see . */ #include #endif +#ifdef HAVE_ACL +#include +#endif + #include #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_posix_acl; +static Lisp_Object Qset_file_posix_acl; static Lisp_Object Qfile_newer_than_file_p; Lisp_Object Qinsert_file_contents; Lisp_Object Qwrite_region; @@ -1829,7 +1835,7 @@ barf_or_query_if_file_exists (Lisp_Object absname, const char *querystring, return; } -DEFUN ("copy-file", Fcopy_file, Scopy_file, 2, 6, +DEFUN ("copy-file", Fcopy_file, Scopy_file, 2, 7, "fCopy file: \nGCopy %s to file: \np\nP", doc: /* Copy FILE to NEWNAME. Both args must be strings. If NEWNAME names a directory, copy FILE there. @@ -1854,8 +1860,11 @@ 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) +on the system, we copy the SELinux context of FILE to NEWNAME. + +If PRESERVE-POSIX-ACL is non-nil and Emacs is built with ACL support, +we copy the ACL 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, Lisp_Object preserve_posix_acl) { int ifd, ofd; int n; @@ -1870,6 +1879,9 @@ on the system, we copy the SELinux context of FILE to NEWNAME. */) security_context_t con; int conlength = 0; #endif +#ifdef HAVE_ACL + acl_t acl = NULL; +#endif encoded_file = encoded_newname = Qnil; GCPRO4 (file, newname, encoded_file, encoded_newname); @@ -1955,6 +1967,15 @@ on the system, we copy the SELinux context of FILE to NEWNAME. */) } #endif +#ifdef HAVE_ACL + if (!NILP (preserve_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) { @@ -2045,6 +2066,17 @@ on the system, we copy the SELinux context of FILE to NEWNAME. */) } #endif +#ifdef HAVE_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 (input_file_statable_p) { if (!NILP (keep_time)) @@ -2262,7 +2294,7 @@ This is what happens in interactive use with M-x. */) have copy-file prompt again. */ Fcopy_file (file, newname, NILP (ok_if_already_exists) ? Qnil : Qt, - Qt, Qt, Qt); + Qt, Qt, Qt, Qt); count = SPECPDL_INDEX (); specbind (Qdelete_by_moving_to_trash, Qnil); @@ -2934,6 +2966,104 @@ compiled with SELinux support. */) return Qnil; } +DEFUN ("file-posix-acl", Ffile_posix_acl, Sfile_posix_acl, 1, 1, 0, + doc: /* Return POSIX ACL entries of file named FILENAME, as a string. +Return nil if file does not exist or is not accessible, or if Emacs was +not compiled with ACL support. */) + (Lisp_Object filename) +{ + Lisp_Object absname; + Lisp_Object handler; +#ifdef HAVE_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_posix_acl); + if (!NILP (handler)) + return call2 (handler, Qfile_posix_acl, absname); + +#ifdef HAVE_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-posix-acl", Fset_file_posix_acl, Sset_file_posix_acl, + 2, 2, 0, + doc: /* Set POSIX ACL of file named FILENAME to ACL-STRING. +ACL-STRING should contain the textual representation of the ACL +entries in either long or short form. + +This function does nothing if Emacs was not compiled with ACL +support. */) + (Lisp_Object filename, Lisp_Object acl_string) +{ + Lisp_Object absname; + Lisp_Object handler; +#ifdef HAVE_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_posix_acl); + if (!NILP (handler)) + return call3 (handler, Qset_file_posix_acl, absname, acl_string); + +#ifdef HAVE_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; +} + 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. */) @@ -5812,6 +5942,8 @@ This includes interactive calls to `delete-file' and defsubr (&Sset_file_modes); defsubr (&Sset_file_times); defsubr (&Sfile_selinux_context); + defsubr (&Sfile_posix_acl); + defsubr (&Sset_file_posix_acl); defsubr (&Sset_file_selinux_context); defsubr (&Sset_default_file_modes); defsubr (&Sdefault_file_modes);