diff options
-rw-r--r-- | .github/workflows/ci.yml | 2 | ||||
-rw-r--r-- | runtime/doc/builtin.txt | 4 | ||||
-rw-r--r-- | runtime/doc/editing.txt | 9 | ||||
-rw-r--r-- | runtime/doc/tags | 6 | ||||
-rw-r--r-- | runtime/doc/various.txt | 3 | ||||
-rwxr-xr-x | src/auto/configure | 28 | ||||
-rw-r--r-- | src/bufwrite.c | 15 | ||||
-rw-r--r-- | src/config.h.in | 1 | ||||
-rw-r--r-- | src/configure.ac | 12 | ||||
-rw-r--r-- | src/errors.h | 10 | ||||
-rw-r--r-- | src/evalfunc.c | 7 | ||||
-rw-r--r-- | src/feature.h | 10 | ||||
-rw-r--r-- | src/os_unix.c | 95 | ||||
-rw-r--r-- | src/proto/os_unix.pro | 1 | ||||
-rw-r--r-- | src/testdir/test_writefile.vim | 23 | ||||
-rw-r--r-- | src/version.c | 7 |
16 files changed, 227 insertions, 6 deletions
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 8c2e5396b7..cd1615f9f4 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -110,6 +110,8 @@ jobs: tcl-dev \ cscope \ libsodium-dev \ + attr \ + libattr1-dev ) fi sudo apt-get update && sudo apt-get install -y "${PKGS[@]}" diff --git a/runtime/doc/builtin.txt b/runtime/doc/builtin.txt index 8a92ff6ecf..b4ea216f31 100644 --- a/runtime/doc/builtin.txt +++ b/runtime/doc/builtin.txt @@ -1,4 +1,4 @@ -*builtin.txt* For Vim version 9.0. Last change: 2023 Aug 09 +*builtin.txt* For Vim version 9.0. Last change: 2023 Sep 27 VIM REFERENCE MANUAL by Bram Moolenaar @@ -11082,6 +11082,8 @@ winaltkeys Compiled with 'winaltkeys' option. windows Compiled with support for more than one window. (always true) writebackup Compiled with 'writebackup' default on. +xattr Compiled with extended attributes support |xattr| + (currently only supported on Linux). xfontset Compiled with X fontset support |xfontset|. xim Compiled with X input method support |xim|. xpm Compiled with pixmap support. diff --git a/runtime/doc/editing.txt b/runtime/doc/editing.txt index 46279110a7..a015c8462a 100644 --- a/runtime/doc/editing.txt +++ b/runtime/doc/editing.txt @@ -1,4 +1,4 @@ -*editing.txt* For Vim version 9.0. Last change: 2023 Sep 22 +*editing.txt* For Vim version 9.0. Last change: 2023 Sep 27 VIM REFERENCE MANUAL by Bram Moolenaar @@ -1097,6 +1097,13 @@ will get the ACL info of the original file. The ACL info is also used to check if a file is read-only (when opening the file). + *xattr* *E1506* *E1507* *E1508* *E1509* +xattr stands for Extended Attributes It is an advanced way to save metadata +alongside the file in the filesystem. It depends on the actual filesystem +being used and Vim supports it only on a Linux system. + Vim attempts to preserve the extended attribute info when writing a file. +The backup file will get the extended attribute of the original file. + *read-only-share* When MS-Windows shares a drive on the network it can be marked as read-only. This means that even if the file read-only attribute is absent, and the ACL diff --git a/runtime/doc/tags b/runtime/doc/tags index 1df34d3a3d..249800fd29 100644 --- a/runtime/doc/tags +++ b/runtime/doc/tags @@ -1483,6 +1483,7 @@ $quote eval.txt /*$quote* +wildmenu various.txt /*+wildmenu* +windows various.txt /*+windows* +writebackup various.txt /*+writebackup* ++xattr various.txt /*+xattr* +xfontset various.txt /*+xfontset* +xim various.txt /*+xim* +xpm various.txt /*+xpm* @@ -4506,6 +4507,10 @@ E1502 builtin.txt /*E1502* E1503 builtin.txt /*E1503* E1504 builtin.txt /*E1504* E1505 builtin.txt /*E1505* +E1506 editing.txt /*E1506* +E1507 editing.txt /*E1507* +E1508 editing.txt /*E1508* +E1509 editing.txt /*E1509* E151 helphelp.txt /*E151* E152 helphelp.txt /*E152* E153 helphelp.txt /*E153* @@ -11224,6 +11229,7 @@ x-resources version5.txt /*x-resources* x11-clientserver remote.txt /*x11-clientserver* x11-cut-buffer gui_x11.txt /*x11-cut-buffer* x11-selection gui_x11.txt /*x11-selection* +xattr editing.txt /*xattr* xf86conf.vim syntax.txt /*xf86conf.vim* xfontset mbyte.txt /*xfontset* xfree-xterm syntax.txt /*xfree-xterm* diff --git a/runtime/doc/various.txt b/runtime/doc/various.txt index e478c8266e..b2b7903935 100644 --- a/runtime/doc/various.txt +++ b/runtime/doc/various.txt @@ -1,4 +1,4 @@ -*various.txt* For Vim version 9.0. Last change: 2022 Dec 13 +*various.txt* For Vim version 9.0. Last change: 2023 Sep 27 VIM REFERENCE MANUAL by Bram Moolenaar @@ -503,6 +503,7 @@ T *+windows* more than one window; Always enabled since 8.0.1118. m *+writebackup* |'writebackup'| is default on m *+xim* X input method |xim| *+xfontset* X fontset support |xfontset| +N *+xattr* compiled with extended attribute support (Linux only) *+xpm* pixmap support m *+xpm_w32* Win32 GUI only: pixmap support |w32-xpm-support| *+xsmp* XSMP (X session management) support diff --git a/src/auto/configure b/src/auto/configure index 34e9f449aa..54c1aa8159 100755 --- a/src/auto/configure +++ b/src/auto/configure @@ -825,6 +825,7 @@ with_global_runtime with_modified_by enable_smack enable_selinux +enable_xattr with_features with_compiledby enable_xsmp @@ -1514,6 +1515,7 @@ Optional Features: --disable-darwin Disable Darwin (Mac OS X) support. --disable-smack Do not check for Smack support. --disable-selinux Do not check for SELinux support. + --disable-xattr Do not check for XATTR support. --disable-xsmp Disable XSMP session management --disable-xsmp-interact Disable XSMP interaction --enable-luainterp=OPTS Include Lua interpreter. default=no OPTS=no/yes/dynamic @@ -5419,6 +5421,32 @@ printf "%s\n" "yes" >&6; } fi fi +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking --enable-xattr argument" >&5 +printf %s "checking --enable-xattr argument... " >&6; } +# Check whether --enable-xattr was given. +if test ${enable_xattr+y} +then : + enableval=$enable_xattr; +else $as_nop + enable_xattr="yes" +fi + +if test "$enable_xattr" = "yes"; then + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +printf "%s\n" "yes" >&6; } + ac_fn_c_check_header_compile "$LINENO" "attr/xattr.h" "ac_cv_header_attr_xattr_h" "$ac_includes_default" +if test "x$ac_cv_header_attr_xattr_h" = xyes +then : + printf "%s\n" "#define HAVE_XATTR 1" >>confdefs.h + +fi + +else + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 +printf "%s\n" "no" >&6; } +fi + + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking --with-features argument" >&5 printf %s "checking --with-features argument... " >&6; } diff --git a/src/bufwrite.c b/src/bufwrite.c index 03a83b569a..bf79ad5bf3 100644 --- a/src/bufwrite.c +++ b/src/bufwrite.c @@ -1471,6 +1471,9 @@ buf_write( # if defined(HAVE_SELINUX) || defined(HAVE_SMACK) mch_copy_sec(fname, backup); # endif +# ifdef FEAT_XATTR + mch_copy_xattr(fname, backup); +# endif #endif // copy the file. @@ -1506,6 +1509,9 @@ buf_write( #if defined(HAVE_SELINUX) || defined(HAVE_SMACK) mch_copy_sec(fname, backup); #endif +#ifdef FEAT_XATTR + mch_copy_xattr(fname, backup); +#endif #ifdef MSWIN (void)mch_copy_file_attribute(fname, backup); #endif @@ -2196,11 +2202,18 @@ restore_backup: } #endif -#if defined(HAVE_SELINUX) || defined(HAVE_SMACK) +#if defined(HAVE_SELINUX) || defined(HAVE_SMACK) || defined(FEAT_XATTR) // Probably need to set the security context. if (!backup_copy) + { +#if defined(HAVE_SELINUX) || defined(HAVE_SMACK) mch_copy_sec(backup, wfname); #endif +#ifdef FEAT_XATTR + mch_copy_xattr(backup, wfname); +#endif + } +#endif #ifdef UNIX // When creating a new file, set its owner/group to that of the diff --git a/src/config.h.in b/src/config.h.in index 93972ca0cc..8ad9f03136 100644 --- a/src/config.h.in +++ b/src/config.h.in @@ -226,6 +226,7 @@ #undef HAVE_MBLEN #undef HAVE_TIMER_CREATE #undef HAVE_CLOCK_GETTIME +#undef HAVE_XATTR /* Define, if needed, for accessing large files. */ #undef _LARGE_FILES diff --git a/src/configure.ac b/src/configure.ac index bfdcfea4d2..e21e23490a 100644 --- a/src/configure.ac +++ b/src/configure.ac @@ -513,6 +513,18 @@ if test "x$found_smack" = "x"; then fi fi +dnl enable xattr support +AC_MSG_CHECKING(--enable-xattr argument) +AC_ARG_ENABLE(xattr, + [ --disable-xattr Do not check for XATTR support.], + , enable_xattr="yes") +if test "$enable_xattr" = "yes"; then + AC_MSG_RESULT(yes) + AC_CHECK_HEADER([attr/xattr.h], [AC_DEFINE(HAVE_XATTR)]) +else + AC_MSG_RESULT(no) +fi + dnl Check user requested features. AC_MSG_CHECKING(--with-features argument) diff --git a/src/errors.h b/src/errors.h index 6b4416963a..16b38cf99a 100644 --- a/src/errors.h +++ b/src/errors.h @@ -3552,6 +3552,14 @@ EXTERN char e_positional_arg_num_type_inconsistent_str_str[] INIT(= N_("E1504: Positional argument %d type used inconsistently: %s/%s")); EXTERN char e_invalid_format_specifier_str[] INIT(= N_("E1505: Invalid format specifier: %s")); -// E1506 - E1519 unused +EXTERN char e_xattr_erange[] + INIT(= N_("E1506: Buffer too small to copy xattr value or key")); +EXTERN char e_xattr_enotsup[] + INIT(= N_("E1507: Extended attributes are not supported by the filesystem")); +EXTERN char e_xattr_e2big[] + INIT(= N_("E1508: size of the extended attribute value is larger than the maximum size allowed")); +EXTERN char e_xattr_other[] + INIT(= N_("E1509: error occured when reading or writing extended attribute")); +// E1509 - E1519 unused EXTERN char e_aptypes_is_null_nr_str[] INIT(= "E1520: Internal error: ap_types or ap_types[idx] is NULL: %d: %s"); diff --git a/src/evalfunc.c b/src/evalfunc.c index 2cd1985a06..501ee03582 100644 --- a/src/evalfunc.c +++ b/src/evalfunc.c @@ -6464,6 +6464,13 @@ f_has(typval_T *argvars, typval_T *rettv) 0 #endif }, + {"xattr", +#ifdef FEAT_XATTR + 1 +#else + 0 +#endif + }, {"xim", #ifdef FEAT_XIM 1 diff --git a/src/feature.h b/src/feature.h index ca180dd1f0..b26dc6ccf1 100644 --- a/src/feature.h +++ b/src/feature.h @@ -22,7 +22,7 @@ * - Add a #define below. * - Add a message in the table above ex_version(). * - Add a string to f_has(). - * - Add a feature to ":help feature-list" in doc/eval.txt. + * - Add a feature to ":help feature-list" in doc/builtin.txt. * - Add feature to ":help +feature-list" in doc/various.txt. * - Add comment for the documentation of commands that use the feature. */ @@ -1175,3 +1175,11 @@ || defined(FEAT_TERMINAL) # define USING_LOAD_LIBRARY #endif + +/* + * XATTR support + */ + +#if defined(FEAT_NORMAL) && defined(HAVE_XATTR) +# define FEAT_XATTR +#endif diff --git a/src/os_unix.c b/src/os_unix.c index c5a54e419e..674dd96666 100644 --- a/src/os_unix.c +++ b/src/os_unix.c @@ -35,6 +35,11 @@ static int selinux_enabled = -1; #endif +#ifdef FEAT_XATTR +# include <attr/xattr.h> +# define XATTR_VAL_LEN 1024 +#endif + #ifdef HAVE_SMACK # include <attr/xattr.h> # include <linux/xattr.h> @@ -3096,6 +3101,96 @@ mch_copy_sec(char_u *from_file, char_u *to_file) } #endif // HAVE_SMACK +#ifdef FEAT_XATTR +/* + * Copy extended attributes from_file to to_file + */ + void +mch_copy_xattr(char_u *from_file, char_u *to_file) +{ + char *xattr_buf; + size_t size; + size_t tsize; + ssize_t keylen, vallen, max_vallen = 0; + char *key; + char *val = NULL; + char *errmsg = NULL; + + if (from_file == NULL) + return; + + // get the length of the extended attributes + size = listxattr((char *)from_file, NULL, 0); + // not supported or no attributes to copy + if (errno == ENOTSUP || size == 0) + return; + xattr_buf = (char*)alloc(size); + if (xattr_buf == NULL) + return; + size = listxattr((char *)from_file, xattr_buf, size); + tsize = size; + + errno = 0; + + for (int round = 0; round < 2; round++) + { + + key = xattr_buf; + if (round == 1) + size = tsize; + + while (size > 0) + { + vallen = getxattr((char *)from_file, key, + val, round ? max_vallen : 0); + // only set the attribute in the second round + if (vallen >= 0 && round && + setxattr((char *)to_file, key, val, 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 + keylen = STRLEN(key) + 1; + size -= keylen; + key += keylen; + } + if (round) + break; + + val = (char*)alloc(max_vallen + 1); + if (val == NULL) + goto error_exit; + + } +error_exit: + vim_free(xattr_buf); + vim_free(val); + + if (errmsg != NULL) + emsg((char *)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. diff --git a/src/proto/os_unix.pro b/src/proto/os_unix.pro index c3a8483f87..6e13de6caa 100644 --- a/src/proto/os_unix.pro +++ b/src/proto/os_unix.pro @@ -37,6 +37,7 @@ long mch_getperm(char_u *name); int mch_setperm(char_u *name, long perm); int mch_fsetperm(int fd, long perm); void mch_copy_sec(char_u *from_file, char_u *to_file); +void mch_copy_xattr(char_u *from_file, char_u *to_file); vim_acl_T mch_get_acl(char_u *fname); void mch_set_acl(char_u *fname, vim_acl_T aclent); void mch_free_acl(vim_acl_T aclent); diff --git a/src/testdir/test_writefile.vim b/src/testdir/test_writefile.vim index 140b2ee037..a54efa7cf9 100644 --- a/src/testdir/test_writefile.vim +++ b/src/testdir/test_writefile.vim @@ -977,4 +977,27 @@ func Test_wq_quitpre_autocommand() call delete('Xsomefile') endfunc +func Test_write_with_xattr_support() + CheckLinux + CheckExecutable setfattr + + let contents = ["file with xattrs", "line two"] + call writefile(contents, 'Xwattr.txt', 'D') + " write a couple of xattr + call system('setfattr -n user.cookie -v chocolate Xwattr.txt') + call system('setfattr -n user.frieda -v bar Xwattr.txt') + call system('setfattr -n user.empty Xwattr.txt') + + set backupcopy=no writebackup& backup& + sp Xwattr.txt + w + $r! getfattr -d % + let expected = ['file with xattrs', 'line two', '# file: Xwattr.txt', 'user.cookie="chocolate"', 'user.empty=""', 'user.frieda="bar"', ''] + call assert_equal(expected, getline(1,'$')) + + set backupcopy& + bw! + +endfunc + " vim: shiftwidth=2 sts=2 expandtab diff --git a/src/version.c b/src/version.c index 5404c02b0e..ffa5afcb57 100644 --- a/src/version.c +++ b/src/version.c @@ -654,6 +654,11 @@ static char *(features[]) = "-X11", # endif #endif +# ifdef FEAT_XATTR + "+xattr", +# else + "-xattr", +# endif #ifdef FEAT_XFONTSET "+xfontset", #else @@ -700,6 +705,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ /**/ + 1962, +/**/ 1961, /**/ 1960, |