aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/nvim/bufwrite.c12
-rw-r--r--src/nvim/eval/funcs.c3
-rw-r--r--src/nvim/os/fs.c97
3 files changed, 111 insertions, 1 deletions
diff --git a/src/nvim/bufwrite.c b/src/nvim/bufwrite.c
index 40b95e5790..db813a3ae1 100644
--- a/src/nvim/bufwrite.c
+++ b/src/nvim/bufwrite.c
@@ -913,6 +913,9 @@ static int buf_write_make_backup(char *fname, bool append, FileInfo *file_info_o
&& os_chown(*backupp, (uv_uid_t)-1, (uv_gid_t)file_info_old->stat.st_gid) != 0) {
os_setperm(*backupp, ((int)perm & 0707) | (((int)perm & 07) << 3));
}
+# ifdef HAVE_XATTR
+ os_copy_xattr(fname, *backupp);
+# endif
#endif
// copy the file
@@ -929,6 +932,9 @@ static int buf_write_make_backup(char *fname, bool append, FileInfo *file_info_o
(double)file_info_old->stat.st_mtim.tv_sec);
#endif
os_set_acl(*backupp, acl);
+#ifdef HAVE_XATTR
+ os_copy_xattr(fname, *backupp);
+#endif
*err = set_err(NULL);
break;
}
@@ -1634,6 +1640,12 @@ restore_backup:
end = 0;
}
+ if (!backup_copy) {
+#ifdef HAVE_XATTR
+ os_copy_xattr(backup, wfname);
+#endif
+ }
+
#ifdef UNIX
// When creating a new file, set its owner/group to that of the original
// file. Get the new device and inode number.
diff --git a/src/nvim/eval/funcs.c b/src/nvim/eval/funcs.c
index 710d9c73a5..5bfce7c272 100644
--- a/src/nvim/eval/funcs.c
+++ b/src/nvim/eval/funcs.c
@@ -3171,6 +3171,9 @@ static void f_has(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
"windows",
"winaltkeys",
"writebackup",
+#ifdef HAVE_XATTR
+ "xattr",
+#endif
"nvim",
};
diff --git a/src/nvim/os/fs.c b/src/nvim/os/fs.c
index 77c4766419..3efb575039 100644
--- a/src/nvim/os/fs.c
+++ b/src/nvim/os/fs.c
@@ -18,6 +18,8 @@
# include <shlobj.h>
#endif
+#include "auto/config.h"
+
#if defined(HAVE_ACL)
# ifdef HAVE_SYS_ACL_H
# include <sys/acl.h>
@@ -27,7 +29,11 @@
# endif
#endif
-#include "auto/config.h"
+#ifdef HAVE_XATTR
+# include <attr/xattr.h>
+# define XATTR_VAL_LEN 1024
+#endif
+
#include "nvim/ascii.h"
#include "nvim/gettext.h"
#include "nvim/globals.h"
@@ -55,6 +61,17 @@
# include "os/fs.c.generated.h"
#endif
+#ifdef HAVE_XATTR
+static const char e_xattr_erange[]
+ = N_("E1506: Buffer too small to copy xattr value or key");
+static const char e_xattr_enotsup[]
+ = N_("E1507: Extended attributes are not supported by the filesystem");
+static const char e_xattr_e2big[]
+ = N_("E1508: size of the extended attribute value is larger than the maximum size allowed");
+static const char e_xattr_other[]
+ = N_("E1509: error occured when reading or writing extended attribute");
+#endif
+
struct iovec;
#define RUN_UV_FS_FUNC(ret, func, ...) \
@@ -743,6 +760,84 @@ int os_setperm(const char *const name, int perm)
return (r == kLibuvSuccess ? OK : FAIL);
}
+#ifdef HAVE_XATTR
+/// Copy extended attributes from_file to to_file
+void os_copy_xattr(const char *from_file, const char *to_file)
+{
+ if (from_file == NULL) {
+ return;
+ }
+
+ // get the length of the extended attributes
+ ssize_t size = listxattr((char *)from_file, NULL, 0);
+ // not supported or no attributes to copy
+ if (errno == ENOTSUP || size <= 0) {
+ return;
+ }
+ char *xattr_buf = xmalloc((size_t)size);
+ size = listxattr(from_file, xattr_buf, (size_t)size);
+ ssize_t tsize = size;
+
+ errno = 0;
+
+ ssize_t max_vallen = 0;
+ char *val = NULL;
+ const char *errmsg = NULL;
+
+ for (int round = 0; round < 2; round++) {
+ char *key = xattr_buf;
+ if (round == 1) {
+ size = tsize;
+ }
+
+ while (size > 0) {
+ ssize_t vallen = getxattr(from_file, key, val, round ? (size_t)max_vallen : 0);
+ // only set the attribute in the second round
+ if (vallen >= 0 && round
+ && setxattr(to_file, key, val, (size_t)vallen, 0) == 0) {
+ //
+ } else if (errno) {
+ switch (errno) {
+ case E2BIG:
+ errmsg = e_xattr_e2big;
+ goto error_exit;
+ case ENOTSUP:
+ errmsg = e_xattr_enotsup;
+ goto error_exit;
+ case ERANGE:
+ errmsg = e_xattr_erange;
+ goto error_exit;
+ default:
+ errmsg = e_xattr_other;
+ goto error_exit;
+ }
+ }
+
+ if (round == 0 && vallen > max_vallen) {
+ max_vallen = vallen;
+ }
+
+ // add one for terminating null
+ ssize_t keylen = (ssize_t)strlen(key) + 1;
+ size -= keylen;
+ key += keylen;
+ }
+ if (round) {
+ break;
+ }
+
+ val = xmalloc((size_t)max_vallen + 1);
+ }
+error_exit:
+ xfree(xattr_buf);
+ xfree(val);
+
+ if (errmsg != NULL) {
+ emsg(errmsg);
+ }
+}
+#endif
+
// Return a pointer to the ACL of file "fname" in allocated memory.
// Return NULL if the ACL is not available for whatever reason.
vim_acl_T os_get_acl(const char *fname)