diff --git a/.travis.yml b/.travis.yml index c0d29f0fff..bdae3bea2d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -15,6 +15,8 @@ install: - sudo apt-get install -y check # for mod_lang - sudo apt-get install -y gettext + # for xattr support + - sudo apt-get install -y libattr1-dev # for mod_cap - sudo apt-get install -y libcap-dev # for mod_geoip diff --git a/config.h.in b/config.h.in index 43054d41d1..d3053656f4 100644 --- a/config.h.in +++ b/config.h.in @@ -276,6 +276,18 @@ /* Define if you have the endprotoent function. */ #undef HAVE_ENDPROTOENT +/* Define if you have the extattr_delete_link function. */ +#undef HAVE_EXTATTR_DELETE_LINK + +/* Define if you have the extattr_delete_link function. */ +#undef HAVE_EXTATTR_GET_LINK + +/* Define if you have the extattr_delete_link function. */ +#undef HAVE_EXTATTR_LIST_LINK + +/* Define if you have the extattr_delete_link function. */ +#undef HAVE_EXTATTR_SET_LINK + /* Define if you have the fconvert function. */ #undef HAVE_FCONVERT @@ -378,9 +390,21 @@ /* Define if you have the inet_pton function. */ #undef HAVE_INET_PTON +/* Define if you have the lgetxattr function. */ +#undef HAVE_LGETXATTR + +/* Define if you have the llistxattr function. */ +#undef HAVE_LLISTXATTR + /* Define if you have the loginrestrictions function. */ #undef HAVE_LOGINRESTRICTIONS +/* Define if you have the lremovexattr function. */ +#undef HAVE_LREMOVEXATTR + +/* Define if you have the lsetxattr function. */ +#undef HAVE_LSETXATTR + /* Define if you have the memcpy function. */ #undef HAVE_MEMCPY @@ -567,6 +591,9 @@ /* Define if you have the header file. */ #undef HAVE_ARPA_INET_H +/* Define if you have the header file. */ +#undef HAVE_ATTR_XATTR_H + /* Define if you have the header file. */ #undef HAVE_BSTRING_H @@ -714,6 +741,9 @@ /* Define if you have the header file. */ #undef HAVE_SYS_DIR_H +/* Define if you have the header file. */ +#undef HAVE_SYS_EXTATTR_H + /* Define if you have the header file. */ #undef HAVE_SYS_FILE_H @@ -786,6 +816,9 @@ /* Define if you have that is POSIX.1 compatible. */ #undef HAVE_SYS_WAIT_H +/* Define if you have the header file. */ +#undef HAVE_SYS_XATTR_H + /* Define if you have the header file. */ #undef HAVE_TERMIOS_H @@ -1046,6 +1079,9 @@ /* Define if using trace support. */ #undef PR_USE_TRACE +/* Define if using xattr support. */ +#undef PR_USE_XATTR + /* Tunable parameters */ #undef PR_TUNABLE_BUFFER_SIZE #undef PR_TUNABLE_NEW_POOL_SIZE diff --git a/configure b/configure index d96db2e457..dd9297bf9c 100755 --- a/configure +++ b/configure @@ -1567,6 +1567,8 @@ Optional Features: --disable-trace disable trace support (default=no) + --disable-xattr disable extended attribute support (default=no) + --enable-devel enable developer-only code (default=no) --enable-buffer-size tune the the size (in bytes) of internal buffers @@ -3889,13 +3891,13 @@ if test "${lt_cv_nm_interface+set}" = set; then else lt_cv_nm_interface="BSD nm" echo "int some_variable = 0;" > conftest.$ac_ext - (eval echo "\"\$as_me:3892: $ac_compile\"" >&5) + (eval echo "\"\$as_me:3894: $ac_compile\"" >&5) (eval "$ac_compile" 2>conftest.err) cat conftest.err >&5 - (eval echo "\"\$as_me:3895: $NM \\\"conftest.$ac_objext\\\"\"" >&5) + (eval echo "\"\$as_me:3897: $NM \\\"conftest.$ac_objext\\\"\"" >&5) (eval "$NM \"conftest.$ac_objext\"" 2>conftest.err > conftest.out) cat conftest.err >&5 - (eval echo "\"\$as_me:3898: output\"" >&5) + (eval echo "\"\$as_me:3900: output\"" >&5) cat conftest.out >&5 if $GREP 'External.*some_variable' conftest.out > /dev/null; then lt_cv_nm_interface="MS dumpbin" @@ -5117,7 +5119,7 @@ ia64-*-hpux*) ;; *-*-irix6*) # Find out which ABI we are using. - echo '#line 5120 "configure"' > conftest.$ac_ext + echo '#line 5122 "configure"' > conftest.$ac_ext if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 (eval $ac_compile) 2>&5 ac_status=$? @@ -6967,11 +6969,11 @@ else -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \ -e 's:$: $lt_compiler_flag:'` - (eval echo "\"\$as_me:6970: $lt_compile\"" >&5) + (eval echo "\"\$as_me:6972: $lt_compile\"" >&5) (eval "$lt_compile" 2>conftest.err) ac_status=$? cat conftest.err >&5 - echo "$as_me:6974: \$? = $ac_status" >&5 + echo "$as_me:6976: \$? = $ac_status" >&5 if (exit $ac_status) && test -s "$ac_outfile"; then # The compiler can only warn and ignore the option if not recognized # So say no if there are warnings other than the usual output. @@ -7306,11 +7308,11 @@ else -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \ -e 's:$: $lt_compiler_flag:'` - (eval echo "\"\$as_me:7309: $lt_compile\"" >&5) + (eval echo "\"\$as_me:7311: $lt_compile\"" >&5) (eval "$lt_compile" 2>conftest.err) ac_status=$? cat conftest.err >&5 - echo "$as_me:7313: \$? = $ac_status" >&5 + echo "$as_me:7315: \$? = $ac_status" >&5 if (exit $ac_status) && test -s "$ac_outfile"; then # The compiler can only warn and ignore the option if not recognized # So say no if there are warnings other than the usual output. @@ -7411,11 +7413,11 @@ else -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \ -e 's:$: $lt_compiler_flag:'` - (eval echo "\"\$as_me:7414: $lt_compile\"" >&5) + (eval echo "\"\$as_me:7416: $lt_compile\"" >&5) (eval "$lt_compile" 2>out/conftest.err) ac_status=$? cat out/conftest.err >&5 - echo "$as_me:7418: \$? = $ac_status" >&5 + echo "$as_me:7420: \$? = $ac_status" >&5 if (exit $ac_status) && test -s out/conftest2.$ac_objext then # The compiler can only warn and ignore the option if not recognized @@ -7466,11 +7468,11 @@ else -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \ -e 's:$: $lt_compiler_flag:'` - (eval echo "\"\$as_me:7469: $lt_compile\"" >&5) + (eval echo "\"\$as_me:7471: $lt_compile\"" >&5) (eval "$lt_compile" 2>out/conftest.err) ac_status=$? cat out/conftest.err >&5 - echo "$as_me:7473: \$? = $ac_status" >&5 + echo "$as_me:7475: \$? = $ac_status" >&5 if (exit $ac_status) && test -s out/conftest2.$ac_objext then # The compiler can only warn and ignore the option if not recognized @@ -10235,7 +10237,7 @@ else lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2 lt_status=$lt_dlunknown cat > conftest.$ac_ext <<_LT_EOF -#line 10238 "configure" +#line 10240 "configure" #include "confdefs.h" #if HAVE_DLFCN_H @@ -10331,7 +10333,7 @@ else lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2 lt_status=$lt_dlunknown cat > conftest.$ac_ext <<_LT_EOF -#line 10334 "configure" +#line 10336 "configure" #include "confdefs.h" #if HAVE_DLFCN_H @@ -14004,11 +14006,11 @@ else -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \ -e 's:$: $lt_compiler_flag:'` - (eval echo "\"\$as_me:14007: $lt_compile\"" >&5) + (eval echo "\"\$as_me:14009: $lt_compile\"" >&5) (eval "$lt_compile" 2>conftest.err) ac_status=$? cat conftest.err >&5 - echo "$as_me:14011: \$? = $ac_status" >&5 + echo "$as_me:14013: \$? = $ac_status" >&5 if (exit $ac_status) && test -s "$ac_outfile"; then # The compiler can only warn and ignore the option if not recognized # So say no if there are warnings other than the usual output. @@ -14103,11 +14105,11 @@ else -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \ -e 's:$: $lt_compiler_flag:'` - (eval echo "\"\$as_me:14106: $lt_compile\"" >&5) + (eval echo "\"\$as_me:14108: $lt_compile\"" >&5) (eval "$lt_compile" 2>out/conftest.err) ac_status=$? cat out/conftest.err >&5 - echo "$as_me:14110: \$? = $ac_status" >&5 + echo "$as_me:14112: \$? = $ac_status" >&5 if (exit $ac_status) && test -s out/conftest2.$ac_objext then # The compiler can only warn and ignore the option if not recognized @@ -14155,11 +14157,11 @@ else -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \ -e 's:$: $lt_compiler_flag:'` - (eval echo "\"\$as_me:14158: $lt_compile\"" >&5) + (eval echo "\"\$as_me:14160: $lt_compile\"" >&5) (eval "$lt_compile" 2>out/conftest.err) ac_status=$? cat out/conftest.err >&5 - echo "$as_me:14162: \$? = $ac_status" >&5 + echo "$as_me:14164: \$? = $ac_status" >&5 if (exit $ac_status) && test -s out/conftest2.$ac_objext then # The compiler can only warn and ignore the option if not recognized @@ -18059,7 +18061,7 @@ else lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2 lt_status=$lt_dlunknown cat > conftest.$ac_ext <<_LT_EOF -#line 18062 "configure" +#line 18064 "configure" #include "confdefs.h" #if HAVE_DLFCN_H @@ -19698,7 +19700,9 @@ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ - + #ifdef HAVE_STDDEF_H + # include + #endif #ifdef HAVE_STDLIB_H # include #endif @@ -20558,6 +20562,12 @@ if test "${enable_trace+set}" = set; then fi +# Check whether --enable-xattr was given. +if test "${enable_xattr+set}" = set; then + enableval=$enable_xattr; +fi + + pr_devel_cflags="-g -O0 -Wcast-align -Wchar-subscripts -Winline -Wstrict-prototypes -Wmissing-declarations -Wnested-externs -Wpointer-arith -Wshadow -Wundef" pr_devel_libs="" @@ -20823,8 +20833,12 @@ cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ - #include - #include + #ifdef HAVE_STDDEF_H + # include + #endif + #ifdef HAVE_STDLIB_H + # include + #endif #ifdef HAVE_EXECINFO_H # include #endif @@ -20894,8 +20908,12 @@ cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ - #include - #include + #ifdef HAVE_STDDEF_H + # include + #endif + #ifdef HAVE_STDLIB_H + # include + #endif #ifdef HAVE_EXECINFO_H # include #endif @@ -33112,8 +33130,15 @@ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ - #include - #include + #ifdef HAVE_STDDEF_H + # include + #endif + #ifdef HAVE_STDLIB_H + # include + #endif + #ifdef HAVE_SYS_TYPES_H + # include + #endif #ifdef HAVE_ICONV_H # include #endif @@ -33180,9 +33205,18 @@ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ - - #include - #include + #ifdef HAVE_STDDEF_H + # include + #endif + #ifdef HAVE_STDLIB_H + # include + #endif + #ifdef HAVE_STDIO_H + # include + #endif + #ifdef HAVE_SYS_TYPES_H + # include + #endif #if HAVE_DIRENT_H # include #endif @@ -33249,8 +33283,15 @@ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ - - #include + #ifdef HAVE_STDDEF_H + # include + #endif + #ifdef HAVE_STDLIB_H + # include + #endif + #ifdef HAVE_STDIO_H + # include + #endif #if HAVE_SYS_TYPES_H # include #endif @@ -36133,7 +36174,15 @@ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ - #include + #ifdef HAVE_STDDEF_H + # include + #endif + #ifdef HAVE_STDLIB_H + # include + #endif + #ifdef HAVE_SYS_TYPES_H + # include + #endif #ifdef HAVE_SYS_ACL_H # include #endif @@ -36955,98 +37004,1204 @@ _ACEOF fi -{ echo "$as_me:$LINENO: checking whether setgrent returns void" >&5 -echo $ECHO_N "checking whether setgrent returns void... $ECHO_C" >&6; } -if test "${ac_cv_func_setgrent_void+set}" = set; then +if test x"$enable_xattr" != xno ; then + # On Free/Net/OpenBSD, it's sys/extattr.h + if test "${ac_cv_header_sys_extattr_h+set}" = set; then + { echo "$as_me:$LINENO: checking for sys/extattr.h" >&5 +echo $ECHO_N "checking for sys/extattr.h... $ECHO_C" >&6; } +if test "${ac_cv_header_sys_extattr_h+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 +fi +{ echo "$as_me:$LINENO: result: $ac_cv_header_sys_extattr_h" >&5 +echo "${ECHO_T}$ac_cv_header_sys_extattr_h" >&6; } else - if test "$cross_compiling" = yes; then - ac_cv_func_setgrent_void=yes + # Is the header compilable? +{ echo "$as_me:$LINENO: checking sys/extattr.h usability" >&5 +echo $ECHO_N "checking sys/extattr.h usability... $ECHO_C" >&6; } +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +#include +_ACEOF +rm -f conftest.$ac_objext +if { (ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 + (eval "$ac_compile") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest.$ac_objext; then + ac_header_compiler=yes else - cat >conftest.$ac_ext <<_ACEOF + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 - #include - #include - int main(int argc, char *argv) { - int i = 0; - getgrent(); - i = setgrent(); - return (i != 1); - } + ac_header_compiler=no +fi + +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +{ echo "$as_me:$LINENO: result: $ac_header_compiler" >&5 +echo "${ECHO_T}$ac_header_compiler" >&6; } +# Is the header present? +{ echo "$as_me:$LINENO: checking sys/extattr.h presence" >&5 +echo $ECHO_N "checking sys/extattr.h presence... $ECHO_C" >&6; } +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ _ACEOF -rm -f conftest$ac_exeext -if { (ac_try="$ac_link" +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include +_ACEOF +if { (ac_try="$ac_cpp conftest.$ac_ext" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 - (eval "$ac_link") 2>&5 + (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1 ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } && { ac_try='./conftest$ac_exeext' - { (case "(($ac_try" in + (exit $ac_status); } >/dev/null && { + test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" || + test ! -s conftest.err + }; then + ac_header_preproc=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_header_preproc=no +fi + +rm -f conftest.err conftest.$ac_ext +{ echo "$as_me:$LINENO: result: $ac_header_preproc" >&5 +echo "${ECHO_T}$ac_header_preproc" >&6; } + +# So? What about this header? +case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in + yes:no: ) + { echo "$as_me:$LINENO: WARNING: sys/extattr.h: accepted by the compiler, rejected by the preprocessor!" >&5 +echo "$as_me: WARNING: sys/extattr.h: accepted by the compiler, rejected by the preprocessor!" >&2;} + { echo "$as_me:$LINENO: WARNING: sys/extattr.h: proceeding with the compiler's result" >&5 +echo "$as_me: WARNING: sys/extattr.h: proceeding with the compiler's result" >&2;} + ac_header_preproc=yes + ;; + no:yes:* ) + { echo "$as_me:$LINENO: WARNING: sys/extattr.h: present but cannot be compiled" >&5 +echo "$as_me: WARNING: sys/extattr.h: present but cannot be compiled" >&2;} + { echo "$as_me:$LINENO: WARNING: sys/extattr.h: check for missing prerequisite headers?" >&5 +echo "$as_me: WARNING: sys/extattr.h: check for missing prerequisite headers?" >&2;} + { echo "$as_me:$LINENO: WARNING: sys/extattr.h: see the Autoconf documentation" >&5 +echo "$as_me: WARNING: sys/extattr.h: see the Autoconf documentation" >&2;} + { echo "$as_me:$LINENO: WARNING: sys/extattr.h: section \"Present But Cannot Be Compiled\"" >&5 +echo "$as_me: WARNING: sys/extattr.h: section \"Present But Cannot Be Compiled\"" >&2;} + { echo "$as_me:$LINENO: WARNING: sys/extattr.h: proceeding with the preprocessor's result" >&5 +echo "$as_me: WARNING: sys/extattr.h: proceeding with the preprocessor's result" >&2;} + { echo "$as_me:$LINENO: WARNING: sys/extattr.h: in the future, the compiler will take precedence" >&5 +echo "$as_me: WARNING: sys/extattr.h: in the future, the compiler will take precedence" >&2;} + + ;; +esac +{ echo "$as_me:$LINENO: checking for sys/extattr.h" >&5 +echo $ECHO_N "checking for sys/extattr.h... $ECHO_C" >&6; } +if test "${ac_cv_header_sys_extattr_h+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + ac_cv_header_sys_extattr_h=$ac_header_preproc +fi +{ echo "$as_me:$LINENO: result: $ac_cv_header_sys_extattr_h" >&5 +echo "${ECHO_T}$ac_cv_header_sys_extattr_h" >&6; } + +fi +if test $ac_cv_header_sys_extattr_h = yes; then + +cat >>confdefs.h <<\_ACEOF +#define HAVE_SYS_EXTATTR_H 1 +_ACEOF + + +cat >>confdefs.h <<\_ACEOF +#define PR_USE_XATTR 1 +_ACEOF + + + { echo "$as_me:$LINENO: checking for extattr_delete_link" >&5 +echo $ECHO_N "checking for extattr_delete_link... $ECHO_C" >&6; } + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + #ifdef HAVE_STDDEF_H + # include + #endif + #ifdef HAVE_STDLIB_H + # include + #endif + #ifdef HAVE_SYS_TYPES_H + # include + #endif + #ifdef HAVE_SYS_EXTATTR_H + # include + #endif + +int +main () +{ + + int res; + int namespace = 0; + const char *path = NULL, name = NULL; + res = extattr_delete_link(path, namespace, name); + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (ac_try="$ac_link" +case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 - (eval "$ac_try") 2>&5 + (eval "$ac_link") 2>conftest.er1 ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); }; }; then - ac_cv_func_setgrent_void=no + (exit $ac_status); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest$ac_exeext && + $as_test_x conftest$ac_exeext; then + + { echo "$as_me:$LINENO: result: yes" >&5 +echo "${ECHO_T}yes" >&6; } + +cat >>confdefs.h <<\_ACEOF +#define HAVE_EXTATTR_DELETE_LINK 1 +_ACEOF + + else - echo "$as_me: program exited with status $ac_status" >&5 -echo "$as_me: failed program was:" >&5 + echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 -( exit $ac_status ) -ac_cv_func_setgrent_void=yes -fi -rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext -fi + + { echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6; } fi -{ echo "$as_me:$LINENO: result: $ac_cv_func_setgrent_void" >&5 -echo "${ECHO_T}$ac_cv_func_setgrent_void" >&6; } -if test $ac_cv_func_setgrent_void = yes; then +rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \ + conftest$ac_exeext conftest.$ac_ext + + { echo "$as_me:$LINENO: checking for extattr_get_link" >&5 +echo $ECHO_N "checking for extattr_get_link... $ECHO_C" >&6; } + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + #ifdef HAVE_STDDEF_H + # include + #endif + #ifdef HAVE_STDLIB_H + # include + #endif + #ifdef HAVE_SYS_TYPES_H + # include + #endif + #ifdef HAVE_SYS_EXTATTR_H + # include + #endif + +int +main () +{ + + ssize_t res; + int namespace = 0; + const char *path = NULL, name = NULL; + void *val = NULL; + size_t sz = 0; + res = extattr_get_link(path, namespace, name, val, sz); + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (ac_try="$ac_link" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 + (eval "$ac_link") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest$ac_exeext && + $as_test_x conftest$ac_exeext; then + + { echo "$as_me:$LINENO: result: yes" >&5 +echo "${ECHO_T}yes" >&6; } cat >>confdefs.h <<\_ACEOF -#define SETGRENT_VOID 1 +#define HAVE_EXTATTR_GET_LINK 1 _ACEOF + +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + + { echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6; } + + fi +rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \ + conftest$ac_exeext conftest.$ac_ext -if test x"$ac_cv_header_curses_h" = xyes; then - { echo "$as_me:$LINENO: checking for initscr in -lcurses" >&5 -echo $ECHO_N "checking for initscr in -lcurses... $ECHO_C" >&6; } -if test "${ac_cv_lib_curses_initscr+set}" = set; then - echo $ECHO_N "(cached) $ECHO_C" >&6 -else - ac_check_lib_save_LIBS=$LIBS -LIBS="-lcurses $LIBS" -cat >conftest.$ac_ext <<_ACEOF + { echo "$as_me:$LINENO: checking for extattr_list_link" >&5 +echo $ECHO_N "checking for extattr_list_link... $ECHO_C" >&6; } + cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ + #ifdef HAVE_STDDEF_H + # include + #endif + #ifdef HAVE_STDLIB_H + # include + #endif + #ifdef HAVE_SYS_TYPES_H + # include + #endif + #ifdef HAVE_SYS_EXTATTR_H + # include + #endif -/* Override any GCC internal prototype to avoid an error. - Use char because int might match the return type of a GCC - builtin and then its argument prototype would still apply. */ -#ifdef __cplusplus -extern "C" -#endif -char initscr (); int main () { -return initscr (); + + ssize_t res; + int namespace = 0; + const char *path = NULL; + void *val = NULL; + size_t sz = 0; + res = extattr_list_link(path, namespace, val, sz); + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (ac_try="$ac_link" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 + (eval "$ac_link") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest$ac_exeext && + $as_test_x conftest$ac_exeext; then + + { echo "$as_me:$LINENO: result: yes" >&5 +echo "${ECHO_T}yes" >&6; } + +cat >>confdefs.h <<\_ACEOF +#define HAVE_EXTATTR_LIST_LINK 1 +_ACEOF + + +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + + { echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6; } + + +fi + +rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \ + conftest$ac_exeext conftest.$ac_ext + + { echo "$as_me:$LINENO: checking for extattr_set_link" >&5 +echo $ECHO_N "checking for extattr_set_link... $ECHO_C" >&6; } + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + #ifdef HAVE_STDDEF_H + # include + #endif + #ifdef HAVE_STDLIB_H + # include + #endif + #ifdef HAVE_SYS_TYPES_H + # include + #endif + #ifdef HAVE_SYS_EXTATTR_H + # include + #endif + +int +main () +{ + + int res; + int namespace = 0; + const char *path = NULL, name = NULL; + void *val = NULL; + size_t sz = 0; + res = extattr_set_link(path, namespace, name, val, sz); + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (ac_try="$ac_link" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 + (eval "$ac_link") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest$ac_exeext && + $as_test_x conftest$ac_exeext; then + + { echo "$as_me:$LINENO: result: yes" >&5 +echo "${ECHO_T}yes" >&6; } + +cat >>confdefs.h <<\_ACEOF +#define HAVE_EXTATTR_SET_LINK 1 +_ACEOF + + +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + + { echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6; } + + +fi + +rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \ + conftest$ac_exeext conftest.$ac_ext + +fi + + + + # On Linux/MacOSX, it's sys/xattr.h + if test "${ac_cv_header_sys_xattr_h+set}" = set; then + { echo "$as_me:$LINENO: checking for sys/xattr.h" >&5 +echo $ECHO_N "checking for sys/xattr.h... $ECHO_C" >&6; } +if test "${ac_cv_header_sys_xattr_h+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +fi +{ echo "$as_me:$LINENO: result: $ac_cv_header_sys_xattr_h" >&5 +echo "${ECHO_T}$ac_cv_header_sys_xattr_h" >&6; } +else + # Is the header compilable? +{ echo "$as_me:$LINENO: checking sys/xattr.h usability" >&5 +echo $ECHO_N "checking sys/xattr.h usability... $ECHO_C" >&6; } +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +#include +_ACEOF +rm -f conftest.$ac_objext +if { (ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 + (eval "$ac_compile") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest.$ac_objext; then + ac_header_compiler=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_header_compiler=no +fi + +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +{ echo "$as_me:$LINENO: result: $ac_header_compiler" >&5 +echo "${ECHO_T}$ac_header_compiler" >&6; } + +# Is the header present? +{ echo "$as_me:$LINENO: checking sys/xattr.h presence" >&5 +echo $ECHO_N "checking sys/xattr.h presence... $ECHO_C" >&6; } +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include +_ACEOF +if { (ac_try="$ac_cpp conftest.$ac_ext" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 + (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } >/dev/null && { + test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" || + test ! -s conftest.err + }; then + ac_header_preproc=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_header_preproc=no +fi + +rm -f conftest.err conftest.$ac_ext +{ echo "$as_me:$LINENO: result: $ac_header_preproc" >&5 +echo "${ECHO_T}$ac_header_preproc" >&6; } + +# So? What about this header? +case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in + yes:no: ) + { echo "$as_me:$LINENO: WARNING: sys/xattr.h: accepted by the compiler, rejected by the preprocessor!" >&5 +echo "$as_me: WARNING: sys/xattr.h: accepted by the compiler, rejected by the preprocessor!" >&2;} + { echo "$as_me:$LINENO: WARNING: sys/xattr.h: proceeding with the compiler's result" >&5 +echo "$as_me: WARNING: sys/xattr.h: proceeding with the compiler's result" >&2;} + ac_header_preproc=yes + ;; + no:yes:* ) + { echo "$as_me:$LINENO: WARNING: sys/xattr.h: present but cannot be compiled" >&5 +echo "$as_me: WARNING: sys/xattr.h: present but cannot be compiled" >&2;} + { echo "$as_me:$LINENO: WARNING: sys/xattr.h: check for missing prerequisite headers?" >&5 +echo "$as_me: WARNING: sys/xattr.h: check for missing prerequisite headers?" >&2;} + { echo "$as_me:$LINENO: WARNING: sys/xattr.h: see the Autoconf documentation" >&5 +echo "$as_me: WARNING: sys/xattr.h: see the Autoconf documentation" >&2;} + { echo "$as_me:$LINENO: WARNING: sys/xattr.h: section \"Present But Cannot Be Compiled\"" >&5 +echo "$as_me: WARNING: sys/xattr.h: section \"Present But Cannot Be Compiled\"" >&2;} + { echo "$as_me:$LINENO: WARNING: sys/xattr.h: proceeding with the preprocessor's result" >&5 +echo "$as_me: WARNING: sys/xattr.h: proceeding with the preprocessor's result" >&2;} + { echo "$as_me:$LINENO: WARNING: sys/xattr.h: in the future, the compiler will take precedence" >&5 +echo "$as_me: WARNING: sys/xattr.h: in the future, the compiler will take precedence" >&2;} + + ;; +esac +{ echo "$as_me:$LINENO: checking for sys/xattr.h" >&5 +echo $ECHO_N "checking for sys/xattr.h... $ECHO_C" >&6; } +if test "${ac_cv_header_sys_xattr_h+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + ac_cv_header_sys_xattr_h=$ac_header_preproc +fi +{ echo "$as_me:$LINENO: result: $ac_cv_header_sys_xattr_h" >&5 +echo "${ECHO_T}$ac_cv_header_sys_xattr_h" >&6; } + +fi +if test $ac_cv_header_sys_xattr_h = yes; then + +cat >>confdefs.h <<\_ACEOF +#define HAVE_SYS_XATTR_H 1 +_ACEOF + + +cat >>confdefs.h <<\_ACEOF +#define PR_USE_XATTR 1 +_ACEOF + + +for ac_header in attr/xattr.h +do +as_ac_Header=`echo "ac_cv_header_$ac_header" | $as_tr_sh` +if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then + { echo "$as_me:$LINENO: checking for $ac_header" >&5 +echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6; } +if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +fi +ac_res=`eval echo '${'$as_ac_Header'}'` + { echo "$as_me:$LINENO: result: $ac_res" >&5 +echo "${ECHO_T}$ac_res" >&6; } +else + # Is the header compilable? +{ echo "$as_me:$LINENO: checking $ac_header usability" >&5 +echo $ECHO_N "checking $ac_header usability... $ECHO_C" >&6; } +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +#include <$ac_header> +_ACEOF +rm -f conftest.$ac_objext +if { (ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 + (eval "$ac_compile") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest.$ac_objext; then + ac_header_compiler=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_header_compiler=no +fi + +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +{ echo "$as_me:$LINENO: result: $ac_header_compiler" >&5 +echo "${ECHO_T}$ac_header_compiler" >&6; } + +# Is the header present? +{ echo "$as_me:$LINENO: checking $ac_header presence" >&5 +echo $ECHO_N "checking $ac_header presence... $ECHO_C" >&6; } +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include <$ac_header> +_ACEOF +if { (ac_try="$ac_cpp conftest.$ac_ext" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 + (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } >/dev/null && { + test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" || + test ! -s conftest.err + }; then + ac_header_preproc=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_header_preproc=no +fi + +rm -f conftest.err conftest.$ac_ext +{ echo "$as_me:$LINENO: result: $ac_header_preproc" >&5 +echo "${ECHO_T}$ac_header_preproc" >&6; } + +# So? What about this header? +case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in + yes:no: ) + { echo "$as_me:$LINENO: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&5 +echo "$as_me: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the compiler's result" >&5 +echo "$as_me: WARNING: $ac_header: proceeding with the compiler's result" >&2;} + ac_header_preproc=yes + ;; + no:yes:* ) + { echo "$as_me:$LINENO: WARNING: $ac_header: present but cannot be compiled" >&5 +echo "$as_me: WARNING: $ac_header: present but cannot be compiled" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: check for missing prerequisite headers?" >&5 +echo "$as_me: WARNING: $ac_header: check for missing prerequisite headers?" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: see the Autoconf documentation" >&5 +echo "$as_me: WARNING: $ac_header: see the Autoconf documentation" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&5 +echo "$as_me: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the preprocessor's result" >&5 +echo "$as_me: WARNING: $ac_header: proceeding with the preprocessor's result" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: in the future, the compiler will take precedence" >&5 +echo "$as_me: WARNING: $ac_header: in the future, the compiler will take precedence" >&2;} + + ;; +esac +{ echo "$as_me:$LINENO: checking for $ac_header" >&5 +echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6; } +if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + eval "$as_ac_Header=\$ac_header_preproc" +fi +ac_res=`eval echo '${'$as_ac_Header'}'` + { echo "$as_me:$LINENO: result: $ac_res" >&5 +echo "${ECHO_T}$ac_res" >&6; } + +fi +if test `eval echo '${'$as_ac_Header'}'` = yes; then + cat >>confdefs.h <<_ACEOF +#define `echo "HAVE_$ac_header" | $as_tr_cpp` 1 +_ACEOF + +fi + +done + + + # Some platforms need libattr for extended attributes + +{ echo "$as_me:$LINENO: checking for setxattr in -lattr" >&5 +echo $ECHO_N "checking for setxattr in -lattr... $ECHO_C" >&6; } +if test "${ac_cv_lib_attr_setxattr+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-lattr $LIBS" +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char setxattr (); +int +main () +{ +return setxattr (); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (ac_try="$ac_link" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 + (eval "$ac_link") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest$ac_exeext && + $as_test_x conftest$ac_exeext; then + ac_cv_lib_attr_setxattr=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_cv_lib_attr_setxattr=no +fi + +rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +{ echo "$as_me:$LINENO: result: $ac_cv_lib_attr_setxattr" >&5 +echo "${ECHO_T}$ac_cv_lib_attr_setxattr" >&6; } +if test $ac_cv_lib_attr_setxattr = yes; then + cat >>confdefs.h <<_ACEOF +#define HAVE_LIBATTR 1 +_ACEOF + + LIBS="-lattr $LIBS" + +fi + + + { echo "$as_me:$LINENO: checking for lgetxattr" >&5 +echo $ECHO_N "checking for lgetxattr... $ECHO_C" >&6; } + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + #ifdef HAVE_STDDEF_H + # include + #endif + #ifdef HAVE_STDLIB_H + # include + #endif + #ifdef HAVE_SYS_TYPES_H + # include + #endif + #ifdef HAVE_SYS_XATTR_H + # include + #endif + +int +main () +{ + + ssize_t res; + const char *path = NULL, *name = NULL; + void *val = NULL; + size_t sz = 0; + res = lgetxattr(path, name, val, sz); + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (ac_try="$ac_link" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 + (eval "$ac_link") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest$ac_exeext && + $as_test_x conftest$ac_exeext; then + + { echo "$as_me:$LINENO: result: yes" >&5 +echo "${ECHO_T}yes" >&6; } + +cat >>confdefs.h <<\_ACEOF +#define HAVE_LGETXATTR 1 +_ACEOF + + +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + + { echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6; } + + +fi + +rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \ + conftest$ac_exeext conftest.$ac_ext + + { echo "$as_me:$LINENO: checking for llistxattr" >&5 +echo $ECHO_N "checking for llistxattr... $ECHO_C" >&6; } + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + #ifdef HAVE_STDDEF_H + # include + #endif + #ifdef HAVE_STDLIB_H + # include + #endif + #ifdef HAVE_SYS_TYPES_H + # include + #endif + #ifdef HAVE_SYS_XATTR_H + # include + #endif + +int +main () +{ + + ssize_t res; + const char *path = NULL; + char *names = NUL; + size_t namessz = 0; + res = llistxattr(path, names, namessz); + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (ac_try="$ac_link" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 + (eval "$ac_link") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest$ac_exeext && + $as_test_x conftest$ac_exeext; then + + { echo "$as_me:$LINENO: result: yes" >&5 +echo "${ECHO_T}yes" >&6; } + +cat >>confdefs.h <<\_ACEOF +#define HAVE_LLISTXATTR 1 +_ACEOF + + +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + + { echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6; } + + +fi + +rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \ + conftest$ac_exeext conftest.$ac_ext + + { echo "$as_me:$LINENO: checking for lremovexattr" >&5 +echo $ECHO_N "checking for lremovexattr... $ECHO_C" >&6; } + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + #ifdef HAVE_STDDEF_H + # include + #endif + #ifdef HAVE_STDLIB_H + # include + #endif + #ifdef HAVE_SYS_TYPES_H + # include + #endif + #ifdef HAVE_SYS_XATTR_H + # include + #endif + +int +main () +{ + + ssize_t res; + const char *path = NULL, *name = NULL; + res = lremovexattr(path, name); + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (ac_try="$ac_link" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 + (eval "$ac_link") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest$ac_exeext && + $as_test_x conftest$ac_exeext; then + + { echo "$as_me:$LINENO: result: yes" >&5 +echo "${ECHO_T}yes" >&6; } + +cat >>confdefs.h <<\_ACEOF +#define HAVE_LREMOVEXATTR 1 +_ACEOF + + +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + + { echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6; } + + +fi + +rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \ + conftest$ac_exeext conftest.$ac_ext + + { echo "$as_me:$LINENO: checking for lsetxattr" >&5 +echo $ECHO_N "checking for lsetxattr... $ECHO_C" >&6; } + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + #ifdef HAVE_STDDEF_H + # include + #endif + #ifdef HAVE_STDLIB_H + # include + #endif + #ifdef HAVE_SYS_TYPES_H + # include + #endif + #ifdef HAVE_SYS_XATTR_H + # include + #endif + +int +main () +{ + + int res, flags = 0; + const char *path = NULL, *name = NULL; + const void *val = NULL; + res = lsetxattr(path, name, val, flags); + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (ac_try="$ac_link" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 + (eval "$ac_link") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest$ac_exeext && + $as_test_x conftest$ac_exeext; then + + { echo "$as_me:$LINENO: result: yes" >&5 +echo "${ECHO_T}yes" >&6; } + +cat >>confdefs.h <<\_ACEOF +#define HAVE_LSETXATTR 1 +_ACEOF + + +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + + { echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6; } + + +fi + +rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \ + conftest$ac_exeext conftest.$ac_ext + +fi + + +fi + +{ echo "$as_me:$LINENO: checking whether setgrent returns void" >&5 +echo $ECHO_N "checking whether setgrent returns void... $ECHO_C" >&6; } +if test "${ac_cv_func_setgrent_void+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test "$cross_compiling" = yes; then + ac_cv_func_setgrent_void=yes +else + cat >conftest.$ac_ext <<_ACEOF + + #include + #include + int main(int argc, char *argv) { + int i = 0; + getgrent(); + i = setgrent(); + return (i != 1); + } + +_ACEOF +rm -f conftest$ac_exeext +if { (ac_try="$ac_link" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 + (eval "$ac_link") 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { ac_try='./conftest$ac_exeext' + { (case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 + (eval "$ac_try") 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_func_setgrent_void=no +else + echo "$as_me: program exited with status $ac_status" >&5 +echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +( exit $ac_status ) +ac_cv_func_setgrent_void=yes +fi +rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext +fi + + +fi +{ echo "$as_me:$LINENO: result: $ac_cv_func_setgrent_void" >&5 +echo "${ECHO_T}$ac_cv_func_setgrent_void" >&6; } + +if test $ac_cv_func_setgrent_void = yes; then + +cat >>confdefs.h <<\_ACEOF +#define SETGRENT_VOID 1 +_ACEOF + +fi + + +if test x"$ac_cv_header_curses_h" = xyes; then + { echo "$as_me:$LINENO: checking for initscr in -lcurses" >&5 +echo $ECHO_N "checking for initscr in -lcurses... $ECHO_C" >&6; } +if test "${ac_cv_lib_curses_initscr+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-lcurses $LIBS" +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char initscr (); +int +main () +{ +return initscr (); ; return 0; } @@ -38366,7 +39521,9 @@ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ - + #ifdef HAVE_STDDEF_H + # include + #endif #ifdef HAVE_STDLIB_H # include #endif @@ -38436,7 +39593,9 @@ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ - + #ifdef HAVE_STDDEF_H + # include + #endif #ifdef HAVE_STDLIB_H # include #endif @@ -38508,7 +39667,9 @@ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ - + #ifdef HAVE_STDDEF_H + # include + #endif #ifdef HAVE_STDLIB_H # include #endif @@ -38579,7 +39740,9 @@ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ - + #ifdef HAVE_STDDEF_H + # include + #endif #ifdef HAVE_STDLIB_H # include #endif @@ -38650,7 +39813,9 @@ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ - + #ifdef HAVE_STDDEF_H + # include + #endif #ifdef HAVE_STDLIB_H # include #endif @@ -38719,7 +39884,9 @@ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ - + #ifdef HAVE_STDDEF_H + # include + #endif #ifdef HAVE_STDLIB_H # include #endif @@ -39145,6 +40312,12 @@ cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ + #ifdef HAVE_STDDEF_H + # include + #endif + #ifdef HAVE_STDLIB_H + # include + #endif #include int @@ -39214,6 +40387,12 @@ cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ + #ifdef HAVE_STDDEF_H + # include + #endif + #ifdef HAVE_STDLIB_H + # include + #endif #include int @@ -39283,6 +40462,12 @@ cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ + #ifdef HAVE_STDDEF_H + # include + #endif + #ifdef HAVE_STDLIB_H + # include + #endif #include #include @@ -39397,7 +40582,9 @@ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ - + #ifdef HAVE_STDDEF_H + # include + #endif #ifdef HAVE_STDLIB_H # include #endif @@ -39469,7 +40656,9 @@ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ - + #ifdef HAVE_STDDEF_H + # include + #endif #ifdef HAVE_STDLIB_H # include #endif @@ -39746,7 +40935,7 @@ cat >>confdefs.h <<\_ACEOF #define PR_USE_SODIUM 1 _ACEOF - ac_build_addl_libs="$ac_build_addl_libs -lsodium" + ac_build_addl_libs="$ac_build_addl_libs -lsodium" fi diff --git a/configure.in b/configure.in index 3647202f13..fbeacd7a43 100644 --- a/configure.in +++ b/configure.in @@ -820,7 +820,9 @@ AC_ARG_ENABLE(pcre, AC_MSG_CHECKING([for PCRE's pcre_free_study]) AC_TRY_LINK( - [ + [ #ifdef HAVE_STDDEF_H + # include + #endif #ifdef HAVE_STDLIB_H # include #endif @@ -967,6 +969,13 @@ AC_ARG_ENABLE(trace, [disable trace support (default=no)]) ]) +dnl Extended attribute (xattr) support +AC_ARG_ENABLE(xattr, + [AC_HELP_STRING( + [--disable-xattr], + [disable extended attribute support (default=no)]) + ]) + dnl Enable developer code pr_devel_cflags="-g -O0 -Wcast-align -Wchar-subscripts -Winline -Wstrict-prototypes -Wmissing-declarations -Wnested-externs -Wpointer-arith -Wshadow -Wundef" pr_devel_libs="" @@ -1021,8 +1030,12 @@ AC_ARG_ENABLE(devel, AC_MSG_CHECKING([for backtrace]) AC_TRY_LINK( [ - #include - #include + #ifdef HAVE_STDDEF_H + # include + #endif + #ifdef HAVE_STDLIB_H + # include + #endif #ifdef HAVE_EXECINFO_H # include #endif @@ -1047,8 +1060,12 @@ AC_ARG_ENABLE(devel, AC_MSG_CHECKING([for backtrace_symbols]) AC_TRY_LINK( [ - #include - #include + #ifdef HAVE_STDDEF_H + # include + #endif + #ifdef HAVE_STDLIB_H + # include + #endif #ifdef HAVE_EXECINFO_H # include #endif @@ -1756,8 +1773,15 @@ AC_CHECK_FUNC(gai_strerror, AC_MSG_CHECKING([for iconv]) AC_TRY_LINK( - [ #include - #include + [ #ifdef HAVE_STDDEF_H + # include + #endif + #ifdef HAVE_STDLIB_H + # include + #endif + #ifdef HAVE_SYS_TYPES_H + # include + #endif #ifdef HAVE_ICONV_H # include #endif @@ -1779,9 +1803,18 @@ AC_TRY_LINK( AC_MSG_CHECKING([for dirfd]) AC_TRY_LINK( - [ - #include - #include + [ #ifdef HAVE_STDDEF_H + # include + #endif + #ifdef HAVE_STDLIB_H + # include + #endif + #ifdef HAVE_STDIO_H + # include + #endif + #ifdef HAVE_SYS_TYPES_H + # include + #endif #if HAVE_DIRENT_H # include #endif @@ -1804,8 +1837,15 @@ AC_TRY_LINK( dnl getaddrinfo is a #define on Tru64 Unix. How annoying. AC_MSG_CHECKING([for getaddrinfo]) AC_TRY_LINK( - [ - #include + [ #ifdef HAVE_STDDEF_H + # include + #endif + #ifdef HAVE_STDLIB_H + # include + #endif + #ifdef HAVE_STDIO_H + # include + #endif #if HAVE_SYS_TYPES_H # include #endif @@ -2040,7 +2080,15 @@ AC_CACHE_CHECK( old_ldflags=$LDFLAGS LDFLAGS="-lsec $LDFLAGS" AC_TRY_LINK( - [ #include + [ #ifdef HAVE_STDDEF_H + # include + #endif + #ifdef HAVE_STDLIB_H + # include + #endif + #ifdef HAVE_SYS_TYPES_H + # include + #endif #ifdef HAVE_SYS_ACL_H # include #endif @@ -2231,6 +2279,272 @@ if test x"$enable_trace" != xno ; then AC_DEFINE(PR_USE_TRACE, 1, [Define for trace support]) fi +dnl Extended attribute checks +if test x"$enable_xattr" != xno ; then + # On Free/Net/OpenBSD, it's sys/extattr.h + AC_CHECK_HEADER(sys/extattr.h, + [AC_DEFINE(HAVE_SYS_EXTATTR_H, 1, [Define if sys/extattr.h is present.]) + AC_DEFINE(PR_USE_XATTR, 1, [Define if using xattr support.]) + + AC_MSG_CHECKING([for extattr_delete_link]) + AC_TRY_LINK( + [ #ifdef HAVE_STDDEF_H + # include + #endif + #ifdef HAVE_STDLIB_H + # include + #endif + #ifdef HAVE_SYS_TYPES_H + # include + #endif + #ifdef HAVE_SYS_EXTATTR_H + # include + #endif + ], + [ + int res; + int namespace = 0; + const char *path = NULL, name = NULL; + res = extattr_delete_link(path, namespace, name); + ], + [ + AC_MSG_RESULT(yes) + AC_DEFINE(HAVE_EXTATTR_DELETE_LINK, 1, [Define if you have extattr_delete_link]) + ], + [ + AC_MSG_RESULT(no) + ] + ) + + AC_MSG_CHECKING([for extattr_get_link]) + AC_TRY_LINK( + [ #ifdef HAVE_STDDEF_H + # include + #endif + #ifdef HAVE_STDLIB_H + # include + #endif + #ifdef HAVE_SYS_TYPES_H + # include + #endif + #ifdef HAVE_SYS_EXTATTR_H + # include + #endif + ], + [ + ssize_t res; + int namespace = 0; + const char *path = NULL, name = NULL; + void *val = NULL; + size_t sz = 0; + res = extattr_get_link(path, namespace, name, val, sz); + ], + [ + AC_MSG_RESULT(yes) + AC_DEFINE(HAVE_EXTATTR_GET_LINK, 1, [Define if you have extattr_get_link]) + ], + [ + AC_MSG_RESULT(no) + ] + ) + + AC_MSG_CHECKING([for extattr_list_link]) + AC_TRY_LINK( + [ #ifdef HAVE_STDDEF_H + # include + #endif + #ifdef HAVE_STDLIB_H + # include + #endif + #ifdef HAVE_SYS_TYPES_H + # include + #endif + #ifdef HAVE_SYS_EXTATTR_H + # include + #endif + ], + [ + ssize_t res; + int namespace = 0; + const char *path = NULL; + void *val = NULL; + size_t sz = 0; + res = extattr_list_link(path, namespace, val, sz); + ], + [ + AC_MSG_RESULT(yes) + AC_DEFINE(HAVE_EXTATTR_LIST_LINK, 1, [Define if you have extattr_list_link]) + ], + [ + AC_MSG_RESULT(no) + ] + ) + + AC_MSG_CHECKING([for extattr_set_link]) + AC_TRY_LINK( + [ #ifdef HAVE_STDDEF_H + # include + #endif + #ifdef HAVE_STDLIB_H + # include + #endif + #ifdef HAVE_SYS_TYPES_H + # include + #endif + #ifdef HAVE_SYS_EXTATTR_H + # include + #endif + ], + [ + int res; + int namespace = 0; + const char *path = NULL, name = NULL; + void *val = NULL; + size_t sz = 0; + res = extattr_set_link(path, namespace, name, val, sz); + ], + [ + AC_MSG_RESULT(yes) + AC_DEFINE(HAVE_EXTATTR_SET_LINK, 1, [Define if you have extattr_set_link]) + ], + [ + AC_MSG_RESULT(no) + ] + ) + ]) + + # On Linux/MacOSX, it's sys/xattr.h + AC_CHECK_HEADER(sys/xattr.h, + [AC_DEFINE(HAVE_SYS_XATTR_H, 1, [Define if sys/xattr.h is present.]) + AC_DEFINE(PR_USE_XATTR, 1, [Define if using xattr support.]) + AC_CHECK_HEADERS(attr/xattr.h) + + # Some platforms need libattr for extended attributes + AC_CHECK_LIB(attr, setxattr) + + AC_MSG_CHECKING([for lgetxattr]) + AC_TRY_LINK( + [ #ifdef HAVE_STDDEF_H + # include + #endif + #ifdef HAVE_STDLIB_H + # include + #endif + #ifdef HAVE_SYS_TYPES_H + # include + #endif + #ifdef HAVE_SYS_XATTR_H + # include + #endif + ], + [ + ssize_t res; + const char *path = NULL, *name = NULL; + void *val = NULL; + size_t sz = 0; + res = lgetxattr(path, name, val, sz); + ], + [ + AC_MSG_RESULT(yes) + AC_DEFINE(HAVE_LGETXATTR, 1, [Define if you have lgetxattr]) + ], + [ + AC_MSG_RESULT(no) + ] + ) + + AC_MSG_CHECKING([for llistxattr]) + AC_TRY_LINK( + [ #ifdef HAVE_STDDEF_H + # include + #endif + #ifdef HAVE_STDLIB_H + # include + #endif + #ifdef HAVE_SYS_TYPES_H + # include + #endif + #ifdef HAVE_SYS_XATTR_H + # include + #endif + ], + [ + ssize_t res; + const char *path = NULL; + char *names = NUL; + size_t namessz = 0; + res = llistxattr(path, names, namessz); + ], + [ + AC_MSG_RESULT(yes) + AC_DEFINE(HAVE_LLISTXATTR, 1, [Define if you have llistxattr]) + ], + [ + AC_MSG_RESULT(no) + ] + ) + + AC_MSG_CHECKING([for lremovexattr]) + AC_TRY_LINK( + [ #ifdef HAVE_STDDEF_H + # include + #endif + #ifdef HAVE_STDLIB_H + # include + #endif + #ifdef HAVE_SYS_TYPES_H + # include + #endif + #ifdef HAVE_SYS_XATTR_H + # include + #endif + ], + [ + ssize_t res; + const char *path = NULL, *name = NULL; + res = lremovexattr(path, name); + ], + [ + AC_MSG_RESULT(yes) + AC_DEFINE(HAVE_LREMOVEXATTR, 1, [Define if you have lremovexattr]) + ], + [ + AC_MSG_RESULT(no) + ] + ) + + AC_MSG_CHECKING([for lsetxattr]) + AC_TRY_LINK( + [ #ifdef HAVE_STDDEF_H + # include + #endif + #ifdef HAVE_STDLIB_H + # include + #endif + #ifdef HAVE_SYS_TYPES_H + # include + #endif + #ifdef HAVE_SYS_XATTR_H + # include + #endif + ], + [ + int res, flags = 0; + const char *path = NULL, *name = NULL; + const void *val = NULL; + res = lsetxattr(path, name, val, flags); + ], + [ + AC_MSG_RESULT(yes) + AC_DEFINE(HAVE_LSETXATTR, 1, [Define if you have lsetxattr]) + ], + [ + AC_MSG_RESULT(no) + ] + ) + ]) +fi + dnl Custom-rolled macro for checking return type of setgrent(3) PR_FUNC_SETGRENT_VOID @@ -2548,7 +2862,9 @@ if test x"$pr_use_mysql" = xyes; then AC_MSG_CHECKING([for MySQL's make_scrambled_password]) AC_TRY_LINK( - [ + [ #ifdef HAVE_STDDEF_H + # include + #endif #ifdef HAVE_STDLIB_H # include #endif @@ -2573,7 +2889,9 @@ if test x"$pr_use_mysql" = xyes; then AC_MSG_CHECKING([for MySQL's make_scrambled_password_323]) AC_TRY_LINK( - [ + [ #ifdef HAVE_STDDEF_H + # include + #endif #ifdef HAVE_STDLIB_H # include #endif @@ -2600,7 +2918,9 @@ if test x"$pr_use_mysql" = xyes; then # my_make_scrambled_password. AC_MSG_CHECKING([for MySQL's my_make_scrambled_password]) AC_TRY_LINK( - [ + [ #ifdef HAVE_STDDEF_H + # include + #endif #ifdef HAVE_STDLIB_H # include #endif @@ -2626,7 +2946,9 @@ if test x"$pr_use_mysql" = xyes; then AC_MSG_CHECKING([for MySQL's my_make_scrambled_password_323]) AC_TRY_LINK( - [ + [ #ifdef HAVE_STDDEF_H + # include + #endif #ifdef HAVE_STDLIB_H # include #endif @@ -2652,7 +2974,9 @@ if test x"$pr_use_mysql" = xyes; then AC_MSG_CHECKING([for MySQL's mysql_ssl_set]) AC_TRY_LINK( - [ + [ #ifdef HAVE_STDDEF_H + # include + #endif #ifdef HAVE_STDLIB_H # include #endif @@ -2676,7 +3000,9 @@ if test x"$pr_use_mysql" = xyes; then AC_MSG_CHECKING([for MySQL's mysql_get_ssl_cipher]) AC_TRY_LINK( - [ + [ #ifdef HAVE_STDDEF_H + # include + #endif #ifdef HAVE_STDLIB_H # include #endif @@ -2844,6 +3170,12 @@ if test x"$pr_use_openssl" = xyes; then AC_TRY_LINK( [ + #ifdef HAVE_STDDEF_H + # include + #endif + #ifdef HAVE_STDLIB_H + # include + #endif #include ], [ @@ -2869,6 +3201,12 @@ if test x"$pr_use_openssl" = xyes; then AC_TRY_LINK( [ + #ifdef HAVE_STDDEF_H + # include + #endif + #ifdef HAVE_STDLIB_H + # include + #endif #include ], [ @@ -2894,6 +3232,12 @@ if test x"$pr_use_openssl" = xyes; then AC_TRY_LINK( [ + #ifdef HAVE_STDDEF_H + # include + #endif + #ifdef HAVE_STDLIB_H + # include + #endif #include #include ], @@ -2964,7 +3308,9 @@ if test x"$pr_use_postgres" = xyes; then AC_MSG_CHECKING([for Postgres's PQescapeStringConn]) AC_TRY_LINK( - [ + [ #ifdef HAVE_STDDEF_H + # include + #endif #ifdef HAVE_STDLIB_H # include #endif @@ -2991,7 +3337,9 @@ if test x"$pr_use_postgres" = xyes; then AC_MSG_CHECKING([for Postgres's PQgetssl]) AC_TRY_LINK( - [ + [ #ifdef HAVE_STDDEF_H + # include + #endif #ifdef HAVE_STDLIB_H # include #endif @@ -3050,8 +3398,8 @@ fi if test x"$pr_use_sodium" = xyes; then AC_CHECK_HEADER(sodium.h, [AC_DEFINE(HAVE_SODIUM_H, 1, [Define if sodium.h is present.]) - AC_DEFINE(PR_USE_SODIUM, 1, [Define if using Sodium support.]) - ac_build_addl_libs="$ac_build_addl_libs -lsodium" + AC_DEFINE(PR_USE_SODIUM, 1, [Define if using Sodium support.]) + ac_build_addl_libs="$ac_build_addl_libs -lsodium" ]) fi diff --git a/contrib/mod_sftp/auth-hostbased.c b/contrib/mod_sftp/auth-hostbased.c index e72dd72e56..c06e39994c 100644 --- a/contrib/mod_sftp/auth-hostbased.c +++ b/contrib/mod_sftp/auth-hostbased.c @@ -41,12 +41,13 @@ int sftp_auth_hostbased(struct ssh2_packet *pkt, cmd_rec *pass_cmd, unsigned char **buf, uint32_t *buflen, int *send_userauth_fail) { struct passwd *pw; char *hostkey_algo, *host_fqdn, *host_user, *host_user_utf8; - const char *fp = NULL; + const char *fp = NULL, *fp_algo = NULL; unsigned char *hostkey_data, *signature_data; unsigned char *buf2, *ptr2; const unsigned char *id; uint32_t buflen2, bufsz2, hostkey_datalen, id_len, signature_len; enum sftp_key_type_e pubkey_type; + int fp_algo_id; if (pr_cmd_dispatch_phase(pass_cmd, PRE_CMD, 0) < 0) { (void) pr_log_writefile(sftp_logfd, MOD_SFTP_VERSION, @@ -136,9 +137,6 @@ int sftp_auth_hostbased(struct ssh2_packet *pkt, cmd_rec *pass_cmd, #ifdef OPENSSL_FIPS if (FIPS_mode()) { - int fp_algo_id; - const char *fp_algo; - # if defined(HAVE_SHA256_OPENSSL) fp_algo_id = SFTP_KEYS_FP_DIGEST_SHA256; fp_algo = "SHA256"; @@ -161,9 +159,6 @@ int sftp_auth_hostbased(struct ssh2_packet *pkt, cmd_rec *pass_cmd, } else { #endif /* OPENSSL_FIPS */ - int fp_algo_id; - const char *fp_algo; - #if defined(HAVE_SHA256_OPENSSL) fp_algo_id = SFTP_KEYS_FP_DIGEST_SHA256; fp_algo = "SHA256"; diff --git a/contrib/mod_sftp/auth-publickey.c b/contrib/mod_sftp/auth-publickey.c index b08b76c869..d4e5e80fe7 100644 --- a/contrib/mod_sftp/auth-publickey.c +++ b/contrib/mod_sftp/auth-publickey.c @@ -83,7 +83,7 @@ int sftp_auth_publickey(struct ssh2_packet *pkt, cmd_rec *pass_cmd, const char *orig_user, const char *user, const char *service, unsigned char **buf, uint32_t *buflen, int *send_userauth_fail) { register unsigned int i; - int have_signature, res; + int fp_algo_id = 0, have_signature, res; enum sftp_key_type_e pubkey_type; unsigned char *pubkey_data; char *pubkey_algo = NULL; @@ -187,8 +187,6 @@ int sftp_auth_publickey(struct ssh2_packet *pkt, cmd_rec *pass_cmd, #ifdef OPENSSL_FIPS if (FIPS_mode()) { - int fp_algo_id; - # if defined(HAVE_SHA256_OPENSSL) fp_algo_id = SFTP_KEYS_FP_DIGEST_SHA256; fp_algo = "SHA256"; @@ -212,8 +210,6 @@ int sftp_auth_publickey(struct ssh2_packet *pkt, cmd_rec *pass_cmd, } else { #endif /* OPENSSL_FIPS */ - int fp_algo_id; - #if defined(HAVE_SHA256_OPENSSL) fp_algo_id = SFTP_KEYS_FP_DIGEST_SHA256; fp_algo = "SHA256"; diff --git a/contrib/mod_sftp/fxp.c b/contrib/mod_sftp/fxp.c index 4d044643e5..15b7d00c73 100644 --- a/contrib/mod_sftp/fxp.c +++ b/contrib/mod_sftp/fxp.c @@ -181,6 +181,10 @@ #define SSH2_FXE_STATVFS_ST_RDONLY 0x1 #define SSH2_FXE_STATVFS_ST_NOSUID 0x2 +/* xattr@proftpd.org extension flags */ +#define SSH2_FXE_XATTR_CREATE 0x1 +#define SSH2_FXE_XATTR_REPLACE 0x2 + extern pr_response_t *resp_list, *resp_err_list; struct fxp_dirent { @@ -229,6 +233,20 @@ struct fxp_packet { unsigned int state; }; +struct fxp_buffer { + /* Pointer to the start of the buffer */ + unsigned char *ptr; + + /* Total size of the buffer */ + uint32_t bufsz; + + /* Current pointer */ + unsigned char *buf; + + /* Length of buffer remaining */ + uint32_t buflen; +}; + #define FXP_PACKET_HAVE_PACKET_LEN 0x0001 #define FXP_PACKET_HAVE_REQUEST_TYPE 0x0002 #define FXP_PACKET_HAVE_REQUEST_ID 0x0004 @@ -245,11 +263,26 @@ static size_t fxp_packet_data_allocsz = 0; #define FXP_PACKET_DATA_DEFAULT_SZ (1024 * 16) #define FXP_RESPONSE_DATA_DEFAULT_SZ 512 +#ifdef PR_USE_XATTR +/* Allocate larger buffers for extended attributes */ +# define FXP_RESPONSE_NAME_DEFAULT_SZ (1024 * 4) +#endif /* PR_USE_XATTR */ + +#ifndef FXP_RESPONSE_NAME_DEFAULT_SZ +# define FXP_RESPONSE_NAME_DEFAULT_SZ FXP_RESPONSE_DATA_DEFAULT_SZ +#endif + #define FXP_MAX_PACKET_LEN (1024 * 512) -#define FXP_MAX_EXTENDED_ATTRIBUTES 100 + +/* Maximum number of SFTP extended attributes we accept at one time. */ +#ifndef FXP_MAX_EXTENDED_ATTRIBUTES +# define FXP_MAX_EXTENDED_ATTRIBUTES 100 +#endif /* Maximum length of SFTP extended attribute name OR value. */ -#define FXP_MAX_EXTENDED_ATTR_LEN 1024 +#ifndef FXP_MAX_EXTENDED_ATTR_LEN +# define FXP_MAX_EXTENDED_ATTR_LEN 1024 +#endif struct fxp_extpair { char *ext_name; @@ -292,79 +325,6 @@ static struct fxp_handle *fxp_handle_get(const char *); static struct fxp_packet *fxp_packet_create(pool *, uint32_t); static int fxp_packet_write(struct fxp_packet *); -/* XXX These two namelist-related functions are the same as the ones - * in kex.c; the code should be refactored out into a misc.c or utils.c - * or somesuch. - */ - -static array_header *fxp_parse_namelist(pool *p, const char *names) { - char *ptr; - array_header *list; - size_t names_len; - - list = make_array(p, 0, sizeof(const char *)); - names_len = strlen(names); - - ptr = memchr(names, ',', names_len); - while (ptr) { - char *elt; - size_t elt_len; - - pr_signals_handle(); - - elt_len = ptr - names; - - elt = palloc(p, elt_len + 1); - memcpy(elt, names, elt_len); - elt[elt_len] = '\0'; - - *((const char **) push_array(list)) = elt; - names = ++ptr; - names_len -= elt_len; - - ptr = memchr(names, ',', names_len); - } - *((const char **) push_array(list)) = pstrdup(p, names); - - return list; -} - -static const char *fxp_get_shared_name(pool *p, const char *c2s_names, - const char *s2c_names) { - register unsigned int i; - const char *name = NULL, **client_names, **server_names; - pool *tmp_pool; - array_header *client_list, *server_list; - - tmp_pool = make_sub_pool(p); - pr_pool_tag(tmp_pool, "SFTP shared name pool"); - - client_list = fxp_parse_namelist(tmp_pool, c2s_names); - client_names = (const char **) client_list->elts; - - server_list = fxp_parse_namelist(tmp_pool, s2c_names); - server_names = (const char **) server_list->elts; - - for (i = 0; i < client_list->nelts; i++) { - register unsigned int j; - - if (name) - break; - - for (j = 0; j < server_list->nelts; j++) { - if (strcmp(client_names[i], server_names[j]) == 0) { - name = client_names[i]; - break; - } - } - } - - name = pstrdup(p, name); - destroy_pool(tmp_pool); - - return name; -} - static struct fxp_session *fxp_get_session(uint32_t channel_id) { struct fxp_session *sess; @@ -502,6 +462,12 @@ static uint32_t fxp_errno2status(int xerrno, const char **reason) { case ENOENT: #ifdef ENXIO case ENXIO: +#endif +#if defined(ENODATA) + case ENODATA: +#endif +#if defined(ENOATTR) && defined(ENODATA) && ENOATTR != ENODATA + case ENOATTR: #endif status_code = SSH2_FX_NO_SUCH_FILE; if (reason) { @@ -525,6 +491,9 @@ static uint32_t fxp_errno2status(int xerrno, const char **reason) { break; case ENOSYS: +#ifdef ENOTSUP + case ENOTSUP: +#endif status_code = SSH2_FX_OP_UNSUPPORTED; if (reason) { *reason = fxp_strerror(status_code); @@ -533,6 +502,12 @@ static uint32_t fxp_errno2status(int xerrno, const char **reason) { case EFAULT: case EINVAL: +#ifdef E2BIG + case E2BIG: +#endif +#ifdef ERANGE + case ERANGE: +#endif if (reason) { *reason = fxp_strerror(SSH2_FX_INVALID_PARAMETER); } @@ -1327,8 +1302,8 @@ static void fxp_msg_write_extpair(unsigned char **buf, uint32_t *buflen, } static int fxp_attrs_set(pr_fh_t *fh, const char *path, struct stat *attrs, - uint32_t attr_flags, unsigned char **buf, uint32_t *buflen, - struct fxp_packet *fxp) { + uint32_t attr_flags, array_header *xattrs, unsigned char **buf, + uint32_t *buflen, struct fxp_packet *fxp) { struct stat st; int res; @@ -1581,6 +1556,66 @@ static int fxp_attrs_set(pr_fh_t *fh, const char *path, struct stat *attrs, } if (fxp_session->client_version > 3) { + /* Note: we handle the xattrs FIRST, before the timestamps, so that + * setting the xattrs does not change the expected timestamps, thus + * preserving the principle of least surprise. + */ + if (attr_flags & SSH2_FX_ATTR_EXTENDED) { +#ifdef PR_USE_XATTR + if (xattrs != NULL && + xattrs->nelts > 0) { + register unsigned int i; + struct fxp_extpair **ext_pairs; + + ext_pairs = xattrs->elts; + for (i = 0; i < xattrs->nelts; i++) { + struct fxp_extpair *xattr; + const char *xattr_name; + void *xattr_val; + size_t xattr_valsz; + + xattr = ext_pairs[i]; + xattr_name = xattr->ext_name; + xattr_val = xattr->ext_data; + xattr_valsz = (size_t) xattr->ext_datalen; + + if (fh != NULL) { + res = pr_fsio_fsetxattr(fxp->pool, fh, xattr_name, xattr_val, + xattr_valsz, 0); + + } else { + res = pr_fsio_lsetxattr(fxp->pool, path, xattr_name, xattr_val, + xattr_valsz, 0); + } + + if (res < 0) { + uint32_t status_code; + const char *reason; + int xerrno = errno; + + (void) pr_log_writefile(sftp_logfd, MOD_SFTP_VERSION, + "error setting xattr '%s' (%lu bytes) on '%s': %s", xattr_name, + (unsigned long) xattr_valsz, path, strerror(xerrno)); + + status_code = fxp_errno2status(xerrno, &reason); + + pr_trace_msg(trace_channel, 8, "sending response: STATUS %lu '%s' " + "('%s' [%d])", (unsigned long) status_code, reason, + strerror(xerrno), xerrno); + + fxp_status_write(fxp->pool, buf, buflen, fxp->request_id, + status_code, reason, NULL); + + errno = xerrno; + return -1; + } + } + } +#else + (void) xattrs; +#endif /* PR_USE_XATTR */ + } + if (attr_flags & SSH2_FX_ATTR_ACCESSTIME) { if (st.st_atime != attrs->st_atime) { struct timeval tvs[2]; @@ -1740,6 +1775,9 @@ static char *fxp_strattrs(pool *p, struct stat *st, uint32_t *attr_flags) { if (fxp_session->client_version >= 6) { flags |= SSH2_FX_ATTR_LINK_COUNT; +#ifdef PR_USE_XATTR + flags |= SSH2_FX_ATTR_EXTENDED; +#endif /* PR_USE_XATTR */ } } } @@ -1924,8 +1962,48 @@ static char *fxp_stroflags(pool *p, int flags) { return str; } +static array_header *fxp_xattrs_read(pool *p, unsigned char **buf, + uint32_t *buflen) { + register unsigned int i; + uint32_t extpair_count; + array_header *xattrs = NULL; + + extpair_count = sftp_msg_read_int(p, buf, buflen); + pr_trace_msg(trace_channel, 15, + "protocol version %lu: read EXTENDED attribute: %lu extensions", + (unsigned long) fxp_session->client_version, + (unsigned long) extpair_count); + + if (extpair_count > FXP_MAX_EXTENDED_ATTRIBUTES) { + (void) pr_log_writefile(sftp_logfd, MOD_SFTP_VERSION, + "received too many EXTENDED attributes (%lu > max %lu), " + "truncating to max", (unsigned long) extpair_count, + (unsigned long) FXP_MAX_EXTENDED_ATTRIBUTES); + extpair_count = FXP_MAX_EXTENDED_ATTRIBUTES; + } + + xattrs = make_array(p, 1, sizeof(struct fxp_extpair *)); + + for (i = 0; i < extpair_count; i++) { + struct fxp_extpair *ext; + + ext = fxp_msg_read_extpair(p, buf, buflen); + if (ext != NULL) { + pr_trace_msg(trace_channel, 15, + "protocol version %lu: read EXTENDED attribute: " + "extension '%s' (%lu bytes of data)", + (unsigned long) fxp_session->client_version, ext->ext_name, + (unsigned long) ext->ext_datalen); + + *((struct fxp_extpair **) push_array(xattrs)) = ext; + } + } + + return xattrs; +} + static struct stat *fxp_attrs_read(struct fxp_packet *fxp, unsigned char **buf, - uint32_t *buflen, uint32_t *flags) { + uint32_t *buflen, uint32_t *flags, array_header **xattrs) { struct stat *st; st = pcalloc(fxp->pool, sizeof(struct stat)); @@ -1951,8 +2029,6 @@ static struct stat *fxp_attrs_read(struct fxp_packet *fxp, unsigned char **buf, st->st_mtime = sftp_msg_read_int(fxp->pool, buf, buflen); } - /* XXX Vendor-specific extensions */ - } else { char file_type; @@ -2260,41 +2336,16 @@ static struct stat *fxp_attrs_read(struct fxp_packet *fxp, unsigned char **buf, (unsigned long) fxp_session->client_version, untranslated ? untranslated : "(nil)"); } + } - /* Vendor-specific extensions */ - if (*flags & SSH2_FX_ATTR_EXTENDED) { - /* Read (and ignore) the EXTENDED attribute. */ - register unsigned int i; - uint32_t extpair_count; - - extpair_count = sftp_msg_read_int(fxp->pool, buf, buflen); - pr_trace_msg(trace_channel, 15, - "protocol version %lu: read EXTENDED attribute: %lu extensions", - (unsigned long) fxp_session->client_version, - (unsigned long) extpair_count); - - if (extpair_count > FXP_MAX_EXTENDED_ATTRIBUTES) { - (void) pr_log_writefile(sftp_logfd, MOD_SFTP_VERSION, - "received too many EXTENDED attributes (%lu > max %lu), " - "truncating to max", (unsigned long) extpair_count, - (unsigned long) FXP_MAX_EXTENDED_ATTRIBUTES); - extpair_count = FXP_MAX_EXTENDED_ATTRIBUTES; - } - - for (i = 0; i < extpair_count; i++) { - struct fxp_extpair *ext; + if (*flags & SSH2_FX_ATTR_EXTENDED) { + array_header *ext_attrs; - ext = fxp_msg_read_extpair(fxp->pool, buf, buflen); - if (ext != NULL) { - pr_trace_msg(trace_channel, 15, - "protocol version %lu: read EXTENDED attribute: " - "extension '%s' (%lu bytes of data)", - (unsigned long) fxp_session->client_version, ext->ext_name, - (unsigned long) ext->ext_datalen); - } - } + /* Read the EXTENDED attribute. */ + ext_attrs = fxp_xattrs_read(fxp->pool, buf, buflen); + if (xattrs != NULL) { + *xattrs = ext_attrs; } - } return st; @@ -2354,23 +2405,134 @@ static char fxp_get_file_type(mode_t mode) { return SSH2_FX_ATTR_FTYPE_UNKNOWN; } -static uint32_t fxp_attrs_write(pool *p, unsigned char **buf, uint32_t *buflen, - struct stat *st, const char *user_owner, const char *group_owner) { - uint32_t flags, len = 0; +static uint32_t fxp_xattrs_write(pool *p, struct fxp_buffer *fxb, + const char *path) { + uint32_t len = 0; + +#ifdef PR_USE_XATTR + int res; + array_header *names = NULL; + + res = pr_fsio_llistxattr(p, path, &names); + if (res > 0) { + register unsigned int i; + pool *sub_pool; + uint32_t xattrsz = 0; + array_header *vals; + + sub_pool = make_sub_pool(p); + pr_pool_tag(sub_pool, "listxattr pool"); + + vals = make_array(sub_pool, names->nelts, sizeof(pr_buffer_t *)); + xattrsz = sizeof(uint32_t); + + for (i = 0; i < names->nelts; i++) { + const char *name; + pr_buffer_t *val; + ssize_t valsz; + + name = ((const char **) names->elts)[i]; + xattrsz += (sizeof(uint32_t) + strlen(name)); + + val = pcalloc(sub_pool, sizeof(pr_buffer_t)); + + valsz = pr_fsio_lgetxattr(p, path, name, NULL, 0); + if (valsz > 0) { + xattrsz += (sizeof(uint32_t) + valsz); + + val->buflen = valsz; + val->buf = palloc(sub_pool, valsz); + + valsz = pr_fsio_lgetxattr(p, path, name, val->buf, valsz); + if (valsz > 0) { + *((pr_buffer_t **) push_array(vals)) = val; + } + } else { + /* Push the empty buffer into the list, so that the vals list + * lines up with the names list. + */ + *((pr_buffer_t **) push_array(vals)) = val; + } + } + + if (fxb->buflen < xattrsz) { + unsigned char *ptr; + uint32_t bufsz, resp_len; + + resp_len = fxb->bufsz - fxb->buflen; + + /* Allocate a buffer large enough for the xattrs */ + pr_trace_msg(trace_channel, 3, + "allocating larger response buffer (have %lu bytes, need %lu bytes)", + (unsigned long) fxb->bufsz, (unsigned long) fxb->bufsz + xattrsz); + + bufsz = fxb->bufsz + xattrsz; + ptr = palloc(p, bufsz); + + /* Copy over our existing response data into the new buffer. */ + memcpy(ptr, fxb->ptr, resp_len); + fxb->ptr = ptr; + fxb->bufsz = bufsz; + fxb->buf = ptr + resp_len; + fxb->buflen = bufsz - resp_len; + } + + len += sftp_msg_write_int(&(fxb->buf), &(fxb->buflen), names->nelts); + for (i = 0; i < names->nelts; i++) { + const char *name; + pr_buffer_t *val; + + name = ((const char **) names->elts)[i]; + val = ((pr_buffer_t **) vals->elts)[i]; + + len += sftp_msg_write_string(&(fxb->buf), &(fxb->buflen), name); + len += sftp_msg_write_data(&(fxb->buf), &(fxb->buflen), + (const unsigned char *) val->buf, (size_t) val->buflen, TRUE); + } + + destroy_pool(sub_pool); + + } else { + /* Have to write an extended count of zero. */ + len += sftp_msg_write_int(&(fxb->buf), &(fxb->buflen), 0); + } +#endif /* PR_USE_XATTR */ + + return len; +} + +static uint32_t fxp_attrs_write(pool *p, struct fxp_buffer *fxb, + const char *path, struct stat *st, uint32_t flags, + const char *user_owner, const char *group_owner) { + uint32_t len = 0; mode_t perms; if (fxp_session->client_version <= 3) { - flags = SSH2_FX_ATTR_SIZE|SSH2_FX_ATTR_UIDGID|SSH2_FX_ATTR_PERMISSIONS| - SSH2_FX_ATTR_ACMODTIME; perms = st->st_mode; - len += sftp_msg_write_int(buf, buflen, flags); - len += sftp_msg_write_long(buf, buflen, st->st_size); - len += sftp_msg_write_int(buf, buflen, st->st_uid); - len += sftp_msg_write_int(buf, buflen, st->st_gid); - len += sftp_msg_write_int(buf, buflen, perms); - len += sftp_msg_write_int(buf, buflen, st->st_atime); - len += sftp_msg_write_int(buf, buflen, st->st_mtime); + len += sftp_msg_write_int(&(fxb->buf), &(fxb->buflen), flags); + + if (flags & SSH2_FX_ATTR_SIZE) { + len += sftp_msg_write_long(&(fxb->buf), &(fxb->buflen), st->st_size); + } + + if (flags & SSH2_FX_ATTR_UIDGID) { + len += sftp_msg_write_int(&(fxb->buf), &(fxb->buflen), st->st_uid); + len += sftp_msg_write_int(&(fxb->buf), &(fxb->buflen), st->st_gid); + } + + if (flags & SSH2_FX_ATTR_PERMISSIONS) { + len += sftp_msg_write_int(&(fxb->buf), &(fxb->buflen), perms); + } + + if (flags & SSH2_FX_ATTR_ACMODTIME) { + len += sftp_msg_write_int(&(fxb->buf), &(fxb->buflen), st->st_atime); + len += sftp_msg_write_int(&(fxb->buf), &(fxb->buflen), st->st_mtime); + } + + if (flags & SSH2_FX_ATTR_EXTENDED) { + len += fxp_xattrs_write(p, fxb, path); + } } else { char file_type; @@ -2382,41 +2544,54 @@ static uint32_t fxp_attrs_write(pool *p, unsigned char **buf, uint32_t *buflen, */ perms &= ~S_IFMT; - flags = SSH2_FX_ATTR_SIZE|SSH2_FX_ATTR_PERMISSIONS|SSH2_FX_ATTR_ACCESSTIME| - SSH2_FX_ATTR_MODIFYTIME|SSH2_FX_ATTR_OWNERGROUP; + file_type = fxp_get_file_type(st->st_mode); + + len += sftp_msg_write_int(&(fxb->buf), &(fxb->buflen), flags); + len += sftp_msg_write_byte(&(fxb->buf), &(fxb->buflen), file_type); - if (fxp_session->client_version >= 6) { - flags |= SSH2_FX_ATTR_LINK_COUNT; + if (flags & SSH2_FX_ATTR_SIZE) { + len += sftp_msg_write_long(&(fxb->buf), &(fxb->buflen), st->st_size); } - file_type = fxp_get_file_type(st->st_mode); + if (flags & SSH2_FX_ATTR_OWNERGROUP) { + const char *user_name, *group_name; - len += sftp_msg_write_int(buf, buflen, flags); - len += sftp_msg_write_byte(buf, buflen, file_type); - len += sftp_msg_write_long(buf, buflen, st->st_size); + if (user_owner == NULL) { + user_name = pr_auth_uid2name(p, st->st_uid); + + } else { + user_name = user_owner; + } - if (user_owner == NULL) { - len += sftp_msg_write_string(buf, buflen, - pr_auth_uid2name(p, st->st_uid)); + if (group_owner == NULL) { + group_name = pr_auth_gid2name(p, st->st_gid); - } else { - len += sftp_msg_write_string(buf, buflen, user_owner); + } else { + group_name = group_owner; + } + + len += sftp_msg_write_string(&(fxb->buf), &(fxb->buflen), user_name); + len += sftp_msg_write_string(&(fxb->buf), &(fxb->buflen), group_name); } - if (group_owner == NULL) { - len += sftp_msg_write_string(buf, buflen, - pr_auth_gid2name(p, st->st_gid)); + if (flags & SSH2_FX_ATTR_PERMISSIONS) { + len += sftp_msg_write_int(&(fxb->buf), &(fxb->buflen), perms); + } - } else { - len += sftp_msg_write_string(buf, buflen, group_owner); + if (flags & SSH2_FX_ATTR_ACCESSTIME) { + len += sftp_msg_write_long(&(fxb->buf), &(fxb->buflen), st->st_atime); } - len += sftp_msg_write_int(buf, buflen, perms); - len += sftp_msg_write_long(buf, buflen, st->st_atime); - len += sftp_msg_write_long(buf, buflen, st->st_mtime); + if (flags & SSH2_FX_ATTR_MODIFYTIME) { + len += sftp_msg_write_long(&(fxb->buf), &(fxb->buflen), st->st_mtime); + } if (flags & SSH2_FX_ATTR_LINK_COUNT) { - len += sftp_msg_write_int(buf, buflen, st->st_nlink); + len += sftp_msg_write_int(&(fxb->buf), &(fxb->buflen), st->st_nlink); + } + + if (flags & SSH2_FX_ATTR_EXTENDED) { + len += fxp_xattrs_write(p, fxb, path); } } @@ -2488,7 +2663,7 @@ static char *fxp_strmode(pool *p, mode_t mode) { static char *fxp_get_path_listing(pool *p, const char *path, struct stat *st, const char *user_owner, const char *group_owner) { const char *user, *group; - char listing[256], *mode_str, time_str[64]; + char listing[1024], *mode_str, time_str[64]; struct tm *t; int user_len, group_len; size_t time_strlen; @@ -2598,33 +2773,31 @@ static struct fxp_dirent *fxp_get_dirent(pool *p, cmd_rec *cmd, return fxd; } -static uint32_t fxp_name_write(pool *p, unsigned char **buf, uint32_t *buflen, - const char *path, struct stat *st, const char *user_owner, - const char *group_owner) { +static uint32_t fxp_name_write(pool *p, struct fxp_buffer *fxb, + const char *path, struct stat *st, uint32_t attr_flags, + const char *user_owner, const char *group_owner) { uint32_t len = 0; + const char *encoded_path; + encoded_path = path; if (fxp_session->client_version >= fxp_utf8_protocol_version) { - len += sftp_msg_write_string(buf, buflen, sftp_utf8_encode_str(p, path)); - - } else { - len += sftp_msg_write_string(buf, buflen, path); + encoded_path = sftp_utf8_encode_str(p, encoded_path); } + len += sftp_msg_write_string(&(fxb->buf), &(fxb->buflen), encoded_path); + if (fxp_session->client_version <= 3) { char *path_desc; path_desc = fxp_get_path_listing(p, path, st, user_owner, group_owner); - if (fxp_session->client_version >= fxp_utf8_protocol_version) { - len += sftp_msg_write_string(buf, buflen, - sftp_utf8_encode_str(p, path_desc)); - - } else { - len += sftp_msg_write_string(buf, buflen, path_desc); + path_desc = sftp_utf8_encode_str(p, path_desc); } + + len += sftp_msg_write_string(&(fxb->buf), &(fxb->buflen), path_desc); } - len += fxp_attrs_write(p, buf, buflen, st, user_owner, group_owner); + len += fxp_attrs_write(p, fxb, path, st, attr_flags, user_owner, group_owner); return len; } @@ -3483,6 +3656,18 @@ static void fxp_version_add_openssh_exts(pool *p, unsigned char **buf, ext.ext_data); fxp_msg_write_extpair(buf, buflen, &ext); } + + if (fxp_ext_flags & SFTP_FXP_EXT_XATTR) { + struct fxp_extpair ext; + + ext.ext_name = "xattr@proftpd.org"; + ext.ext_data = (unsigned char *) "1"; + ext.ext_datalen = 1; + + pr_trace_msg(trace_channel, 11, "+ SFTP extension: %s = '%s'", ext.ext_name, + ext.ext_data); + fxp_msg_write_extpair(buf, buflen, &ext); + } } static void fxp_version_add_newline_ext(pool *p, unsigned char **buf, @@ -3617,6 +3802,9 @@ static void fxp_version_add_supported2_ext(pool *p, unsigned char **buf, file_mask = SSH2_FX_ATTR_SIZE|SSH2_FX_ATTR_PERMISSIONS| SSH2_FX_ATTR_ACCESSTIME|SSH2_FX_ATTR_MODIFYTIME|SSH2_FX_ATTR_OWNERGROUP; +#ifdef PR_USE_XATTR + file_mask |= SSH2_FX_ATTR_EXTENDED; +#endif /* PR_USE_XATTR */ bits_mask = 0; @@ -3915,7 +4103,8 @@ static int fxp_handle_ext_check_file(struct fxp_packet *fxp, char *digest_list, NULL); #endif - digest_name = fxp_get_shared_name(fxp->pool, digest_list, supported_digests); + digest_name = sftp_misc_namelist_shared(fxp->pool, digest_list, + supported_digests); if (digest_name == NULL) { xerrno = EINVAL; @@ -5323,26 +5512,656 @@ static int fxp_handle_ext_statvfs(struct fxp_packet *fxp, const char *path) { #endif sftp_msg_write_long(&buf, &buflen, fs_id); - /* These flags and values are defined by OpenSSH's PROTOCOL document. - * - * Other platforms support more fs.f_flag values than just ST_RDONLY - * and ST_NOSUID, but those are the only two flags handled by OpenSSH; - * thus we cannot simply send fs.f_flag directly to the client as is. - */ -#ifdef ST_RDONLY - if (fs.f_flag & ST_RDONLY) { - fs_flags |= SSH2_FXE_STATVFS_ST_RDONLY; + /* These flags and values are defined by OpenSSH's PROTOCOL document. + * + * Other platforms support more fs.f_flag values than just ST_RDONLY + * and ST_NOSUID, but those are the only two flags handled by OpenSSH; + * thus we cannot simply send fs.f_flag directly to the client as is. + */ +#ifdef ST_RDONLY + if (fs.f_flag & ST_RDONLY) { + fs_flags |= SSH2_FXE_STATVFS_ST_RDONLY; + } +#endif + +#ifdef ST_NOSUID + if (fs.f_flag & ST_NOSUID) { + fs_flags |= SSH2_FXE_STATVFS_ST_NOSUID; + } +#endif + + sftp_msg_write_long(&buf, &buflen, fs_flags); + sftp_msg_write_long(&buf, &buflen, fs.f_namemax); + + resp = fxp_packet_create(fxp->pool, fxp->channel_id); + resp->payload = ptr; + resp->payload_sz = (bufsz - buflen); + + return fxp_packet_write(resp); +} +#endif /* !HAVE_SYS_STATVFS_H */ + +#ifdef PR_USE_XATTR +static int fxp_handle_ext_getxattr(struct fxp_packet *fxp, const char *path, + const char *name, uint32_t valsz) { + ssize_t res; + void *val; + unsigned char *buf, *ptr; + uint32_t buflen, bufsz, status_code; + const char *reason; + struct fxp_packet *resp; + + val = pcalloc(fxp->pool, (size_t) valsz+1); + + buflen = bufsz = FXP_RESPONSE_DATA_DEFAULT_SZ + valsz; + buf = ptr = palloc(fxp->pool, bufsz); + + res = pr_fsio_lgetxattr(fxp->pool, path, name, val, (size_t) valsz); + if (res < 0) { + int xerrno = errno; + + (void) pr_log_writefile(sftp_logfd, MOD_SFTP_VERSION, + "getxattr(2) error on '%s' for attribute '%s': %s", path, name, + strerror(xerrno)); + + status_code = fxp_errno2status(xerrno, &reason); + + pr_trace_msg(trace_channel, 8, "sending response: STATUS %lu '%s' " + "('%s' [%d])", (unsigned long) status_code, reason, strerror(xerrno), + xerrno); + + fxp_status_write(fxp->pool, &buf, &buflen, fxp->request_id, status_code, + reason, NULL); + + resp = fxp_packet_create(fxp->pool, fxp->channel_id); + resp->payload = ptr; + resp->payload_sz = (bufsz - buflen); + + return fxp_packet_write(resp); + } + + pr_trace_msg(trace_channel, 8, + "sending response: EXTENDED_REPLY (%lu bytes)", (unsigned long) res); + + sftp_msg_write_byte(&buf, &buflen, SFTP_SSH2_FXP_EXTENDED_REPLY); + sftp_msg_write_int(&buf, &buflen, fxp->request_id); + sftp_msg_write_data(&buf, &buflen, val, res, TRUE); + + resp = fxp_packet_create(fxp->pool, fxp->channel_id); + resp->payload = ptr; + resp->payload_sz = (bufsz - buflen); + + return fxp_packet_write(resp); +} + +static int fxp_handle_ext_fgetxattr(struct fxp_packet *fxp, const char *handle, + const char *name, uint32_t valsz) { + ssize_t res; + void *val; + unsigned char *buf, *ptr; + uint32_t buflen, bufsz, status_code; + const char *path, *reason; + struct fxp_handle *fxh; + struct fxp_packet *resp; + + buflen = bufsz = FXP_RESPONSE_DATA_DEFAULT_SZ + valsz; + buf = ptr = palloc(fxp->pool, bufsz); + + fxh = fxp_handle_get(handle); + if (fxh == NULL) { + pr_trace_msg(trace_channel, 17, + "fgetxattr@proftpd.org: unable to find handle for name '%s': %s", handle, + strerror(errno)); + + status_code = SSH2_FX_INVALID_HANDLE; + + pr_trace_msg(trace_channel, 8, "sending response: STATUS %lu '%s'", + (unsigned long) status_code, fxp_strerror(status_code)); + + fxp_status_write(fxp->pool, &buf, &buflen, fxp->request_id, status_code, + fxp_strerror(status_code), NULL); + + resp = fxp_packet_create(fxp->pool, fxp->channel_id); + resp->payload = ptr; + resp->payload_sz = (bufsz - buflen); + + return fxp_packet_write(resp); + } + + if (fxh->dirh != NULL) { + /* Request for extended attributes on a directory handle. It's not + * easy to get the file descriptor on a directory, so we'll just do + * by path instead. + */ + return fxp_handle_ext_getxattr(fxp, fxh->fh->fh_path, name, valsz); + } + + if (fxh->fh == NULL) { + status_code = SSH2_FX_INVALID_HANDLE; + + pr_trace_msg(trace_channel, 8, "sending response: STATUS %lu '%s'", + (unsigned long) status_code, fxp_strerror(status_code)); + + fxp_status_write(fxp->pool, &buf, &buflen, fxp->request_id, status_code, + fxp_strerror(status_code), NULL); + + resp = fxp_packet_create(fxp->pool, fxp->channel_id); + resp->payload = ptr; + resp->payload_sz = (bufsz - buflen); + + return fxp_packet_write(resp); + } + + path = fxh->fh->fh_path; + val = pcalloc(fxp->pool, (size_t) valsz+1); + + res = pr_fsio_fgetxattr(fxp->pool, fxh->fh, name, val, (size_t) valsz); + if (res < 0) { + int xerrno = errno; + + (void) pr_log_writefile(sftp_logfd, MOD_SFTP_VERSION, + "fgetxattr(2) error on '%s' for attribute '%s': %s", path, name, + strerror(xerrno)); + + status_code = fxp_errno2status(xerrno, &reason); + + pr_trace_msg(trace_channel, 8, "sending response: STATUS %lu '%s' " + "('%s' [%d])", (unsigned long) status_code, reason, strerror(xerrno), + xerrno); + + fxp_status_write(fxp->pool, &buf, &buflen, fxp->request_id, status_code, + reason, NULL); + + resp = fxp_packet_create(fxp->pool, fxp->channel_id); + resp->payload = ptr; + resp->payload_sz = (bufsz - buflen); + + return fxp_packet_write(resp); + } + + pr_trace_msg(trace_channel, 8, + "sending response: EXTENDED_REPLY (%lu bytes)", (unsigned long) res); + + sftp_msg_write_byte(&buf, &buflen, SFTP_SSH2_FXP_EXTENDED_REPLY); + sftp_msg_write_int(&buf, &buflen, fxp->request_id); + sftp_msg_write_data(&buf, &buflen, val, res, TRUE); + + resp = fxp_packet_create(fxp->pool, fxp->channel_id); + resp->payload = ptr; + resp->payload_sz = (bufsz - buflen); + + return fxp_packet_write(resp); +} + +static int fxp_handle_ext_listxattr(struct fxp_packet *fxp, const char *path) { + register unsigned int i; + int res; + unsigned char *buf, *ptr; + uint32_t buflen, bufsz, status_code; + const char *reason; + struct fxp_packet *resp; + array_header *names = NULL; + + buflen = bufsz = FXP_RESPONSE_NAME_DEFAULT_SZ; + buf = ptr = palloc(fxp->pool, bufsz); + + res = pr_fsio_llistxattr(fxp->pool, path, &names); + if (res < 0) { + int xerrno = errno; + + (void) pr_log_writefile(sftp_logfd, MOD_SFTP_VERSION, + "listxattr(2) error on '%s': %s", path, strerror(xerrno)); + + status_code = fxp_errno2status(xerrno, &reason); + + pr_trace_msg(trace_channel, 8, "sending response: STATUS %lu '%s' " + "('%s' [%d])", (unsigned long) status_code, reason, strerror(xerrno), + xerrno); + + fxp_status_write(fxp->pool, &buf, &buflen, fxp->request_id, status_code, + reason, NULL); + + resp = fxp_packet_create(fxp->pool, fxp->channel_id); + resp->payload = ptr; + resp->payload_sz = (bufsz - buflen); + + return fxp_packet_write(resp); + } + + pr_trace_msg(trace_channel, 8, + "sending response: EXTENDED_REPLY (%d attribute names)", names->nelts); + + sftp_msg_write_byte(&buf, &buflen, SFTP_SSH2_FXP_EXTENDED_REPLY); + sftp_msg_write_int(&buf, &buflen, fxp->request_id); + sftp_msg_write_int(&buf, &buflen, names->nelts); + for (i = 0; i < names->nelts; i++) { + const char *name; + + name = ((const char **) names->elts)[i]; + sftp_msg_write_string(&buf, &buflen, name); + } + + resp = fxp_packet_create(fxp->pool, fxp->channel_id); + resp->payload = ptr; + resp->payload_sz = (bufsz - buflen); + + return fxp_packet_write(resp); +} + +static int fxp_handle_ext_flistxattr(struct fxp_packet *fxp, + const char *handle) { + register unsigned int i; + int res; + unsigned char *buf, *ptr; + uint32_t buflen, bufsz, status_code; + const char *path, *reason; + struct fxp_handle *fxh; + struct fxp_packet *resp; + array_header *names = NULL; + + buflen = bufsz = FXP_RESPONSE_NAME_DEFAULT_SZ; + buf = ptr = palloc(fxp->pool, bufsz); + + fxh = fxp_handle_get(handle); + if (fxh == NULL) { + pr_trace_msg(trace_channel, 17, + "flistxattr@proftpd.org: unable to find handle for name '%s': %s", handle, + strerror(errno)); + + status_code = SSH2_FX_INVALID_HANDLE; + + pr_trace_msg(trace_channel, 8, "sending response: STATUS %lu '%s'", + (unsigned long) status_code, fxp_strerror(status_code)); + + fxp_status_write(fxp->pool, &buf, &buflen, fxp->request_id, status_code, + fxp_strerror(status_code), NULL); + + resp = fxp_packet_create(fxp->pool, fxp->channel_id); + resp->payload = ptr; + resp->payload_sz = (bufsz - buflen); + + return fxp_packet_write(resp); + } + + if (fxh->dirh != NULL) { + /* Request for extended attributes on a directory handle. It's not + * easy to get the file descriptor on a directory, so we'll just do + * by path instead. + */ + return fxp_handle_ext_listxattr(fxp, fxh->fh->fh_path); + } + + if (fxh->fh == NULL) { + status_code = SSH2_FX_INVALID_HANDLE; + + pr_trace_msg(trace_channel, 8, "sending response: STATUS %lu '%s'", + (unsigned long) status_code, fxp_strerror(status_code)); + + fxp_status_write(fxp->pool, &buf, &buflen, fxp->request_id, status_code, + fxp_strerror(status_code), NULL); + + resp = fxp_packet_create(fxp->pool, fxp->channel_id); + resp->payload = ptr; + resp->payload_sz = (bufsz - buflen); + + return fxp_packet_write(resp); + } + + path = fxh->fh->fh_path; + res = pr_fsio_flistxattr(fxp->pool, fxh->fh, &names); + if (res < 0) { + int xerrno = errno; + + (void) pr_log_writefile(sftp_logfd, MOD_SFTP_VERSION, + "flistxattr(2) error on '%s': %s", path, strerror(xerrno)); + + status_code = fxp_errno2status(xerrno, &reason); + + pr_trace_msg(trace_channel, 8, "sending response: STATUS %lu '%s' " + "('%s' [%d])", (unsigned long) status_code, reason, strerror(xerrno), + xerrno); + + fxp_status_write(fxp->pool, &buf, &buflen, fxp->request_id, status_code, + reason, NULL); + + resp = fxp_packet_create(fxp->pool, fxp->channel_id); + resp->payload = ptr; + resp->payload_sz = (bufsz - buflen); + + return fxp_packet_write(resp); + } + + pr_trace_msg(trace_channel, 8, + "sending response: EXTENDED_REPLY (%d attributes)", names->nelts); + + sftp_msg_write_byte(&buf, &buflen, SFTP_SSH2_FXP_EXTENDED_REPLY); + sftp_msg_write_int(&buf, &buflen, fxp->request_id); + sftp_msg_write_int(&buf, &buflen, names->nelts); + for (i = 0; i < names->nelts; i++) { + const char *name; + + name = ((const char **) names->elts)[i]; + sftp_msg_write_string(&buf, &buflen, name); + } + + sftp_msg_write_data(&buf, &buflen, (const unsigned char *) names, res, TRUE); + + resp = fxp_packet_create(fxp->pool, fxp->channel_id); + resp->payload = ptr; + resp->payload_sz = (bufsz - buflen); + + return fxp_packet_write(resp); +} + +static int fxp_handle_ext_removexattr(struct fxp_packet *fxp, const char *path, + const char *name) { + int res; + unsigned char *buf, *ptr; + uint32_t buflen, bufsz, status_code; + const char *reason; + struct fxp_packet *resp; + + buflen = bufsz = FXP_RESPONSE_DATA_DEFAULT_SZ; + buf = ptr = palloc(fxp->pool, bufsz); + + res = pr_fsio_lremovexattr(fxp->pool, path, name); + if (res < 0) { + int xerrno = errno; + + (void) pr_log_writefile(sftp_logfd, MOD_SFTP_VERSION, + "removexattr(2) error on '%s' for attribute '%s': %s", path, name, + strerror(xerrno)); + + status_code = fxp_errno2status(xerrno, &reason); + + pr_trace_msg(trace_channel, 8, "sending response: STATUS %lu '%s' " + "('%s' [%d])", (unsigned long) status_code, reason, + xerrno != EOF ? strerror(xerrno) : "End of file", xerrno); + + fxp_status_write(fxp->pool, &buf, &buflen, fxp->request_id, status_code, + reason, NULL); + + resp = fxp_packet_create(fxp->pool, fxp->channel_id); + resp->payload = ptr; + resp->payload_sz = (bufsz - buflen); + + return fxp_packet_write(resp); + } + + status_code = SSH2_FX_OK; + reason = "OK"; + + pr_trace_msg(trace_channel, 8, "sending response: STATUS %lu '%s'", + (unsigned long) status_code, reason); + + fxp_status_write(fxp->pool, &buf, &buflen, fxp->request_id, status_code, + reason, NULL); + + resp = fxp_packet_create(fxp->pool, fxp->channel_id); + resp->payload = ptr; + resp->payload_sz = (bufsz - buflen); + + return fxp_packet_write(resp); +} + +static int fxp_handle_ext_fremovexattr(struct fxp_packet *fxp, + const char *handle, const char *name) { + int res; + unsigned char *buf, *ptr; + uint32_t buflen, bufsz, status_code; + const char *path, *reason; + struct fxp_handle *fxh; + struct fxp_packet *resp; + + buflen = bufsz = FXP_RESPONSE_DATA_DEFAULT_SZ; + buf = ptr = palloc(fxp->pool, bufsz); + + fxh = fxp_handle_get(handle); + if (fxh == NULL) { + pr_trace_msg(trace_channel, 17, + "fremovexattr@proftpd.org: unable to find handle for name '%s': %s", + handle, strerror(errno)); + + status_code = SSH2_FX_INVALID_HANDLE; + + pr_trace_msg(trace_channel, 8, "sending response: STATUS %lu '%s'", + (unsigned long) status_code, fxp_strerror(status_code)); + + fxp_status_write(fxp->pool, &buf, &buflen, fxp->request_id, status_code, + fxp_strerror(status_code), NULL); + + resp = fxp_packet_create(fxp->pool, fxp->channel_id); + resp->payload = ptr; + resp->payload_sz = (bufsz - buflen); + + return fxp_packet_write(resp); + } + + if (fxh->dirh != NULL) { + /* Request for extended attributes on a directory handle. It's not + * easy to get the file descriptor on a directory, so we'll just do + * by path instead. + */ + return fxp_handle_ext_removexattr(fxp, fxh->fh->fh_path, name); + } + + if (fxh->fh == NULL) { + status_code = SSH2_FX_INVALID_HANDLE; + + pr_trace_msg(trace_channel, 8, "sending response: STATUS %lu '%s'", + (unsigned long) status_code, fxp_strerror(status_code)); + + fxp_status_write(fxp->pool, &buf, &buflen, fxp->request_id, status_code, + fxp_strerror(status_code), NULL); + + resp = fxp_packet_create(fxp->pool, fxp->channel_id); + resp->payload = ptr; + resp->payload_sz = (bufsz - buflen); + + return fxp_packet_write(resp); + } + + path = fxh->fh->fh_path; + + res = pr_fsio_fremovexattr(fxp->pool, fxh->fh, name); + if (res < 0) { + int xerrno = errno; + + (void) pr_log_writefile(sftp_logfd, MOD_SFTP_VERSION, + "fremovexattr(2) error on '%s' for attribute '%s': %s", path, name, + strerror(xerrno)); + + status_code = fxp_errno2status(xerrno, &reason); + + pr_trace_msg(trace_channel, 8, "sending response: STATUS %lu '%s' " + "('%s' [%d])", (unsigned long) status_code, reason, + xerrno != EOF ? strerror(xerrno) : "End of file", xerrno); + + fxp_status_write(fxp->pool, &buf, &buflen, fxp->request_id, status_code, + reason, NULL); + + resp = fxp_packet_create(fxp->pool, fxp->channel_id); + resp->payload = ptr; + resp->payload_sz = (bufsz - buflen); + + return fxp_packet_write(resp); + } + + status_code = SSH2_FX_OK; + reason = "OK"; + + pr_trace_msg(trace_channel, 8, "sending response: STATUS %lu '%s'", + (unsigned long) status_code, reason); + + fxp_status_write(fxp->pool, &buf, &buflen, fxp->request_id, status_code, + reason, NULL); + + resp = fxp_packet_create(fxp->pool, fxp->channel_id); + resp->payload = ptr; + resp->payload_sz = (bufsz - buflen); + + return fxp_packet_write(resp); +} + +static int fxp_handle_ext_setxattr(struct fxp_packet *fxp, const char *path, + const char *name, void *val, uint32_t valsz, uint32_t pflags) { + int res, flags = 0; + unsigned char *buf, *ptr; + uint32_t buflen, bufsz, status_code; + const char *reason; + struct fxp_packet *resp; + + buflen = bufsz = FXP_RESPONSE_DATA_DEFAULT_SZ; + buf = ptr = palloc(fxp->pool, bufsz); + + if (pflags & SSH2_FXE_XATTR_CREATE) { + flags |= PR_FSIO_XATTR_FL_CREATE; + } + + if (pflags & SSH2_FXE_XATTR_REPLACE) { + flags |= PR_FSIO_XATTR_FL_REPLACE; + } + + res = pr_fsio_lsetxattr(fxp->pool, path, name, val, (size_t) valsz, flags); + if (res < 0) { + int xerrno = errno; + + (void) pr_log_writefile(sftp_logfd, MOD_SFTP_VERSION, + "setxattr(2) error on '%s' for attribute '%s': %s", path, name, + strerror(xerrno)); + + status_code = fxp_errno2status(xerrno, &reason); + + pr_trace_msg(trace_channel, 8, "sending response: STATUS %lu '%s' " + "('%s' [%d])", (unsigned long) status_code, reason, + xerrno != EOF ? strerror(xerrno) : "End of file", xerrno); + + fxp_status_write(fxp->pool, &buf, &buflen, fxp->request_id, status_code, + reason, NULL); + + resp = fxp_packet_create(fxp->pool, fxp->channel_id); + resp->payload = ptr; + resp->payload_sz = (bufsz - buflen); + + return fxp_packet_write(resp); + } + + status_code = SSH2_FX_OK; + reason = "OK"; + + pr_trace_msg(trace_channel, 8, "sending response: STATUS %lu '%s'", + (unsigned long) status_code, reason); + + fxp_status_write(fxp->pool, &buf, &buflen, fxp->request_id, status_code, + reason, NULL); + + resp = fxp_packet_create(fxp->pool, fxp->channel_id); + resp->payload = ptr; + resp->payload_sz = (bufsz - buflen); + + return fxp_packet_write(resp); +} + +static int fxp_handle_ext_fsetxattr(struct fxp_packet *fxp, const char *handle, + const char *name, void *val, uint32_t valsz, uint32_t pflags) { + int res, flags = 0; + unsigned char *buf, *ptr; + uint32_t buflen, bufsz, status_code; + const char *path, *reason; + struct fxp_handle *fxh; + struct fxp_packet *resp; + + buflen = bufsz = FXP_RESPONSE_DATA_DEFAULT_SZ; + buf = ptr = palloc(fxp->pool, bufsz); + + fxh = fxp_handle_get(handle); + if (fxh == NULL) { + pr_trace_msg(trace_channel, 17, + "fsetxattr@proftpd.org: unable to find handle for name '%s': %s", handle, + strerror(errno)); + + status_code = SSH2_FX_INVALID_HANDLE; + + pr_trace_msg(trace_channel, 8, "sending response: STATUS %lu '%s'", + (unsigned long) status_code, fxp_strerror(status_code)); + + fxp_status_write(fxp->pool, &buf, &buflen, fxp->request_id, status_code, + fxp_strerror(status_code), NULL); + + resp = fxp_packet_create(fxp->pool, fxp->channel_id); + resp->payload = ptr; + resp->payload_sz = (bufsz - buflen); + + return fxp_packet_write(resp); + } + + if (fxh->dirh != NULL) { + /* Request for extended attributes on a directory handle. It's not + * easy to get the file descriptor on a directory, so we'll just do + * by path instead. + */ + return fxp_handle_ext_setxattr(fxp, fxh->fh->fh_path, name, val, valsz, + pflags); + } + + if (fxh->fh == NULL) { + status_code = SSH2_FX_INVALID_HANDLE; + + pr_trace_msg(trace_channel, 8, "sending response: STATUS %lu '%s'", + (unsigned long) status_code, fxp_strerror(status_code)); + + fxp_status_write(fxp->pool, &buf, &buflen, fxp->request_id, status_code, + fxp_strerror(status_code), NULL); + + resp = fxp_packet_create(fxp->pool, fxp->channel_id); + resp->payload = ptr; + resp->payload_sz = (bufsz - buflen); + + return fxp_packet_write(resp); + } + + if (pflags & SSH2_FXE_XATTR_CREATE) { + flags |= PR_FSIO_XATTR_FL_CREATE; } -#endif -#ifdef ST_NOSUID - if (fs.f_flag & ST_NOSUID) { - fs_flags |= SSH2_FXE_STATVFS_ST_NOSUID; + if (pflags & SSH2_FXE_XATTR_REPLACE) { + flags |= PR_FSIO_XATTR_FL_REPLACE; } -#endif - sftp_msg_write_long(&buf, &buflen, fs_flags); - sftp_msg_write_long(&buf, &buflen, fs.f_namemax); + path = fxh->fh->fh_path; + + res = pr_fsio_fsetxattr(fxp->pool, fxh->fh, name, val, (size_t) valsz, flags); + if (res < 0) { + int xerrno = errno; + + (void) pr_log_writefile(sftp_logfd, MOD_SFTP_VERSION, + "fsetxattr(2) error on '%s' for attribute '%s': %s", path, name, + strerror(xerrno)); + + status_code = fxp_errno2status(xerrno, &reason); + + pr_trace_msg(trace_channel, 8, "sending response: STATUS %lu '%s' " + "('%s' [%d])", (unsigned long) status_code, reason, + xerrno != EOF ? strerror(xerrno) : "End of file", xerrno); + + fxp_status_write(fxp->pool, &buf, &buflen, fxp->request_id, status_code, + reason, NULL); + + resp = fxp_packet_create(fxp->pool, fxp->channel_id); + resp->payload = ptr; + resp->payload_sz = (bufsz - buflen); + + return fxp_packet_write(resp); + } + + status_code = SSH2_FX_OK; + reason = "OK"; + + pr_trace_msg(trace_channel, 8, "sending response: STATUS %lu '%s'", + (unsigned long) status_code, reason); + + fxp_status_write(fxp->pool, &buf, &buflen, fxp->request_id, status_code, + reason, NULL); resp = fxp_packet_create(fxp->pool, fxp->channel_id); resp->payload = ptr; @@ -5350,7 +6169,7 @@ static int fxp_handle_ext_statvfs(struct fxp_packet *fxp, const char *path) { return fxp_packet_write(resp); } -#endif /* !HAVE_SYS_STATVFS_H */ +#endif /* PR_USE_XATTR */ static int fxp_handle_ext_vendor_id(struct fxp_packet *fxp) { unsigned char *buf, *ptr; @@ -6169,6 +6988,120 @@ static int fxp_handle_extended(struct fxp_packet *fxp) { } #endif +#ifdef PR_USE_XATTR + if (fxp_ext_flags & SFTP_FXP_EXT_XATTR) { + if (strcmp(ext_request_name, "fgetxattr@proftpd.org") == 0) { + const char *handle, *name; + uint32_t valsz; + + handle = sftp_msg_read_string(fxp->pool, &fxp->payload, &fxp->payload_sz); + name = sftp_msg_read_string(fxp->pool, &fxp->payload, &fxp->payload_sz); + valsz = sftp_msg_read_int(fxp->pool, &fxp->payload, &fxp->payload_sz); + + res = fxp_handle_ext_fgetxattr(fxp, handle, name, valsz); + pr_cmd_dispatch_phase(cmd, res == 0 ? LOG_CMD : LOG_CMD_ERR, 0); + + return res; + } + + if (strcmp(ext_request_name, "flistxattr@proftpd.org") == 0) { + const char *handle; + + handle = sftp_msg_read_string(fxp->pool, &fxp->payload, &fxp->payload_sz); + + res = fxp_handle_ext_flistxattr(fxp, handle); + pr_cmd_dispatch_phase(cmd, res == 0 ? LOG_CMD : LOG_CMD_ERR, 0); + + return res; + } + + if (strcmp(ext_request_name, "fremovexattr@proftpd.org") == 0) { + const char *handle, *name; + + handle = sftp_msg_read_string(fxp->pool, &fxp->payload, &fxp->payload_sz); + name = sftp_msg_read_string(fxp->pool, &fxp->payload, &fxp->payload_sz); + + res = fxp_handle_ext_fremovexattr(fxp, handle, name); + pr_cmd_dispatch_phase(cmd, res == 0 ? LOG_CMD : LOG_CMD_ERR, 0); + + return res; + } + + if (strcmp(ext_request_name, "fsetxattr@proftpd.org") == 0) { + const char *handle, *name; + void *val; + uint32_t pflags, valsz; + + handle = sftp_msg_read_string(fxp->pool, &fxp->payload, &fxp->payload_sz); + name = sftp_msg_read_string(fxp->pool, &fxp->payload, &fxp->payload_sz); + valsz = sftp_msg_read_int(fxp->pool, &fxp->payload, &fxp->payload_sz); + val = (void *) sftp_msg_read_data(fxp->pool, &fxp->payload, + &fxp->payload_sz, valsz); + pflags = sftp_msg_read_int(fxp->pool, &fxp->payload, &fxp->payload_sz); + + res = fxp_handle_ext_fsetxattr(fxp, handle, name, val, valsz, pflags); + pr_cmd_dispatch_phase(cmd, res == 0 ? LOG_CMD : LOG_CMD_ERR, 0); + + return res; + } + + if (strcmp(ext_request_name, "getxattr@proftpd.org") == 0) { + const char *path, *name; + uint32_t valsz; + + path = sftp_msg_read_string(fxp->pool, &fxp->payload, &fxp->payload_sz); + name = sftp_msg_read_string(fxp->pool, &fxp->payload, &fxp->payload_sz); + valsz = sftp_msg_read_int(fxp->pool, &fxp->payload, &fxp->payload_sz); + + res = fxp_handle_ext_getxattr(fxp, path, name, valsz); + pr_cmd_dispatch_phase(cmd, res == 0 ? LOG_CMD : LOG_CMD_ERR, 0); + + return res; + } + + if (strcmp(ext_request_name, "listxattr@proftpd.org") == 0) { + const char *path; + + path = sftp_msg_read_string(fxp->pool, &fxp->payload, &fxp->payload_sz); + + res = fxp_handle_ext_listxattr(fxp, path); + pr_cmd_dispatch_phase(cmd, res == 0 ? LOG_CMD : LOG_CMD_ERR, 0); + + return res; + } + + if (strcmp(ext_request_name, "removexattr@proftpd.org") == 0) { + const char *path, *name; + + path = sftp_msg_read_string(fxp->pool, &fxp->payload, &fxp->payload_sz); + name = sftp_msg_read_string(fxp->pool, &fxp->payload, &fxp->payload_sz); + + res = fxp_handle_ext_removexattr(fxp, path, name); + pr_cmd_dispatch_phase(cmd, res == 0 ? LOG_CMD : LOG_CMD_ERR, 0); + + return res; + } + + if (strcmp(ext_request_name, "setxattr@proftpd.org") == 0) { + const char *path, *name; + void *val; + uint32_t pflags, valsz; + + path = sftp_msg_read_string(fxp->pool, &fxp->payload, &fxp->payload_sz); + name = sftp_msg_read_string(fxp->pool, &fxp->payload, &fxp->payload_sz); + valsz = sftp_msg_read_int(fxp->pool, &fxp->payload, &fxp->payload_sz); + val = (void *) sftp_msg_read_data(fxp->pool, &fxp->payload, + &fxp->payload_sz, valsz); + pflags = sftp_msg_read_int(fxp->pool, &fxp->payload, &fxp->payload_sz); + + res = fxp_handle_ext_setxattr(fxp, path, name, val, valsz, pflags); + pr_cmd_dispatch_phase(cmd, res == 0 ? LOG_CMD : LOG_CMD_ERR, 0); + + return res; + } + } +#endif /* PR_USE_XATTR */ + (void) pr_log_writefile(sftp_logfd, MOD_SFTP_VERSION, "client requested '%s' extension, rejecting", ext_request_name); status_code = SSH2_FX_OP_UNSUPPORTED; @@ -6198,6 +7131,7 @@ static int fxp_handle_fsetstat(struct fxp_packet *fxp) { struct fxp_handle *fxh; struct fxp_packet *resp; cmd_rec *cmd; + array_header *xattrs = NULL; name = sftp_msg_read_string(fxp->pool, &fxp->payload, &fxp->payload_sz); @@ -6209,7 +7143,8 @@ static int fxp_handle_fsetstat(struct fxp_packet *fxp) { pr_scoreboard_entry_update(session.pid, PR_SCORE_CMD_ARG, "%s", name, NULL, NULL); - attrs = fxp_attrs_read(fxp, &fxp->payload, &fxp->payload_sz, &attr_flags); + attrs = fxp_attrs_read(fxp, &fxp->payload, &fxp->payload_sz, &attr_flags, + &xattrs); if (attrs == NULL) { pr_cmd_dispatch_phase(cmd, LOG_CMD_ERR, 0); return 0; @@ -6360,6 +7295,17 @@ static int fxp_handle_fsetstat(struct fxp_packet *fxp) { attr_flags &= ~SSH2_FX_ATTR_OWNERGROUP; } + /* If the SFTPOption for ignoring the xattrs for SFTP setstat requests is set, + * handle it by clearing the SSH2_FX_ATTR_EXTENDED flag. + */ + if ((sftp_opts & SFTP_OPT_IGNORE_SFTP_SET_XATTRS) && + (attr_flags & SSH2_FX_ATTR_EXTENDED)) { + pr_trace_msg(trace_channel, 7, + "SFTPOption 'IgnoreSFTPSetExtendedAttributes' configured, ignoring " + "xattrs sent by client"); + attr_flags &= ~SSH2_FX_ATTR_EXTENDED; + } + /* If the SFTPOption for ignoring the perms for SFTP setstat requests is set, * handle it by clearing the SSH2_FX_ATTR_PERMISSIONS flag. */ @@ -6384,11 +7330,12 @@ static int fxp_handle_fsetstat(struct fxp_packet *fxp) { } if (fxh->fh != NULL) { - res = fxp_attrs_set(fxh->fh, fxh->fh->fh_path, attrs, attr_flags, &buf, - &buflen, fxp); + res = fxp_attrs_set(fxh->fh, fxh->fh->fh_path, attrs, attr_flags, xattrs, + &buf, &buflen, fxp); } else { - res = fxp_attrs_set(NULL, fxh->dir, attrs, attr_flags, &buf, &buflen, fxp); + res = fxp_attrs_set(NULL, fxh->dir, attrs, attr_flags, xattrs, &buf, + &buflen, fxp); } if (res < 0) { @@ -6434,10 +7381,11 @@ static int fxp_handle_fsetstat(struct fxp_packet *fxp) { } static int fxp_handle_fstat(struct fxp_packet *fxp) { - unsigned char *buf, *ptr; + unsigned char *buf; char *cmd_name, *name; - uint32_t buflen, bufsz; + uint32_t attr_flags, buflen; struct stat st; + struct fxp_buffer *fxb; struct fxp_handle *fxh; struct fxp_packet *resp; cmd_rec *cmd; @@ -6457,11 +7405,6 @@ static int fxp_handle_fstat(struct fxp_packet *fxp) { name); if (fxp_session->client_version > 3) { - uint32_t attr_flags; - - /* These are hints from the client about what file attributes are - * of particular interest. We do not currently honor them. - */ attr_flags = sftp_msg_read_int(fxp->pool, &fxp->payload, &fxp->payload_sz); pr_trace_msg(trace_channel, 7, "received request: FSTAT %s %s", name, @@ -6469,10 +7412,13 @@ static int fxp_handle_fstat(struct fxp_packet *fxp) { } else { pr_trace_msg(trace_channel, 7, "received request: FSTAT %s", name); + attr_flags = SSH2_FX_ATTR_SIZE|SSH2_FX_ATTR_UIDGID|SSH2_FX_ATTR_PERMISSIONS| + SSH2_FX_ATTR_ACMODTIME; } - buflen = bufsz = FXP_RESPONSE_DATA_DEFAULT_SZ; - buf = ptr = palloc(fxp->pool, bufsz); + fxb = pcalloc(fxp->pool, sizeof(struct fxp_buffer)); + fxb->bufsz = buflen = FXP_RESPONSE_NAME_DEFAULT_SZ; + fxb->ptr = buf = palloc(fxp->pool, fxb->bufsz); fxh = fxp_handle_get(name); if (fxh == NULL) { @@ -6495,8 +7441,8 @@ static int fxp_handle_fstat(struct fxp_packet *fxp) { pr_response_clear(&resp_err_list); resp = fxp_packet_create(fxp->pool, fxp->channel_id); - resp->payload = ptr; - resp->payload_sz = (bufsz - buflen); + resp->payload = fxb->ptr; + resp->payload_sz = (fxb->bufsz - buflen); return fxp_packet_write(resp); } @@ -6515,8 +7461,8 @@ static int fxp_handle_fstat(struct fxp_packet *fxp) { pr_response_clear(&resp_err_list); resp = fxp_packet_create(fxp->pool, fxp->channel_id); - resp->payload = ptr; - resp->payload_sz = (bufsz - buflen); + resp->payload = fxb->ptr; + resp->payload_sz = (fxb->bufsz - buflen); return fxp_packet_write(resp); } @@ -6546,8 +7492,8 @@ static int fxp_handle_fstat(struct fxp_packet *fxp) { pr_response_clear(&resp_err_list); resp = fxp_packet_create(fxp->pool, fxp->channel_id); - resp->payload = ptr; - resp->payload_sz = (bufsz - buflen); + resp->payload = fxb->ptr; + resp->payload_sz = (fxb->bufsz - buflen); return fxp_packet_write(resp); } @@ -6575,8 +7521,8 @@ static int fxp_handle_fstat(struct fxp_packet *fxp) { pr_response_clear(&resp_err_list); resp = fxp_packet_create(fxp->pool, fxp->channel_id); - resp->payload = ptr; - resp->payload_sz = (bufsz - buflen); + resp->payload = fxb->ptr; + resp->payload_sz = (fxb->bufsz - buflen); return fxp_packet_write(resp); } @@ -6601,15 +7547,23 @@ static int fxp_handle_fstat(struct fxp_packet *fxp) { fake_group = session.group; } - fxp_attrs_write(fxp->pool, &buf, &buflen, &st, fake_user, fake_group); + fxb->buf = buf; + fxb->buflen = buflen; + + fxp_attrs_write(fxp->pool, fxb, fxh->fh->fh_path, &st, attr_flags, fake_user, + fake_group); + + /* fxp_attrs_write will have changed the buf/buflen values in the buffer. */ + buf = fxb->buf; + buflen = fxb->buflen; pr_cmd_dispatch_phase(cmd, POST_CMD, 0); pr_cmd_dispatch_phase(cmd, LOG_CMD, 0); pr_response_clear(&resp_list); resp = fxp_packet_create(fxp->pool, fxp->channel_id); - resp->payload = ptr; - resp->payload_sz = (bufsz - buflen); + resp->payload = fxb->ptr; + resp->payload_sz = (fxb->bufsz - buflen); return fxp_packet_write(resp); } @@ -6694,7 +7648,6 @@ static int fxp_handle_init(struct fxp_packet *fxp) { } fxp_version_add_version_ext(fxp->pool, &buf, &buflen); - fxp_version_add_openssh_exts(fxp->pool, &buf, &buflen); if (fxp_session->client_version >= 4) { fxp_version_add_newline_ext(fxp->pool, &buf, &buflen); @@ -6708,6 +7661,8 @@ static int fxp_handle_init(struct fxp_packet *fxp) { fxp_version_add_supported2_ext(fxp->pool, &buf, &buflen); } + fxp_version_add_openssh_exts(fxp->pool, &buf, &buflen); + pr_event_generate("mod_sftp.sftp.protocol-version", &(fxp_session->client_version)); pr_cmd_dispatch_phase(cmd, POST_CMD, 0); @@ -7146,10 +8101,11 @@ static int fxp_handle_lock(struct fxp_packet *fxp) { } static int fxp_handle_lstat(struct fxp_packet *fxp) { - unsigned char *buf, *ptr; + unsigned char *buf; char *cmd_name, *path; - uint32_t buflen, bufsz; + uint32_t attr_flags, buflen; struct stat st; + struct fxp_buffer *fxb; struct fxp_packet *resp; cmd_rec *cmd; const char *fake_user = NULL, *fake_group = NULL; @@ -7168,11 +8124,6 @@ static int fxp_handle_lstat(struct fxp_packet *fxp) { path); if (fxp_session->client_version > 3) { - uint32_t attr_flags; - - /* These are hints from the client about what file attributes are - * of particular interest. We do not currently honor them. - */ attr_flags = sftp_msg_read_int(fxp->pool, &fxp->payload, &fxp->payload_sz); pr_trace_msg(trace_channel, 7, "received request: LSTAT %s %s", path, @@ -7180,6 +8131,11 @@ static int fxp_handle_lstat(struct fxp_packet *fxp) { } else { pr_trace_msg(trace_channel, 7, "received request: LSTAT %s", path); + attr_flags = SSH2_FX_ATTR_SIZE|SSH2_FX_ATTR_UIDGID|SSH2_FX_ATTR_PERMISSIONS| + SSH2_FX_ATTR_ACMODTIME; +#ifdef PR_USE_XATTR + attr_flags |= SSH2_FX_ATTR_EXTENDED; +#endif /* PR_USE_XATTR */ } if (strlen(path) == 0) { @@ -7193,8 +8149,9 @@ static int fxp_handle_lstat(struct fxp_packet *fxp) { cmd = fxp_cmd_alloc(fxp->pool, "LSTAT", path); cmd->cmd_class = CL_READ|CL_SFTP; - buflen = bufsz = FXP_RESPONSE_DATA_DEFAULT_SZ; - buf = ptr = palloc(fxp->pool, bufsz); + fxb = pcalloc(fxp->pool, sizeof(struct fxp_buffer)); + fxb->bufsz = buflen = FXP_RESPONSE_NAME_DEFAULT_SZ; + fxb->ptr = buf = palloc(fxp->pool, fxb->bufsz); if (pr_cmd_dispatch_phase(cmd, PRE_CMD, 0) < 0) { uint32_t status_code = SSH2_FX_PERMISSION_DENIED; @@ -7213,8 +8170,8 @@ static int fxp_handle_lstat(struct fxp_packet *fxp) { pr_response_clear(&resp_err_list); resp = fxp_packet_create(fxp->pool, fxp->channel_id); - resp->payload = ptr; - resp->payload_sz = (bufsz - buflen); + resp->payload = fxb->ptr; + resp->payload_sz = (fxb->bufsz - buflen); return fxp_packet_write(resp); } @@ -7243,8 +8200,8 @@ static int fxp_handle_lstat(struct fxp_packet *fxp) { pr_response_clear(&resp_err_list); resp = fxp_packet_create(fxp->pool, fxp->channel_id); - resp->payload = ptr; - resp->payload_sz = (bufsz - buflen); + resp->payload = fxb->ptr; + resp->payload_sz = (fxb->bufsz - buflen); return fxp_packet_write(resp); } @@ -7271,8 +8228,8 @@ static int fxp_handle_lstat(struct fxp_packet *fxp) { pr_response_clear(&resp_err_list); resp = fxp_packet_create(fxp->pool, fxp->channel_id); - resp->payload = ptr; - resp->payload_sz = (bufsz - buflen); + resp->payload = fxb->ptr; + resp->payload_sz = (fxb->bufsz - buflen); return fxp_packet_write(resp); } @@ -7301,8 +8258,8 @@ static int fxp_handle_lstat(struct fxp_packet *fxp) { pr_response_clear(&resp_err_list); resp = fxp_packet_create(fxp->pool, fxp->channel_id); - resp->payload = ptr; - resp->payload_sz = (bufsz - buflen); + resp->payload = fxb->ptr; + resp->payload_sz = (fxb->bufsz - buflen); return fxp_packet_write(resp); } @@ -7327,15 +8284,22 @@ static int fxp_handle_lstat(struct fxp_packet *fxp) { fake_group = session.group; } - fxp_attrs_write(fxp->pool, &buf, &buflen, &st, fake_user, fake_group); + fxb->buf = buf; + fxb->buflen = buflen; + + fxp_attrs_write(fxp->pool, fxb, path, &st, attr_flags, fake_user, fake_group); + + /* fxp_attrs_write will have changed the buf/buflen fields in the buffer. */ + buf = fxb->buf; + buflen = fxb->buflen; pr_cmd_dispatch_phase(cmd, POST_CMD, 0); pr_cmd_dispatch_phase(cmd, LOG_CMD, 0); pr_response_clear(&resp_list); resp = fxp_packet_create(fxp->pool, fxp->channel_id); - resp->payload = ptr; - resp->payload_sz = (bufsz - buflen); + resp->payload = fxb->ptr; + resp->payload_sz = (fxb->bufsz - buflen); return fxp_packet_write(resp); } @@ -7349,6 +8313,7 @@ static int fxp_handle_mkdir(struct fxp_packet *fxp) { uint32_t attr_flags, buflen, bufsz, status_code; struct fxp_packet *resp; cmd_rec *cmd, *cmd2; + array_header *xattrs = NULL; path = sftp_msg_read_string(fxp->pool, &fxp->payload, &fxp->payload_sz); if (fxp_session->client_version >= fxp_utf8_protocol_version) { @@ -7360,7 +8325,8 @@ static int fxp_handle_mkdir(struct fxp_packet *fxp) { pr_scoreboard_entry_update(session.pid, PR_SCORE_CMD_ARG, "%s", path, NULL, NULL); - attrs = fxp_attrs_read(fxp, &fxp->payload, &fxp->payload_sz, &attr_flags); + attrs = fxp_attrs_read(fxp, &fxp->payload, &fxp->payload_sz, &attr_flags, + &xattrs); if (attrs == NULL) { (void) pr_log_writefile(sftp_logfd, MOD_SFTP_VERSION, "MKDIR request missing required attributes, ignoring"); @@ -7377,6 +8343,17 @@ static int fxp_handle_mkdir(struct fxp_packet *fxp) { attr_flags &= ~SSH2_FX_ATTR_PERMISSIONS; } + /* If the SFTPOption for ignoring xattrs for SFTP uploads is set, handle it + * by clearing the SSH2_FX_ATTR_EXTENDED flag. + */ + if ((sftp_opts & SFTP_OPT_IGNORE_SFTP_UPLOAD_XATTRS) && + (attr_flags & SSH2_FX_ATTR_EXTENDED)) { + pr_trace_msg(trace_channel, 7, + "SFTPOption 'IgnoreSFTPUploadExtendedAttributes' configured, " + "ignoring xattrs sent by client"); + attr_flags &= ~SSH2_FX_ATTR_EXTENDED; + } + attrs_str = fxp_strattrs(fxp->pool, attrs, &attr_flags); pr_proctitle_set("%s - %s: MKDIR %s %s", session.user, session.proc_prefix, @@ -7675,6 +8652,7 @@ static int fxp_handle_open(struct fxp_packet *fxp) { struct fxp_handle *fxh; struct fxp_packet *resp; cmd_rec *cmd, *cmd2 = NULL; + array_header *xattrs = NULL; path = sftp_msg_read_string(fxp->pool, &fxp->payload, &fxp->payload_sz); if (fxp_session->client_version >= fxp_utf8_protocol_version) { @@ -7855,7 +8833,8 @@ static int fxp_handle_open(struct fxp_packet *fxp) { open_flags = fxp_get_v5_open_flags(desired_access, flags); } - attrs = fxp_attrs_read(fxp, &fxp->payload, &fxp->payload_sz, &attr_flags); + attrs = fxp_attrs_read(fxp, &fxp->payload, &fxp->payload_sz, &attr_flags, + &xattrs); if (attrs == NULL) { pr_cmd_dispatch_phase(cmd, POST_CMD_ERR, 0); pr_cmd_dispatch_phase(cmd, LOG_CMD_ERR, 0); @@ -8201,6 +9180,17 @@ static int fxp_handle_open(struct fxp_packet *fxp) { attr_flags &= ~SSH2_FX_ATTR_PERMISSIONS; } + /* If the SFTPOption for ignoring xattrs for SFTP uploads is set, handle it + * by clearing the SSH2_FX_ATTR_EXTENDED flag. + */ + if ((sftp_opts & SFTP_OPT_IGNORE_SFTP_UPLOAD_XATTRS) && + (attr_flags & SSH2_FX_ATTR_EXTENDED)) { + pr_trace_msg(trace_channel, 7, + "SFTPOption 'IgnoreSFTPUploadExtendedAttributes' configured, " + "ignoring xattrs sent by client"); + attr_flags &= ~SSH2_FX_ATTR_EXTENDED; + } + /* If the client provided a suggested size in the OPEN, ignore it. * Trying to honor the suggested size by truncating the file here can * cause problems, as when the client is resuming a transfer and the @@ -8223,7 +9213,8 @@ static int fxp_handle_open(struct fxp_packet *fxp) { attr_flags &= ~SSH2_FX_ATTR_SIZE; - res = fxp_attrs_set(fh, fh->fh_path, attrs, attr_flags, &buf, &buflen, fxp); + res = fxp_attrs_set(fh, fh->fh_path, attrs, attr_flags, xattrs, &buf, + &buflen, fxp); if (res < 0) { int xerrno = errno; @@ -9102,10 +10093,11 @@ static int fxp_handle_read(struct fxp_packet *fxp) { static int fxp_handle_readdir(struct fxp_packet *fxp) { register unsigned int i; - unsigned char *buf, *ptr; + unsigned char *buf; char *cmd_name, *name; - uint32_t buflen, bufsz, curr_packet_pathsz = 0, max_packetsz; + uint32_t attr_flags, buflen, curr_packet_pathsz = 0, max_packetsz; struct dirent *dent; + struct fxp_buffer *fxb; struct fxp_dirent **paths; struct fxp_handle *fxh; struct fxp_packet *resp; @@ -9133,9 +10125,11 @@ static int fxp_handle_readdir(struct fxp_packet *fxp) { /* XXX What's a good size here? */ + fxb = pcalloc(fxp->pool, sizeof(struct fxp_buffer)); + max_packetsz = sftp_channel_get_max_packetsz(); - buflen = bufsz = max_packetsz; - buf = ptr = palloc(fxp->pool, bufsz); + fxb->bufsz = buflen = max_packetsz; + fxb->ptr = buf = palloc(fxp->pool, fxb->bufsz); fxh = fxp_handle_get(name); if (fxh == NULL) { @@ -9158,8 +10152,8 @@ static int fxp_handle_readdir(struct fxp_packet *fxp) { pr_response_clear(&resp_err_list); resp = fxp_packet_create(fxp->pool, fxp->channel_id); - resp->payload = ptr; - resp->payload_sz = (bufsz - buflen); + resp->payload = fxb->ptr; + resp->payload_sz = (fxb->bufsz - buflen); return fxp_packet_write(resp); } @@ -9178,8 +10172,8 @@ static int fxp_handle_readdir(struct fxp_packet *fxp) { pr_response_clear(&resp_err_list); resp = fxp_packet_create(fxp->pool, fxp->channel_id); - resp->payload = ptr; - resp->payload_sz = (bufsz - buflen); + resp->payload = fxb->ptr; + resp->payload_sz = (fxb->bufsz - buflen); return fxp_packet_write(resp); } @@ -9236,8 +10230,8 @@ static int fxp_handle_readdir(struct fxp_packet *fxp) { pr_response_clear(&resp_list); resp = fxp_packet_create(fxp->pool, fxp->channel_id); - resp->payload = ptr; - resp->payload_sz = (bufsz - buflen); + resp->payload = fxb->ptr; + resp->payload_sz = (fxb->bufsz - buflen); return fxp_packet_write(resp); } @@ -9275,8 +10269,8 @@ static int fxp_handle_readdir(struct fxp_packet *fxp) { pr_response_clear(&resp_err_list); resp = fxp_packet_create(fxp->pool, fxp->channel_id); - resp->payload = ptr; - resp->payload_sz = (bufsz - buflen); + resp->payload = fxb->ptr; + resp->payload_sz = (fxb->bufsz - buflen); return fxp_packet_write(resp); } @@ -9301,12 +10295,20 @@ static int fxp_handle_readdir(struct fxp_packet *fxp) { while ((dent = pr_fsio_readdir(fxh->dirh)) != NULL) { char *real_path; struct fxp_dirent *fxd; - uint32_t curr_packetsz, max_entry_metadata = 256; - uint32_t max_entrysz = (PR_TUNABLE_PATH_MAX + 1 + max_entry_metadata); + uint32_t curr_packetsz, max_entry_metadata, max_entrysz; size_t dent_len; pr_signals_handle(); + /* How much non-path data do we expect to be associated with this entry? */ +#ifdef PR_USE_XATTR + max_entry_metadata = (1024 * 4); +#else + max_entry_metadata = 256; +#endif /* PR_USE_XATTR */ + + max_entrysz = (PR_TUNABLE_PATH_MAX + 1 + max_entry_metadata); + /* Do not expand/resolve dot directories; it will be handled automatically * lower down in the ACL-checking code. Plus, this allows regex filters * that rely on the dot directory name to work properly. @@ -9339,7 +10341,7 @@ static int fxp_handle_readdir(struct fxp_packet *fxp) { * the maximum packet size and the max entry size. * * We assume that each entry will need up to PR_TUNABLE_PATH_MAX+1 bytes for - * the filename, and 256 bytes of associated data. + * the filename, and max_entry_metadata bytes of associated data. * * We have the total number of entries for this message when there is less * than enough space for one more maximum-sized entry. @@ -9384,8 +10386,8 @@ static int fxp_handle_readdir(struct fxp_packet *fxp) { pr_response_clear(&resp_err_list); resp = fxp_packet_create(fxp->pool, fxp->channel_id); - resp->payload = ptr; - resp->payload_sz = (bufsz - buflen); + resp->payload = fxb->ptr; + resp->payload_sz = (fxb->bufsz - buflen); return fxp_packet_write(resp); } @@ -9405,8 +10407,8 @@ static int fxp_handle_readdir(struct fxp_packet *fxp) { pr_response_clear(&resp_list); resp = fxp_packet_create(fxp->pool, fxp->channel_id); - resp->payload = ptr; - resp->payload_sz = (bufsz - buflen); + resp->payload = fxb->ptr; + resp->payload_sz = (fxb->bufsz - buflen); return fxp_packet_write(resp); } @@ -9418,24 +10420,47 @@ static int fxp_handle_readdir(struct fxp_packet *fxp) { sftp_msg_write_int(&buf, &buflen, fxp->request_id); sftp_msg_write_int(&buf, &buflen, path_list->nelts); + fxb->buf = buf; + fxb->buflen = buflen; paths = path_list->elts; + + /* For READDIR requests, since they do NOT contain a flags field for clients + * to express which attributes they want, we ASSUME some standard fields. + */ + attr_flags = SSH2_FX_ATTR_SIZE|SSH2_FX_ATTR_PERMISSIONS| + SSH2_FX_ATTR_ACCESSTIME|SSH2_FX_ATTR_MODIFYTIME|SSH2_FX_ATTR_OWNERGROUP; + + if (fxp_session->client_version >= 3) { +#ifdef PR_USE_XATTR + attr_flags |= SSH2_FX_ATTR_EXTENDED; +#endif /* PR_USE_XATTR */ + } + + if (fxp_session->client_version >= 6) { + attr_flags |= SSH2_FX_ATTR_LINK_COUNT; + } + for (i = 0; i < path_list->nelts; i++) { uint32_t name_len = 0; - name_len = fxp_name_write(fxp->pool, &buf, &buflen, paths[i]->client_path, - paths[i]->st, fake_user, fake_group); + name_len = fxp_name_write(fxp->pool, fxb, paths[i]->client_path, + paths[i]->st, attr_flags, fake_user, fake_group); pr_trace_msg(trace_channel, 19, "READDIR: FXP_NAME entry size: %lu bytes", (unsigned long) name_len); } + /* fxp_name_write will have changed the values stashed in the buffer. */ + buf = fxb->buf; + buflen = fxb->buflen; + if (fxp_session->client_version > 5) { sftp_msg_write_bool(&buf, &buflen, have_eod ? TRUE : FALSE); } resp = fxp_packet_create(fxp->pool, fxp->channel_id); - resp->payload = ptr; - resp->payload_sz = (bufsz - buflen); + resp->payload = fxb->ptr; + resp->payload_sz = (fxb->bufsz - buflen); session.xfer.total_bytes += resp->payload_sz; session.total_bytes += resp->payload_sz; @@ -9449,10 +10474,11 @@ static int fxp_handle_readdir(struct fxp_packet *fxp) { static int fxp_handle_readlink(struct fxp_packet *fxp) { char data[PR_TUNABLE_PATH_MAX + 1]; - unsigned char *buf, *ptr; + unsigned char *buf; char *path, *resolved_path; int res; - uint32_t buflen, bufsz; + uint32_t buflen; + struct fxp_buffer *fxb; struct fxp_packet *resp; cmd_rec *cmd; @@ -9482,8 +10508,9 @@ static int fxp_handle_readlink(struct fxp_packet *fxp) { cmd = fxp_cmd_alloc(fxp->pool, "READLINK", path); cmd->cmd_class = CL_READ|CL_SFTP; - buflen = bufsz = FXP_RESPONSE_DATA_DEFAULT_SZ; - buf = ptr = palloc(fxp->pool, bufsz); + fxb = pcalloc(fxp->pool, sizeof(struct fxp_buffer)); + fxb->bufsz = buflen = FXP_RESPONSE_NAME_DEFAULT_SZ; + fxb->ptr = buf = palloc(fxp->pool, fxb->bufsz); if (pr_cmd_dispatch_phase(cmd, PRE_CMD, 0) < 0) { uint32_t status_code = SSH2_FX_PERMISSION_DENIED; @@ -9502,8 +10529,8 @@ static int fxp_handle_readlink(struct fxp_packet *fxp) { pr_response_clear(&resp_err_list); resp = fxp_packet_create(fxp->pool, fxp->channel_id); - resp->payload = ptr; - resp->payload_sz = (bufsz - buflen); + resp->payload = fxb->ptr; + resp->payload_sz = (fxb->bufsz - buflen); return fxp_packet_write(resp); } @@ -9535,8 +10562,8 @@ static int fxp_handle_readlink(struct fxp_packet *fxp) { pr_response_clear(&resp_err_list); resp = fxp_packet_create(fxp->pool, fxp->channel_id); - resp->payload = ptr; - resp->payload_sz = (bufsz - buflen); + resp->payload = fxb->ptr; + resp->payload_sz = (fxb->bufsz - buflen); return fxp_packet_write(resp); } @@ -9559,8 +10586,8 @@ static int fxp_handle_readlink(struct fxp_packet *fxp) { pr_response_clear(&resp_err_list); resp = fxp_packet_create(fxp->pool, fxp->channel_id); - resp->payload = ptr; - resp->payload_sz = (bufsz - buflen); + resp->payload = fxb->ptr; + resp->payload_sz = (fxb->bufsz - buflen); return fxp_packet_write(resp); } @@ -9578,8 +10605,8 @@ static int fxp_handle_readlink(struct fxp_packet *fxp) { const char *reason; int xerrno = errno; - buf = ptr; - buflen = bufsz; + buf = fxb->ptr; + buflen = fxb->bufsz; status_code = fxp_errno2status(xerrno, &reason); @@ -9628,7 +10655,13 @@ static int fxp_handle_readlink(struct fxp_packet *fxp) { fake_group = session.group; } - fxp_name_write(fxp->pool, &buf, &buflen, data, &st, fake_user, fake_group); + fxb->buf = buf; + fxb->buflen = buflen; + + fxp_name_write(fxp->pool, fxb, data, &st, 0, fake_user, fake_group); + + buf = fxb->buf; + buflen = fxb->buflen; pr_cmd_dispatch_phase(cmd, POST_CMD, 0); pr_cmd_dispatch_phase(cmd, LOG_CMD, 0); @@ -9636,13 +10669,14 @@ static int fxp_handle_readlink(struct fxp_packet *fxp) { } resp = fxp_packet_create(fxp->pool, fxp->channel_id); - resp->payload = ptr; - resp->payload_sz = (bufsz - buflen); + resp->payload = fxb->ptr; + resp->payload_sz = (fxb->bufsz - buflen); return fxp_packet_write(resp); } -static void fxp_trace_v6_realpath_flags(pool *p, unsigned char flags) { +static void fxp_trace_v6_realpath_flags(pool *p, unsigned char flags, + int client_sent) { char *flags_str = ""; int trace_level = 15; @@ -9664,15 +10698,17 @@ static void fxp_trace_v6_realpath_flags(pool *p, unsigned char flags) { break; } - pr_trace_msg(trace_channel, trace_level, "REALPATH flags = %s", flags_str); + pr_trace_msg(trace_channel, trace_level, "REALPATH flags = %s (%s)", + flags_str, client_sent == TRUE ? "explicit" : "default"); } static int fxp_handle_realpath(struct fxp_packet *fxp) { int res, xerrno; - unsigned char *buf, *ptr, realpath_flags = 0; + unsigned char *buf, realpath_flags = 0; char *path; - uint32_t buflen, bufsz; + uint32_t buflen; struct stat st; + struct fxp_buffer *fxb; struct fxp_packet *resp; cmd_rec *cmd; @@ -9716,7 +10752,7 @@ static int fxp_handle_realpath(struct fxp_packet *fxp) { realpath_flags = sftp_msg_read_byte(fxp->pool, &fxp->payload, &fxp->payload_sz); - fxp_trace_v6_realpath_flags(fxp->pool, realpath_flags); + fxp_trace_v6_realpath_flags(fxp->pool, realpath_flags, TRUE); if (fxp->payload_sz > 0) { composite_path = sftp_msg_read_string(fxp->pool, &fxp->payload, @@ -9733,11 +10769,15 @@ static int fxp_handle_realpath(struct fxp_packet *fxp) { pr_trace_msg(trace_channel, 13, "REALPATH request set composite-path: '%s'", composite_path); } + + } else { + fxp_trace_v6_realpath_flags(fxp->pool, realpath_flags, FALSE); } } - buflen = bufsz = PR_TUNABLE_PATH_MAX + 32; - buf = ptr = palloc(fxp->pool, bufsz); + fxb = pcalloc(fxp->pool, sizeof(struct fxp_buffer)); + fxb->bufsz = buflen = FXP_RESPONSE_NAME_DEFAULT_SZ; + fxb->ptr = buf = palloc(fxp->pool, fxb->bufsz); res = pr_cmd_dispatch_phase(cmd, PRE_CMD, 0); if (res < 0) { @@ -9768,8 +10808,14 @@ static int fxp_handle_realpath(struct fxp_packet *fxp) { sftp_msg_write_byte(&buf, &buflen, SFTP_SSH2_FXP_NAME); sftp_msg_write_int(&buf, &buflen, fxp->request_id); sftp_msg_write_int(&buf, &buflen, 1); - fxp_name_write(fxp->pool, &buf, &buflen, path, &st, "nobody", - "nobody"); + + fxb->buf = buf; + fxb->buflen = buflen; + + fxp_name_write(fxp->pool, fxb, path, &st, 0, "nobody", "nobody"); + + buf = fxb->buf; + buflen = fxb->buflen; } pr_cmd_dispatch_phase(cmd, POST_CMD_ERR, 0); @@ -9777,8 +10823,8 @@ static int fxp_handle_realpath(struct fxp_packet *fxp) { pr_response_clear(&resp_err_list); resp = fxp_packet_create(fxp->pool, fxp->channel_id); - resp->payload = ptr; - resp->payload_sz = (bufsz - buflen); + resp->payload = fxb->ptr; + resp->payload_sz = (fxb->bufsz - buflen); return fxp_packet_write(resp); } @@ -9828,8 +10874,14 @@ static int fxp_handle_realpath(struct fxp_packet *fxp) { sftp_msg_write_byte(&buf, &buflen, SFTP_SSH2_FXP_NAME); sftp_msg_write_int(&buf, &buflen, fxp->request_id); sftp_msg_write_int(&buf, &buflen, 1); - fxp_name_write(fxp->pool, &buf, &buflen, path, &st, "nobody", - "nobody"); + + fxb->buf = buf; + fxb->buflen = buflen; + + fxp_name_write(fxp->pool, fxb, path, &st, 0, "nobody", "nobody"); + + buf = fxb->buf; + buflen = fxb->buflen; } pr_cmd_dispatch_phase(cmd, POST_CMD_ERR, 0); @@ -9837,8 +10889,8 @@ static int fxp_handle_realpath(struct fxp_packet *fxp) { pr_response_clear(&resp_err_list); resp = fxp_packet_create(fxp->pool, fxp->channel_id); - resp->payload = ptr; - resp->payload_sz = (bufsz - buflen); + resp->payload = fxb->ptr; + resp->payload_sz = (fxb->bufsz - buflen); return fxp_packet_write(resp); } @@ -9861,8 +10913,8 @@ static int fxp_handle_realpath(struct fxp_packet *fxp) { (void) pr_log_writefile(sftp_logfd, MOD_SFTP_VERSION, "REALPATH of '%s' blocked by configuration", path); - buf = ptr; - buflen = bufsz; + buf = fxb->ptr; + buflen = fxb->bufsz; status_code = fxp_errno2status(xerrno, &reason); @@ -9890,8 +10942,14 @@ static int fxp_handle_realpath(struct fxp_packet *fxp) { sftp_msg_write_byte(&buf, &buflen, SFTP_SSH2_FXP_NAME); sftp_msg_write_int(&buf, &buflen, fxp->request_id); sftp_msg_write_int(&buf, &buflen, 1); - fxp_name_write(fxp->pool, &buf, &buflen, path, &st, "nobody", - "nobody"); + + fxb->buf = buf; + fxb->buflen = buflen; + + fxp_name_write(fxp->pool, fxb, path, &st, 0, "nobody", "nobody"); + + buf = fxb->buf; + buflen = fxb->buflen; } pr_cmd_dispatch_phase(cmd, POST_CMD_ERR, 0); @@ -9935,8 +10993,8 @@ static int fxp_handle_realpath(struct fxp_packet *fxp) { (void) pr_log_writefile(sftp_logfd, MOD_SFTP_VERSION, "error checking '%s' for REALPATH: %s", path, strerror(xerrno)); - buf = ptr; - buflen = bufsz; + buf = fxb->ptr; + buflen = fxb->bufsz; status_code = fxp_errno2status(xerrno, &reason); @@ -9964,8 +11022,14 @@ static int fxp_handle_realpath(struct fxp_packet *fxp) { sftp_msg_write_byte(&buf, &buflen, SFTP_SSH2_FXP_NAME); sftp_msg_write_int(&buf, &buflen, fxp->request_id); sftp_msg_write_int(&buf, &buflen, 1); - fxp_name_write(fxp->pool, &buf, &buflen, path, &st, "nobody", - "nobody"); + + fxb->buf = buf; + fxb->buflen = buflen; + + fxp_name_write(fxp->pool, fxb, path, &st, 0, "nobody", "nobody"); + + buf = fxb->buf; + buflen = fxb->buflen; } pr_cmd_dispatch_phase(cmd, POST_CMD_ERR, 0); @@ -9996,8 +11060,13 @@ static int fxp_handle_realpath(struct fxp_packet *fxp) { fake_group = session.group; } - fxp_name_write(fxp->pool, &buf, &buflen, path, &st, fake_user, - fake_group); + fxb->buf = buf; + fxb->buflen = buflen; + + fxp_name_write(fxp->pool, fxb, path, &st, 0, fake_user, fake_group); + + buf = fxb->buf; + buflen = fxb->buflen; pr_cmd_dispatch_phase(cmd, POST_CMD, 0); pr_cmd_dispatch_phase(cmd, LOG_CMD, 0); @@ -10006,8 +11075,8 @@ static int fxp_handle_realpath(struct fxp_packet *fxp) { } resp = fxp_packet_create(fxp->pool, fxp->channel_id); - resp->payload = ptr; - resp->payload_sz = (bufsz - buflen); + resp->payload = fxb->ptr; + resp->payload_sz = (fxb->bufsz - buflen); return fxp_packet_write(resp); } @@ -11108,6 +12177,7 @@ static int fxp_handle_setstat(struct fxp_packet *fxp) { struct fxp_packet *resp; cmd_rec *cmd; struct stat st; + array_header *xattrs = NULL; path = sftp_msg_read_string(fxp->pool, &fxp->payload, &fxp->payload_sz); if (fxp_session->client_version >= fxp_utf8_protocol_version) { @@ -11119,7 +12189,8 @@ static int fxp_handle_setstat(struct fxp_packet *fxp) { pr_scoreboard_entry_update(session.pid, PR_SCORE_CMD_ARG, "%s", path, NULL, NULL); - attrs = fxp_attrs_read(fxp, &fxp->payload, &fxp->payload_sz, &attr_flags); + attrs = fxp_attrs_read(fxp, &fxp->payload, &fxp->payload_sz, &attr_flags, + &xattrs); if (attrs == NULL) { return 0; } @@ -11254,6 +12325,17 @@ static int fxp_handle_setstat(struct fxp_packet *fxp) { attr_flags &= ~SSH2_FX_ATTR_OWNERGROUP; } + /* If the SFTPOption for ignoring the xattrs for SFTP setstat requests is set, + * handle it by clearing the SSH2_FX_ATTR_EXTENDED flag. + */ + if ((sftp_opts & SFTP_OPT_IGNORE_SFTP_SET_XATTRS) && + (attr_flags & SSH2_FX_ATTR_EXTENDED)) { + pr_trace_msg(trace_channel, 7, + "SFTPOption 'IgnoreSFTPSetExtendedAttributes' configured, ignoring " + "xattrs sent by client"); + attr_flags &= ~SSH2_FX_ATTR_EXTENDED; + } + /* If the SFTPOption for ignoring the perms for SFTP setstat requests is set, * handle it by clearing the SSH2_FX_ATTR_PERMISSIONS flag. */ @@ -11277,7 +12359,8 @@ static int fxp_handle_setstat(struct fxp_packet *fxp) { } } - res = fxp_attrs_set(NULL, path, attrs, attr_flags, &buf, &buflen, fxp); + res = fxp_attrs_set(NULL, path, attrs, attr_flags, xattrs, &buf, &buflen, + fxp); if (res < 0) { pr_cmd_dispatch_phase(cmd, POST_CMD_ERR, 0); pr_cmd_dispatch_phase(cmd, LOG_CMD_ERR, 0); @@ -11310,10 +12393,11 @@ static int fxp_handle_setstat(struct fxp_packet *fxp) { } static int fxp_handle_stat(struct fxp_packet *fxp) { - unsigned char *buf, *ptr; + unsigned char *buf; char *cmd_name, *path; - uint32_t buflen, bufsz; + uint32_t attr_flags, buflen; struct stat st; + struct fxp_buffer *fxb; struct fxp_packet *resp; cmd_rec *cmd; const char *fake_user = NULL, *fake_group = NULL; @@ -11331,11 +12415,6 @@ static int fxp_handle_stat(struct fxp_packet *fxp) { pr_proctitle_set("%s - %s: STAT %s", session.user, session.proc_prefix, path); if (fxp_session->client_version > 3) { - uint32_t attr_flags; - - /* These are hints from the client about what file attributes are - * of particular interest. We do not currently honor them. - */ attr_flags = sftp_msg_read_int(fxp->pool, &fxp->payload, &fxp->payload_sz); pr_trace_msg(trace_channel, 7, "received request: STAT %s %s", path, @@ -11343,6 +12422,11 @@ static int fxp_handle_stat(struct fxp_packet *fxp) { } else { pr_trace_msg(trace_channel, 7, "received request: STAT %s", path); + attr_flags = SSH2_FX_ATTR_SIZE|SSH2_FX_ATTR_UIDGID|SSH2_FX_ATTR_PERMISSIONS| + SSH2_FX_ATTR_ACMODTIME; +#ifdef PR_USE_XATTR + attr_flags |= SSH2_FX_ATTR_EXTENDED; +#endif /* PR_USE_XATTR */ } if (strlen(path) == 0) { @@ -11356,8 +12440,9 @@ static int fxp_handle_stat(struct fxp_packet *fxp) { cmd = fxp_cmd_alloc(fxp->pool, "STAT", path); cmd->cmd_class = CL_READ|CL_SFTP; - buflen = bufsz = FXP_RESPONSE_DATA_DEFAULT_SZ; - buf = ptr = palloc(fxp->pool, bufsz); + fxb = pcalloc(fxp->pool, sizeof(struct fxp_buffer)); + fxb->bufsz = buflen = FXP_RESPONSE_NAME_DEFAULT_SZ; + fxb->ptr = buf = palloc(fxp->pool, fxb->bufsz); if (pr_cmd_dispatch_phase(cmd, PRE_CMD, 0) < 0) { uint32_t status_code = SSH2_FX_PERMISSION_DENIED; @@ -11376,8 +12461,8 @@ static int fxp_handle_stat(struct fxp_packet *fxp) { pr_response_clear(&resp_err_list); resp = fxp_packet_create(fxp->pool, fxp->channel_id); - resp->payload = ptr; - resp->payload_sz = (bufsz - buflen); + resp->payload = fxb->ptr; + resp->payload_sz = (fxb->bufsz - buflen); return fxp_packet_write(resp); } @@ -11424,8 +12509,8 @@ static int fxp_handle_stat(struct fxp_packet *fxp) { pr_response_clear(&resp_err_list); resp = fxp_packet_create(fxp->pool, fxp->channel_id); - resp->payload = ptr; - resp->payload_sz = (bufsz - buflen); + resp->payload = fxb->ptr; + resp->payload_sz = (fxb->bufsz - buflen); return fxp_packet_write(resp); } @@ -11452,8 +12537,8 @@ static int fxp_handle_stat(struct fxp_packet *fxp) { pr_response_clear(&resp_err_list); resp = fxp_packet_create(fxp->pool, fxp->channel_id); - resp->payload = ptr; - resp->payload_sz = (bufsz - buflen); + resp->payload = fxb->ptr; + resp->payload_sz = (fxb->bufsz - buflen); return fxp_packet_write(resp); } @@ -11484,8 +12569,8 @@ static int fxp_handle_stat(struct fxp_packet *fxp) { pr_response_clear(&resp_err_list); resp = fxp_packet_create(fxp->pool, fxp->channel_id); - resp->payload = ptr; - resp->payload_sz = (bufsz - buflen); + resp->payload = fxb->ptr; + resp->payload_sz = (fxb->bufsz - buflen); return fxp_packet_write(resp); } @@ -11510,15 +12595,21 @@ static int fxp_handle_stat(struct fxp_packet *fxp) { fake_group = session.group; } - fxp_attrs_write(fxp->pool, &buf, &buflen, &st, fake_user, fake_group); + fxb->buf = buf; + fxb->buflen = buflen; + + fxp_attrs_write(fxp->pool, fxb, path, &st, attr_flags, fake_user, fake_group); + + buf = fxb->buf; + buflen = fxb->buflen; pr_cmd_dispatch_phase(cmd, POST_CMD, 0); pr_cmd_dispatch_phase(cmd, LOG_CMD, 0); pr_response_clear(&resp_list); resp = fxp_packet_create(fxp->pool, fxp->channel_id); - resp->payload = ptr; - resp->payload_sz = (bufsz - buflen); + resp->payload = fxb->ptr; + resp->payload_sz = (fxb->bufsz - buflen); return fxp_packet_write(resp); } diff --git a/contrib/mod_sftp/fxp.h b/contrib/mod_sftp/fxp.h index 34d4cf3a92..254a48e7ea 100644 --- a/contrib/mod_sftp/fxp.h +++ b/contrib/mod_sftp/fxp.h @@ -73,6 +73,7 @@ #define SFTP_FXP_EXT_SPACE_AVAIL 0x0040 #define SFTP_FXP_EXT_FSYNC 0x0080 #define SFTP_FXP_EXT_HARDLINK 0x0100 +#define SFTP_FXP_EXT_XATTR 0x0200 #define SFTP_FXP_EXT_DEFAULT \ (SFTP_FXP_EXT_CHECK_FILE|SFTP_FXP_EXT_COPY_FILE|SFTP_FXP_EXT_VERSION_SELECT|SFTP_FXP_EXT_POSIX_RENAME|SFTP_FXP_EXT_SPACE_AVAIL|SFTP_FXP_EXT_STATVFS|SFTP_FXP_EXT_FSYNC|SFTP_FXP_EXT_HARDLINK) diff --git a/contrib/mod_sftp/kex.c b/contrib/mod_sftp/kex.c index 6b399f7211..86c13abd6d 100644 --- a/contrib/mod_sftp/kex.c +++ b/contrib/mod_sftp/kex.c @@ -36,6 +36,7 @@ #include "disconnect.h" #include "interop.h" #include "tap.h" +#include "misc.h" #ifdef PR_USE_SODIUM # include @@ -1124,44 +1125,6 @@ static int finish_ecdh(struct sftp_kex *kex) { } #endif /* PR_USE_OPENSSL_ECC */ -static array_header *parse_namelist(pool *p, const char *names) { - char *ptr; - array_header *list; - size_t names_len; - - list = make_array(p, 0, sizeof(const char *)); - - names_len = strlen(names); - if (names_len == 0) { - return list; - } - - ptr = memchr(names, ',', names_len); - while (ptr != NULL) { - char *elt; - size_t elt_len; - - pr_signals_handle(); - - elt_len = ptr - names; - - elt = palloc(p, elt_len + 1); - memcpy(elt, names, elt_len); - elt[elt_len] = '\0'; - - *((const char **) push_array(list)) = elt; - names = ++ptr; - - /* Add one for the ',' character we skipped over. */ - names_len -= (elt_len + 1); - - ptr = memchr(names, ',', names_len); - } - *((const char **) push_array(list)) = pstrdup(p, names); - - return list; -} - /* Given a name-list, return the first (i.e. preferred) name in the list. */ static const char *get_preferred_name(pool *p, const char *names) { register unsigned int i; @@ -1185,45 +1148,6 @@ static const char *get_preferred_name(pool *p, const char *names) { return NULL; } -/* Given name-lists from the client and server, find the first name from the - * client list which appears on the server list. - */ -static const char *get_shared_name(pool *p, const char *c2s_names, - const char *s2c_names) { - register unsigned int i; - const char *name = NULL, **client_names, **server_names; - pool *tmp_pool; - array_header *client_list, *server_list; - - tmp_pool = make_sub_pool(p); - pr_pool_tag(tmp_pool, "SSH2 session shared name pool"); - - client_list = parse_namelist(tmp_pool, c2s_names); - client_names = (const char **) client_list->elts; - - server_list = parse_namelist(tmp_pool, s2c_names); - server_names = (const char **) server_list->elts; - - for (i = 0; i < client_list->nelts; i++) { - register unsigned int j; - - if (name) - break; - - for (j = 0; j < server_list->nelts; j++) { - if (strcmp(client_names[i], server_names[j]) == 0) { - name = client_names[i]; - break; - } - } - } - - name = pstrdup(p, name); - destroy_pool(tmp_pool); - - return name; -} - /* Note that in this default list of key exchange algorithms, one of the * REQUIRED algorithms is conspicuously absent: * @@ -1766,7 +1690,7 @@ static int get_session_names(struct sftp_kex *kex, int *correct_guess) { } } - kex_algo = get_shared_name(kex->pool, client_list, server_list); + kex_algo = sftp_misc_namelist_shared(kex->pool, client_list, server_list); if (kex_algo != NULL) { /* Unlike the following algorithms, we wait to setup the chosen kex algo * until the end. Why? The kex algo setup may require knowledge of the @@ -1793,7 +1717,7 @@ static int get_session_names(struct sftp_kex *kex, int *correct_guess) { pr_trace_msg(trace_channel, 8, "server-sent host key algorithms: %s", server_list); - shared = get_shared_name(kex->pool, client_list, server_list); + shared = sftp_misc_namelist_shared(kex->pool, client_list, server_list); if (shared) { if (setup_hostkey_algo(kex, shared) < 0) { destroy_pool(tmp_pool); @@ -1821,7 +1745,7 @@ static int get_session_names(struct sftp_kex *kex, int *correct_guess) { pr_trace_msg(trace_channel, 8, "server-sent client encryption algorithms: %s", server_list); - shared = get_shared_name(kex->pool, client_list, server_list); + shared = sftp_misc_namelist_shared(kex->pool, client_list, server_list); if (shared) { if (setup_c2s_encrypt_algo(kex, shared) < 0) { destroy_pool(tmp_pool); @@ -1849,7 +1773,7 @@ static int get_session_names(struct sftp_kex *kex, int *correct_guess) { pr_trace_msg(trace_channel, 8, "server-sent server encryption algorithms: %s", server_list); - shared = get_shared_name(kex->pool, client_list, server_list); + shared = sftp_misc_namelist_shared(kex->pool, client_list, server_list); if (shared) { if (setup_s2c_encrypt_algo(kex, shared) < 0) { destroy_pool(tmp_pool); @@ -1877,7 +1801,7 @@ static int get_session_names(struct sftp_kex *kex, int *correct_guess) { pr_trace_msg(trace_channel, 8, "server-sent client MAC algorithms: %s", server_list); - shared = get_shared_name(kex->pool, client_list, server_list); + shared = sftp_misc_namelist_shared(kex->pool, client_list, server_list); if (shared) { if (setup_c2s_mac_algo(kex, shared) < 0) { destroy_pool(tmp_pool); @@ -1905,7 +1829,7 @@ static int get_session_names(struct sftp_kex *kex, int *correct_guess) { pr_trace_msg(trace_channel, 8, "server-sent server MAC algorithms: %s", server_list); - shared = get_shared_name(kex->pool, client_list, server_list); + shared = sftp_misc_namelist_shared(kex->pool, client_list, server_list); if (shared) { if (setup_s2c_mac_algo(kex, shared) < 0) { destroy_pool(tmp_pool); @@ -1933,7 +1857,7 @@ static int get_session_names(struct sftp_kex *kex, int *correct_guess) { pr_trace_msg(trace_channel, 8, "server-sent client compression algorithms: %s", server_list); - shared = get_shared_name(kex->pool, client_list, server_list); + shared = sftp_misc_namelist_shared(kex->pool, client_list, server_list); if (shared) { if (setup_c2s_comp_algo(kex, shared) < 0) { destroy_pool(tmp_pool); @@ -1961,7 +1885,7 @@ static int get_session_names(struct sftp_kex *kex, int *correct_guess) { pr_trace_msg(trace_channel, 8, "server-sent server compression algorithms: %s", server_list); - shared = get_shared_name(kex->pool, client_list, server_list); + shared = sftp_misc_namelist_shared(kex->pool, client_list, server_list); if (shared) { if (setup_s2c_comp_algo(kex, shared) < 0) { destroy_pool(tmp_pool); @@ -1989,7 +1913,7 @@ static int get_session_names(struct sftp_kex *kex, int *correct_guess) { pr_trace_msg(trace_channel, 8, "server-sent client languages: %s", client_list); - shared = get_shared_name(kex->pool, client_list, server_list); + shared = sftp_misc_namelist_shared(kex->pool, client_list, server_list); if (shared) { if (setup_c2s_lang(kex, shared) < 0) { destroy_pool(tmp_pool); @@ -2020,7 +1944,7 @@ static int get_session_names(struct sftp_kex *kex, int *correct_guess) { pr_trace_msg(trace_channel, 8, "server-sent server languages: %s", client_list); - shared = get_shared_name(kex->pool, client_list, server_list); + shared = sftp_misc_namelist_shared(kex->pool, client_list, server_list); if (shared) { if (setup_s2c_lang(kex, shared) < 0) { destroy_pool(tmp_pool); diff --git a/contrib/mod_sftp/misc.c b/contrib/mod_sftp/misc.c index 6564945432..97afa2f4ba 100644 --- a/contrib/mod_sftp/misc.c +++ b/contrib/mod_sftp/misc.c @@ -1,6 +1,6 @@ /* * ProFTPD - mod_sftp miscellaneous - * Copyright (c) 2010-2015 TJ Saunders + * Copyright (c) 2010-2016 TJ Saunders * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -286,3 +286,76 @@ int sftp_misc_chown_path(pool *p, const char *path) { return 0; } + +static array_header *parse_namelist(pool *p, const char *names) { + char *ptr; + array_header *list; + size_t names_len; + + list = make_array(p, 0, sizeof(const char *)); + names_len = strlen(names); + + if (names_len == 0) { + return list; + } + + ptr = memchr(names, ',', names_len); + while (ptr != NULL) { + char *elt; + size_t elt_len; + + pr_signals_handle(); + + elt_len = ptr - names; + + elt = palloc(p, elt_len + 1); + memcpy(elt, names, elt_len); + elt[elt_len] = '\0'; + + *((const char **) push_array(list)) = elt; + names = ++ptr; + names_len -= elt_len; + + ptr = memchr(names, ',', names_len); + } + *((const char **) push_array(list)) = pstrdup(p, names); + + return list; +} + +const char *sftp_misc_namelist_shared(pool *p, const char *c2s_names, + const char *s2c_names) { + register unsigned int i; + const char *name = NULL, **client_names, **server_names; + pool *tmp_pool; + array_header *client_list, *server_list; + + tmp_pool = make_sub_pool(p); + pr_pool_tag(tmp_pool, "Share name pool"); + + client_list = parse_namelist(tmp_pool, c2s_names); + client_names = (const char **) client_list->elts; + + server_list = parse_namelist(tmp_pool, s2c_names); + server_names = (const char **) server_list->elts; + + for (i = 0; i < client_list->nelts; i++) { + register unsigned int j; + + if (name != NULL) { + break; + } + + for (j = 0; j < server_list->nelts; j++) { + if (strcmp(client_names[i], server_names[j]) == 0) { + name = client_names[i]; + break; + } + } + } + + name = pstrdup(p, name); + destroy_pool(tmp_pool); + + return name; +} diff --git a/contrib/mod_sftp/misc.h b/contrib/mod_sftp/misc.h index 65a271c910..4b761a3561 100644 --- a/contrib/mod_sftp/misc.h +++ b/contrib/mod_sftp/misc.h @@ -1,6 +1,6 @@ /* * ProFTPD - mod_sftp miscellaneous - * Copyright (c) 2010-2015 TJ Saunders + * Copyright (c) 2010-2016 TJ Saunders * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -29,5 +29,6 @@ int sftp_misc_chown_file(pool *, pr_fh_t *); int sftp_misc_chown_path(pool *, const char *); +const char *sftp_misc_namelist_shared(pool *, const char *, const char *); #endif /* MOD_SFTP_MISC_H */ diff --git a/contrib/mod_sftp/mod_sftp.c b/contrib/mod_sftp/mod_sftp.c index 8fee2c3de4..83b4077889 100644 --- a/contrib/mod_sftp/mod_sftp.c +++ b/contrib/mod_sftp/mod_sftp.c @@ -1124,6 +1124,23 @@ MODRET set_sftpextensions(cmd_rec *cmd) { break; } + } else if (strncasecmp(ext, "xattr", 8) == 0) { +#ifdef HAVE_SYS_XATTR_H + switch (action) { + case '-': + ext_flags &= ~SFTP_FXP_EXT_XATTR; + break; + + case '+': + ext_flags |= SFTP_FXP_EXT_XATTR; + break; + } +#else + pr_log_debug(DEBUG0, "%s: xattr@proftpd.org extension not supported " + "on this system; requires extended attribute support", + (char *) cmd->argv[0]); +#endif /* HAVE_SYS_XATTR_H */ + } else { CONF_ERROR(cmd, pstrcat(cmd->tmp_pool, "unknown extension: '", ext, "'", NULL)); @@ -1419,18 +1436,25 @@ MODRET set_sftpoptions(cmd_rec *cmd) { } else if (strncmp(cmd->argv[i], "MatchKeySubject", 16) == 0) { opts |= SFTP_OPT_MATCH_KEY_SUBJECT; - } else if (strcmp(cmd->argv[1], "AllowInsecureLogin") == 0) { + } else if (strcmp(cmd->argv[i], "AllowInsecureLogin") == 0) { opts |= SFTP_OPT_ALLOW_INSECURE_LOGIN; - } else if (strcmp(cmd->argv[1], "InsecureHostKeyPerms") == 0) { + } else if (strcmp(cmd->argv[i], "InsecureHostKeyPerms") == 0) { opts |= SFTP_OPT_INSECURE_HOSTKEY_PERMS; - } else if (strcmp(cmd->argv[1], "AllowWeakDH") == 0) { + } else if (strcmp(cmd->argv[i], "AllowWeakDH") == 0) { opts |= SFTP_OPT_ALLOW_WEAK_DH; } else if (strcmp(cmd->argv[i], "IgnoreFIFOs") == 0) { opts |= SFTP_OPT_IGNORE_FIFOS; + } else if (strcmp(cmd->argv[i], + "IgnoreSFTPUploadExtendedAttributes") == 0) { + opts |= SFTP_OPT_IGNORE_SFTP_UPLOAD_XATTRS; + + } else if (strcmp(cmd->argv[i], "IgnoreSFTPSetExtendedAttributes") == 0) { + opts |= SFTP_OPT_IGNORE_SFTP_SET_XATTRS; + } else { CONF_ERROR(cmd, pstrcat(cmd->tmp_pool, ": unknown SFTPOption '", cmd->argv[i], "'", NULL)); diff --git a/contrib/mod_sftp/mod_sftp.h.in b/contrib/mod_sftp/mod_sftp.h.in index f8484b15cf..dc569acf2a 100644 --- a/contrib/mod_sftp/mod_sftp.h.in +++ b/contrib/mod_sftp/mod_sftp.h.in @@ -114,6 +114,8 @@ #define SFTP_OPT_INSECURE_HOSTKEY_PERMS 0x0400 #define SFTP_OPT_ALLOW_WEAK_DH 0x0800 #define SFTP_OPT_IGNORE_FIFOS 0x1000 +#define SFTP_OPT_IGNORE_SFTP_UPLOAD_XATTRS 0x2000 +#define SFTP_OPT_IGNORE_SFTP_SET_XATTRS 0x2000 /* mod_sftp service flags */ #define SFTP_SERVICE_FL_SFTP 0x0001 diff --git a/contrib/mod_sftp/msg.h b/contrib/mod_sftp/msg.h index b74d618a0d..5bc71d4357 100644 --- a/contrib/mod_sftp/msg.h +++ b/contrib/mod_sftp/msg.h @@ -1,6 +1,6 @@ /* * ProFTPD - mod_sftp message format - * Copyright (c) 2008-2013 TJ Saunders + * Copyright (c) 2008-2016 TJ Saunders * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -20,8 +20,6 @@ * give permission to link this program with OpenSSL, and distribute the * resulting executable, without including the source code for OpenSSL in the * source distribution. - * - * $Id: msg.h,v 1.7 2013-03-28 19:56:13 castaglia Exp $ */ #include "mod_sftp.h" diff --git a/doc/contrib/mod_sftp.html b/doc/contrib/mod_sftp.html index 7979d5e350..196bf73ebf 100644 --- a/doc/contrib/mod_sftp.html +++ b/doc/contrib/mod_sftp.html @@ -698,8 +698,10 @@

SFTPExtensions

  • spaceAvailable
  • statvfs
  • hardlink +
  • xattr -All extensions except vendorID are enabled by default. +All extensions except vendorID and +xattr are enabled by default.

    To enable an extension, preface the extension name with a '+' (plus) character; @@ -993,6 +995,18 @@

    SFTPOptions

    proftpd-1.3.5rc4.
  • +

    +

  • IgnoreSFTPSetExtendedAttributes
    +

    + Use this option to have mod_sftp silently ignore any + extended attributes sent by SFTP clients via the SETSTAT or + FSETSTAT SFTP requests. + +

    + Note that this option first appeared in + proftpd-1.3.6rc3. +

  • +

  • IgnoreSFTPSetOwners

    @@ -1032,6 +1046,21 @@

    SFTPOptions

    proftpd-1.3.4rc4.
  • +

    +

  • IgnoreSFTPUploadExtendedAttributes
    +

    + When an SFTP client uploads a file or creates a directory, the desired + extended attributes ("xattrs") on the path are sent to the server as part + of the upload. (This is different from FTP, which does not include + the file attributes in an upload.) If you need more FTP-like functionality + for any reason and wish to have mod_sftp silently ignore any + extended attributes sent by the SFTP client, use this option. + +

    + Note that this option first appeared in + proftpd-1.3.6rc3. +

  • +

  • IgnoreSFTPUploadPerms

    diff --git a/doc/modules/mod_core.html b/doc/modules/mod_core.html index eb2fc523e2..bc0d400981 100644 --- a/doc/modules/mod_core.html +++ b/doc/modules/mod_core.html @@ -27,6 +27,7 @@

    Directives

  • DisplayConnect
  • DisplayQuit
  • FSCachePolicy +
  • FSOptions
  • GroupOwner
  • HideGroup
  • HideNoAccess @@ -451,6 +452,28 @@

    FSCachePolicy

    FSCachePolicy size 128 maxAge 120 +
    +

    FSOptions

    +Syntax: FSOptions opt1 ...
    +Default: None
    +Context: server config, <VirtualHost>, <Global>
    +Module: mod_core
    +Compatibility: 1.3.6rc3 and later + +

    +The FSOptions directive configures various optional behavior of +ProFTPD's filesystem API. The currently supported options are: +

      +
    • IgnoreExtendedAttributes +

      + When the --enable-xattr configure option is enabled, ProFTPD + will support extended attributes where possible. However, this might cause + issues with some clients (e.g. some SFTP clients) that do not + properly support them. Use this option to disable ProFTPD's support for + extended attributes. +

    • +
    +

    GroupOwner

    Syntax: GroupOwner group-name|"~"
    diff --git a/include/fsio.h b/include/fsio.h index c330e8cb9c..2a3bcdbe41 100644 --- a/include/fsio.h +++ b/include/fsio.h @@ -31,6 +31,17 @@ #include "conf.h" +#ifdef PR_USE_XATTR +# if defined(HAVE_SYS_EXTATTR_H) +# include +# elif defined(HAVE_SYS_XATTR_H) +# include +# if defined(HAVE_ATTR_XATTR_H) +# include +# endif /* HAVE_ATTR_XATTR_H */ +# endif /* HAVE_SYS_XATTR_H */ +#endif /* PR_USE_XATTR */ + /* This is a Tru64-specific hack, to work around some macro funkiness * in their /usr/include/sys/mount.h header. */ @@ -56,6 +67,14 @@ #define FSIO_FILE_CHOWN (1 << 14) #define FSIO_FILE_ACCESS (1 << 15) #define FSIO_FILE_UTIMES (1 << 23) +#define FSIO_FILE_GETXATTR (1 << 24) +#define FSIO_FILE_LGETXATTR (1 << 25) +#define FSIO_FILE_LISTXATTR (1 << 26) +#define FSIO_FILE_LLISTXATTR (1 << 27) +#define FSIO_FILE_REMOVEXATTR (1 << 28) +#define FSIO_FILE_LREMOVEXATTR (1 << 29) +#define FSIO_FILE_SETXATTR (1 << 30) +#define FSIO_FILE_LSETXATTR (1 << 31) /* Macro that defines the most common file ops */ #define FSIO_FILE_COMMON (FSIO_FILE_OPEN|FSIO_FILE_READ|FSIO_FILE_WRITE|\ @@ -128,6 +147,24 @@ struct fs_rec { int (*futimes)(pr_fh_t *, int, struct timeval *); int (*fsync)(pr_fh_t *, int); + /* Extended attribute support */ + ssize_t (*getxattr)(pool *, pr_fs_t *, const char *, const char *, void *, + size_t); + ssize_t (*lgetxattr)(pool *, pr_fs_t *, const char *, const char *, void *, + size_t); + ssize_t (*fgetxattr)(pool *, pr_fh_t *, int, const char *, void *, size_t); + int (*listxattr)(pool *, pr_fs_t *, const char *, array_header **); + int (*llistxattr)(pool *, pr_fs_t *, const char *, array_header **); + int (*flistxattr)(pool *, pr_fh_t *, int, array_header **); + int (*removexattr)(pool *, pr_fs_t *, const char *, const char *); + int (*lremovexattr)(pool *, pr_fs_t *, const char *, const char *); + int (*fremovexattr)(pool *, pr_fh_t *, int, const char *); + int (*setxattr)(pool *, pr_fs_t *, const char *, const char *, void *, + size_t, int); + int (*lsetxattr)(pool *, pr_fs_t *, const char *, const char *, void *, + size_t, int); + int (*fsetxattr)(pool *, pr_fh_t *, int, const char *, void *, size_t, int); + /* For actual operations on the directory (or subdirs) * we cast the return from opendir to DIR* in src/fs.c, so * modules can use their own data type @@ -240,6 +277,24 @@ int pr_fsio_futimes(pr_fh_t *, struct timeval *); int pr_fsio_fsync(pr_fh_t *fh); off_t pr_fsio_lseek(pr_fh_t *, off_t, int); +/* Extended attribute support */ +ssize_t pr_fsio_getxattr(pool *p, const char *, const char *, void *, size_t); +ssize_t pr_fsio_lgetxattr(pool *, const char *, const char *, void *, size_t); +ssize_t pr_fsio_fgetxattr(pool *, pr_fh_t *, const char *, void *, size_t); +int pr_fsio_listxattr(pool *, const char *, array_header **); +int pr_fsio_llistxattr(pool *, const char *, array_header **); +int pr_fsio_flistxattr(pool *, pr_fh_t *, array_header **); +int pr_fsio_removexattr(pool *, const char *, const char *); +int pr_fsio_lremovexattr(pool *, const char *, const char *); +int pr_fsio_fremovexattr(pool *, pr_fh_t *, const char *); +int pr_fsio_setxattr(pool *, const char *, const char *, void *, size_t, int); +int pr_fsio_lsetxattr(pool *, const char *, const char *, void *, size_t, int); +int pr_fsio_fsetxattr(pool *, pr_fh_t *, const char *, void *, size_t, int); + +/* setxattr flags */ +#define PR_FSIO_XATTR_FL_CREATE 0x001 +#define PR_FSIO_XATTR_FL_REPLACE 0x002 + /* Set a flag determining whether we guard against write operations in * certain sensitive directories while we are chrooted, e.g. "Roaring Beast" * style attacks. @@ -251,6 +306,12 @@ int pr_fsio_guard_chroot(int); */ int pr_fsio_set_use_mkdtemp(int); +/* Sets a bitmask of various FSIO API options. Returns the previously + * set options. + */ +unsigned long pr_fsio_set_options(unsigned long opts); +#define PR_FSIO_OPT_IGNORE_XATTR 0x00001 + /* FS-related functions */ char *pr_fsio_getline(char *, size_t, pr_fh_t *, unsigned int *); diff --git a/modules/mod_core.c b/modules/mod_core.c index 2bf7a11984..e27caeaf7d 100644 --- a/modules/mod_core.c +++ b/modules/mod_core.c @@ -1431,6 +1431,37 @@ MODRET set_fscachepolicy(cmd_rec *cmd) { return PR_HANDLED(cmd); } +/* usage: FSOptions opt1 opt2 ... */ +MODRET set_fsoptions(cmd_rec *cmd) { + register unsigned int i; + config_rec *c; + + unsigned long opts = 0UL; + + if (cmd->argc-1 == 0) { + CONF_ERROR(cmd, "wrong number of parameters"); + } + + CHECK_CONF(cmd, CONF_ROOT|CONF_VIRTUAL|CONF_GLOBAL); + + c = add_config_param(cmd->argv[0], 1, NULL); + + for (i = 1; i < cmd->argc; i++) { + if (strcmp(cmd->argv[i], "IgnoreExtendedAttributes") == 0) { + opts |= PR_FSIO_OPT_IGNORE_XATTR; + + } else { + CONF_ERROR(cmd, pstrcat(cmd->tmp_pool, ": unknown FSOption '", + cmd->argv[i], "'", NULL)); + } + } + + c->argv[0] = pcalloc(c->pool, sizeof(unsigned long)); + *((unsigned long *) c->argv[0]) = opts; + + return PR_HANDLED(cmd); +} + MODRET set_group(cmd_rec *cmd) { struct group *grp = NULL; @@ -4818,6 +4849,9 @@ MODRET core_post_host(cmd_rec *cmd) { int res; config_rec *c; + /* Reset the FS options */ + (void) pr_fsio_set_options(0UL); + /* Remove the TimeoutIdle timer. */ (void) pr_timer_remove(PR_TIMER_IDLE, ANY_MODULE); @@ -6463,11 +6497,12 @@ static int core_sess_init(void) { char *displayquit = NULL; config_rec *c = NULL; unsigned int *debug_level = NULL; + unsigned long fs_opts = 0UL; init_auth(); c = find_config(main_server->conf, CONF_PARAM, "MultilineRFC2228", FALSE); - if (c) { + if (c != NULL) { session.multiline_rfc2228 = *((int *) c->argv[0]); } @@ -6496,8 +6531,23 @@ static int core_sess_init(void) { /* Check for a configured DebugLevel. */ debug_level = get_param_ptr(main_server->conf, "DebugLevel", FALSE); - if (debug_level != NULL) + if (debug_level != NULL) { pr_log_setdebuglevel(*debug_level); + } + + c = find_config(main_server->conf, CONF_PARAM, "FSOptions", FALSE); + while (c != NULL) { + unsigned long opts = 0; + + pr_signals_handle(); + + opts = *((unsigned long *) c->argv[0]); + fs_opts |= opts; + + c = find_config_next(c, c->next, CONF_PARAM, "FSOptions", FALSE); + } + + (void) pr_fsio_set_options(fs_opts); /* Check for any server-specific RegexOptions */ c = find_config(main_server->conf, CONF_PARAM, "RegexOptions", FALSE); @@ -6772,6 +6822,7 @@ static conftable core_conftab[] = { { "DisplayQuit", set_displayquit, NULL }, { "From", add_from, NULL }, { "FSCachePolicy", set_fscachepolicy, NULL }, + { "FSOptions", set_fsoptions, NULL }, { "Group", set_group, NULL }, { "GroupOwner", add_groupowner, NULL }, { "HideFiles", set_hidefiles, NULL }, diff --git a/src/fsio.c b/src/fsio.c index 35460fa49c..a75f656e51 100644 --- a/src/fsio.c +++ b/src/fsio.c @@ -72,7 +72,6 @@ struct fsopendir { DIR *dir; }; -static const char *trace_channel = "fsio"; static pr_fs_t *root_fs = NULL, *fs_cwd = NULL; static array_header *fs_map = NULL; @@ -93,6 +92,7 @@ static char cwd[PR_TUNABLE_PATH_MAX + 1] = "/"; static size_t cwd_len = 1; static int fsio_guard_chroot = FALSE; +static unsigned long fsio_opts = 0UL; /* Runtime enabling/disabling of mkdtemp(3) use. */ #ifdef HAVE_MKDTEMP @@ -104,6 +104,8 @@ static int fsio_use_mkdtemp = FALSE; /* Runtime enabling/disabling of encoding of paths. */ static int use_encoding = TRUE; +static const char *trace_channel = "fsio"; + /* Guard against attacks like "Roaring Beast" when we are chrooted. See: * * https://auscert.org.au/15286 @@ -474,6 +476,593 @@ static int sys_fsync(pr_fh_t *fh, int fd) { return res; } +static ssize_t sys_getxattr(pool *p, pr_fs_t *fs, const char *path, + const char *name, void *val, size_t valsz) { + ssize_t res; + + (void) p; + +#ifdef PR_USE_XATTR +# if defined(HAVE_SYS_EXTATTR_H) + res = extattr_get_file(path, EXTATTR_NAMESPACE_USER, name, val, valsz); +# elif defined(HAVE_SYS_XATTR_H) +# if defined(XATTR_NOFOLLOW) + res = getxattr(path, name, val, valsz, 0, 0); +# else + res = getxattr(path, name, val, valsz); +# endif /* XATTR_NOFOLLOW */ +# endif /* HAVE_SYS_XATTR_H */ +#else + (void) fs; + (void) path; + (void) name; + (void) val; + (void) valsz; + errno = ENOSYS; + res = -1; +#endif /* PR_USE_XATTR */ + + return res; +} + +static ssize_t sys_lgetxattr(pool *p, pr_fs_t *fs, const char *path, + const char *name, void *val, size_t valsz) { + ssize_t res; + + (void) p; + +#ifdef PR_USE_XATTR +# if defined(HAVE_SYS_EXTATTR_H) +# if defined(HAVE_EXTATTR_GET_LINK) + res = extattr_get_link(path, EXTATTR_NAMESPACE_USER, name, val, valsz); +# else + res = extattr_get_file(path, EXTATTR_NAMESPACE_USER, name, val, valsz); +# endif /* HAVE_EXTATTR_GET_LINK */ +# elif defined(HAVE_SYS_XATTR_H) +# if defined(HAVE_LGETXATTR) + res = lgetxattr(path, name, val, valsz); +# elif defined(XATTR_NOFOLLOW) + res = getxattr(path, name, val, valsz, 0, XATTR_NOFOLLOW); +# else + res = getxattr(path, name, val, valsz); +# endif /* HAVE_LGETXATTR */ +# endif /* HAVE_SYS_XATTR_H */ +#else + (void) fs; + (void) path; + (void) name; + (void) val; + (void) valsz; + errno = ENOSYS; + res = -1; +#endif /* PR_USE_XATTR */ + + return res; +} + +static ssize_t sys_fgetxattr(pool *p, pr_fh_t *fh, int fd, const char *name, + void *val, size_t valsz) { + ssize_t res; + + (void) p; + +#ifdef PR_USE_XATTR +# if defined(HAVE_SYS_EXTATTR_H) + res = extattr_get_fd(fd, EXTATTR_NAMESPACE_USER, name, val, valsz); +# elif defined(HAVE_SYS_XATTR_H) +# if defined(XATTR_NOFOLLOW) + res = fgetxattr(fd, name, val, valsz, 0, 0); +# else + res = fgetxattr(fd, name, val, valsz); +# endif /* XATTR_NOFOLLOW */ +# endif /* HAVE_SYS_XATTR_H */ +#else + (void) fh; + (void) fd; + (void) name; + (void) val; + (void) valsz; + errno = ENOSYS; + res = -1; +#endif /* PR_USE_XATTR */ + + return res; +} + +#ifdef PR_USE_XATTR +static array_header *parse_xattr_namelist(pool *p, char *namelist, size_t sz) { + array_header *names; + char *ptr; + + names = make_array(p, 0, sizeof(char *)); + ptr = namelist; + +# if defined(HAVE_SYS_EXTATTR_H) + /* BSD style name lists use a one-byte length prefix (limiting xattr names + * to a maximum length of 255 bytes), followed by the name, without any + * terminating NUL. + */ + while (sz > 0) { + unsigned char len; + + pr_signals_handle(); + + len = (unsigned char) *ptr; + ptr++; + sz--; + + *((char **) push_array(names)) = pstrndup(p, ptr, len); + + ptr += len; + sz -= len; + } + +# elif defined(HAVE_SYS_XATTR_H) + /* Linux/MacOSX style name lists use NUL-terminated xattr names. */ + while (sz > 0) { + char *ptr2; + size_t len; + + pr_signals_handle(); + + for (ptr2 = ptr; *ptr2; ptr2++); + len = ptr2 - ptr; + *((char **) push_array(names)) = pstrndup(p, ptr, len); + + ptr = ptr2 + 1; + sz -= (len + 1); + } +# endif /* HAVE_SYS_XATTR_H */ + + return names; +} + +static ssize_t unix_listxattr(const char *path, char *namelist, size_t len) { + ssize_t res; + +#if defined(HAVE_SYS_EXTATTR_H) + res = extattr_list_file(path, EXTATTR_NAMESPACE_USER, namelist, len); +#elif defined(HAVE_SYS_XATTR_H) +# if defined(XATTR_NOFOLLOW) + res = listxattr(path, namelist, len, 0); +# else + res = listxattr(path, namelist, len); +# endif /* XATTR_NOFOLLOW */ +#endif /* HAVE_SYS_XATTR_H */ + + return res; +} + +static ssize_t unix_llistxattr(const char *path, char *namelist, size_t len) { + ssize_t res; + +# if defined(HAVE_SYS_EXTATTR_H) +# if defined(HAVE_EXTATTR_LIST_LINK) + res = extattr_list_link(path, EXTATTR_NAMESPACE_USER, namelist, len); +# else + res = extattr_list_file(path, EXTATTR_NAMESPACE_USER, namelist, len); +# endif /* HAVE_EXTATTR_LIST_LINK */ +# elif defined(HAVE_SYS_XATTR_H) +# if defined(HAVE_LLISTXATTR) + res = llistxattr(path, namelist, len); +# elif defined(XATTR_NOFOLLOW) + res = listxattr(path, namelist, len, XATTR_NOFOLLOW); +# else + res = listxattr(path, namelist, len); +# endif /* XATTR_NOFOLLOW */ +# endif /* HAVE_SYS_XATTR_H */ + + return res; +} + +static ssize_t unix_flistxattr(int fd, char *namelist, size_t len) { + ssize_t res; + +# if defined(HAVE_SYS_EXTATTR_H) + res = extattr_list_fd(fd, EXTATTR_NAMESPACE_USER, namelist, len); +# elif defined(HAVE_SYS_XATTR_H) +# if defined(XATTR_NOFOLLOW) + res = flistxattr(fd, namelist, len, 0); +# else + res = flistxattr(fd, namelist, len); +# endif /* XATTR_NOFOLLOW */ +# endif /* HAVE_SYS_XATTR_H */ + + return res; +} +#endif /* PR_USE_XATTR */ + +static int sys_listxattr(pool *p, pr_fs_t *fs, const char *path, + array_header **names) { + ssize_t res; + char *namelist = NULL; + size_t len = 0; + +#ifdef PR_USE_XATTR + /* We need to handle the different formats of namelists that listxattr et al + * can provide. On *BSDs, the namelist buffer uses length prefixes and no + * terminating NULs; on Linux/Mac, the namelist buffer uses ONLY + * NUL-terminated names. + * + * Thus we ALWAYS provide all the available attribute names, by first + * querying for the full namelist buffer size, allocating that out of + * given pool, querying for the names (using the buffer), and then parsing + * them into an array. + */ + + res = unix_listxattr(path, NULL, 0); + if (res < 0) { + return -1; + } + + len = res; + namelist = palloc(p, len); + + res = unix_listxattr(path, namelist, len); + if (res < 0) { + return -1; + } + + *names = parse_xattr_namelist(p, namelist, len); + if (pr_trace_get_level(trace_channel) >= 15) { + register unsigned int i; + int count; + const char **attr_names; + + count = (*names)->nelts; + attr_names = (*names)->elts; + + pr_trace_msg(trace_channel, 15, "listxattr: found %d xattr names for '%s'", + count, path); + for (i = 0; i < count; i++) { + pr_trace_msg(trace_channel, 15, " [%u]: '%s'", i, attr_names[i]); + } + } + + res = (*names)->nelts; + +#else + (void) fs; + (void) path; + (void) names; + (void) namelist; + (void) len; + errno = ENOSYS; + res = -1; +#endif /* PR_USE_XATTR */ + + return (int) res; +} + +static int sys_llistxattr(pool *p, pr_fs_t *fs, const char *path, + array_header **names) { + ssize_t res; + char *namelist = NULL; + size_t len = 0; + +#ifdef PR_USE_XATTR + /* See sys_listxattr for a description of why we use this approach. */ + res = unix_llistxattr(path, NULL, 0); + if (res < 0) { + return -1; + } + + len = res; + namelist = palloc(p, len); + + res = unix_llistxattr(path, namelist, len); + if (res < 0) { + return -1; + } + + *names = parse_xattr_namelist(p, namelist, len); + if (pr_trace_get_level(trace_channel) >= 15) { + register unsigned int i; + int count; + const char **attr_names; + + count = (*names)->nelts; + attr_names = (*names)->elts; + + pr_trace_msg(trace_channel, 15, "llistxattr: found %d xattr names for '%s'", + count, path); + for (i = 0; i < count; i++) { + pr_trace_msg(trace_channel, 15, " [%u]: '%s'", i, attr_names[i]); + } + } + + res = (*names)->nelts; + +#else + (void) fs; + (void) path; + (void) names; + (void) namelist; + (void) len; + errno = ENOSYS; + res = -1; +#endif /* PR_USE_XATTR */ + + return (int) res; +} + +static int sys_flistxattr(pool *p, pr_fh_t *fh, int fd, array_header **names) { + ssize_t res; + char *namelist = NULL; + size_t len = 0; + +#ifdef PR_USE_XATTR + /* See sys_listxattr for a description of why we use this approach. */ + res = unix_flistxattr(fd, NULL, 0); + if (res < 0) { + return -1; + } + + len = res; + namelist = palloc(p, len); + + res = unix_flistxattr(fd, namelist, len); + if (res < 0) { + return -1; + } + + *names = parse_xattr_namelist(p, namelist, len); + if (pr_trace_get_level(trace_channel) >= 15) { + register unsigned int i; + int count; + const char **attr_names; + + count = (*names)->nelts; + attr_names = (*names)->elts; + + pr_trace_msg(trace_channel, 15, "flistxattr: found %d xattr names for '%s'", + count, fh->fh_path); + for (i = 0; i < count; i++) { + pr_trace_msg(trace_channel, 15, " [%u]: '%s'", i, attr_names[i]); + } + } + + res = (*names)->nelts; + +#else + (void) fh; + (void) fd; + (void) names; + (void) namelist; + (void) len; + errno = ENOSYS; + res = -1; +#endif /* PR_USE_XATTR */ + + return (int) res; +} + +static int sys_removexattr(pool *p, pr_fs_t *fs, const char *path, + const char *name) { + int res; + + (void) p; + +#ifdef PR_USE_XATTR +# if defined(HAVE_SYS_EXTATTR_H) + res = extattr_delete_file(path, EXTATTR_NAMESPACE_USER, name); +# elif defined(HAVE_SYS_XATTR_H) +# if defined(XATTR_NOFOLLOW) + res = removexattr(path, name, 0); +# else + res = removexattr(path, name); +# endif /* XATTR_NOFOLLOW */ +# endif /* HAVE_SYS_XATTR_H */ +#else + (void) fs; + (void) path; + (void) name; + errno = ENOSYS; + res = -1; +#endif /* PR_USE_XATTR */ + + return res; +} + +static int sys_lremovexattr(pool *p, pr_fs_t *fs, const char *path, + const char *name) { + int res; + + (void) p; + +#ifdef PR_USE_XATTR +# if defined(HAVE_SYS_EXTATTR_H) +# if defined(HAVE_EXTATTR_DELETE_LINK) + res = extattr_delete_link(path, EXTATTR_NAMESPACE_USER, name); +# else + res = extattr_delete_file(path, EXTATTR_NAMESPACE_USER, name); +# endif /* HAVE_EXTATTR_DELETE_LINK */ +# elif defined(HAVE_SYS_XATTR_H) +# if defined(HAVE_LREMOVEXATTR) + res = lremovexattr(path, name); +# elif defined(XATTR_NOFOLLOW) + res = removexattr(path, name, XATTR_NOFOLLOW); +# else + res = removexattr(path, name); +# endif /* XATTR_NOFOLLOW */ +# endif /* HAVE_SYS_XATTR_H */ +#else + (void) fs; + (void) path; + (void) name; + errno = ENOSYS; + res = -1; +#endif /* PR_USE_XATTR */ + + return res; +} + +static int sys_fremovexattr(pool *p, pr_fh_t *fh, int fd, const char *name) { + int res; + + (void) p; + +#ifdef PR_USE_XATTR +# if defined(HAVE_SYS_EXTATTR_H) + res = extattr_delete_fd(fd, EXTATTR_NAMESPACE_USER, name); +# elif defined(HAVE_SYS_XATTR_H) +# if defined(XATTR_NOFOLLOW) + res = fremovexattr(fd, name, 0); +# else + res = fremovexattr(fd, name); +# endif /* XATTR_NOFOLLOW */ +# endif /* HAVE_SYS_XATTR_H */ +#else + (void) fh; + (void) fd; + (void) name; + errno = ENOSYS; + res = -1; +#endif /* PR_USE_XATTR */ + + return res; +} + +#if defined(PR_USE_XATTR) && defined(HAVE_SYS_XATTR_H) +/* Map the given flags onto the sys/xattr.h flags */ +static int get_setxattr_flags(int fsio_flags) { + int xattr_flags = 0; + + /* If both CREATE and REPLACE are set, use a value of zero; per the + * man pages, this value gives the desired "create or replace" semantics. + * Right? + */ + + if (fsio_flags & PR_FSIO_XATTR_FL_CREATE) { +#if defined(XATTR_CREATE) + xattr_flags = XATTR_CREATE; +#endif /* XATTR_CREATE */ + + if (fsio_flags & PR_FSIO_XATTR_FL_REPLACE) { + xattr_flags = 0; + } + + } else if (fsio_flags & PR_FSIO_XATTR_FL_REPLACE) { +#if defined(XATTR_REPLACE) + xattr_flags = XATTR_REPLACE; +#endif /* XATTR_REPLACE */ + } + + return xattr_flags; +} +#endif /* PR_USE_XATTR and */ + +static int sys_setxattr(pool *p, pr_fs_t *fs, const char *path, + const char *name, void *val, size_t valsz, int flags) { + int res, xattr_flags = 0; + + (void) p; + +#ifdef PR_USE_XATTR +# if defined(HAVE_SYS_EXTATTR_H) + (void) xattr_flags; + res = extattr_set_file(path, EXTATTR_NAMESPACE_USER, name, val, valsz); + +# elif defined(HAVE_SYS_XATTR_H) + xattr_flags = get_setxattr_flags(flags); + +# if defined(XATTR_NOFOLLOW) + res = setxattr(path, name, val, valsz, 0, flags); +# else + res = setxattr(path, name, val, valsz, flags); +# endif /* XATTR_NOFOLLOW */ +# endif /* HAVE_SYS_XATTR_H */ +#else + (void) fs; + (void) path; + (void) name; + (void) val; + (void) valsz; + (void) flags; + (void) xattr_flags; + errno = ENOSYS; + res = -1; +#endif /* PR_USE_XATTR */ + + return res; +} + +static int sys_lsetxattr(pool *p, pr_fs_t *fs, const char *path, + const char *name, void *val, size_t valsz, int flags) { + int res, xattr_flags = 0; + + (void) p; + +#ifdef PR_USE_XATTR +# if defined(HAVE_SYS_EXTATTR_H) + (void) xattr_flags; +# if defined(HAVE_EXTATTR_SET_LINK) + res = extattr_set_link(path, EXTATTR_NAMESPACE_USER, name, val, valsz); +# else + res = extattr_set_file(path, EXTATTR_NAMESPACE_USER, name, val, valsz); +# endif /* HAVE_EXTATTR_SET_LINK */ +# elif defined(HAVE_SYS_XATTR_H) + xattr_flags = get_setxattr_flags(flags); + +# if defined(HAVE_LSETXATTR) + res = lsetxattr(path, name, val, valsz, flags); +# elif defined(XATTR_NOFOLLOW) + xattr_flags |= XATTR_NOFOLLOW; + res = setxattr(path, name, val, valsz, 0, flags); +# else + res = setxattr(path, name, val, valsz, flags); +# endif /* XATTR_NOFOLLOW */ +# endif /* HAVE_SYS_XATTR_H */ +#else + (void) fs; + (void) path; + (void) name; + (void) val; + (void) valsz; + (void) flags; + (void) xattr_flags; + errno = ENOSYS; + res = -1; +#endif /* PR_USE_XATTR */ + + return res; +} + +static int sys_fsetxattr(pool *p, pr_fh_t *fh, int fd, const char *name, + void *val, size_t valsz, int flags) { + int res, xattr_flags = 0; + + (void) p; + +#ifdef PR_USE_XATTR +# if defined(HAVE_SYS_EXTATTR_H) + (void) xattr_flags; + res = extattr_set_fd(fd, EXTATTR_NAMESPACE_USER, name, val, valsz); + +# elif defined(HAVE_SYS_XATTR_H) + xattr_flags = get_setxattr_flags(flags); + +# if defined(XATTR_NOFOLLOW) + res = fsetxattr(fd, name, val, valsz, 0, xattr_flags); +# else + res = fgetxattr(fd, name, val, valsz); +# endif /* XATTR_NOFOLLOW */ +# endif /* HAVE_SYS_XATTR_H */ +#else + (void) fh; + (void) fd; + (void) name; + (void) val; + (void) valsz; + (void) flags; + (void) xattr_flags; + errno = ENOSYS; + res = -1; +#endif /* PR_USE_XATTR */ + + return res; +} + static int sys_chroot(pr_fs_t *fs, const char *path) { if (chroot(path) < 0) { return -1; @@ -1136,6 +1725,9 @@ int pr_fs_copy_file(const char *src, const char *dst) { char *buf; size_t bufsz; int dst_existed = FALSE, res; +#ifdef PR_USE_XATTR + array_header *xattrs = NULL; +#endif /* PR_USE_XATTR */ if (src == NULL || dst == NULL) { @@ -1423,6 +2015,43 @@ int pr_fs_copy_file(const char *src, const char *dst) { } #endif /* HAVE_POSIX_ACL */ +#ifdef PR_USE_XATTR + /* Copy any xattrs that the source file may have. We'll use the + * destination file handle's pool for our xattr allocations. + */ + if (pr_fsio_flistxattr(dst_fh->fh_pool, src_fh, &xattrs) > 0) { + register unsigned int i; + const char **names; + + names = xattrs->elts; + for (i = 0; xattrs->nelts; i++) { + ssize_t valsz; + + /* First, find out how much memory we need for this attribute's + * value. + */ + valsz = pr_fsio_fgetxattr(dst_fh->fh_pool, src_fh, names[i], NULL, 0); + if (valsz > 0) { + void *val; + ssize_t sz; + + val = palloc(dst_fh->fh_pool, valsz); + sz = pr_fsio_fgetxattr(dst_fh->fh_pool, src_fh, names[i], val, valsz); + if (sz > 0) { + sz = pr_fsio_fsetxattr(dst_fh->fh_pool, dst_fh, names[i], val, + valsz, 0); + if (sz < 0 && + errno != ENOSYS) { + pr_trace_msg(trace_channel, 7, + "error copying xattr '%s' (%lu bytes) from '%s' to '%s': %s", + names[i], (unsigned long) valsz, src, dst, strerror(errno)); + } + } + } + } + } +#endif /* PR_USE_XATTR */ + (void) pr_fsio_close(src_fh); res = pr_fsio_close(dst_fh); @@ -3180,6 +3809,15 @@ int pr_fsio_guard_chroot(int guard) { return prev; } +unsigned long pr_fsio_set_options(unsigned long opts) { + unsigned long prev; + + prev = fsio_opts; + fsio_opts = opts; + + return prev; +} + int pr_fsio_set_use_mkdtemp(int value) { int prev_value; @@ -4897,21 +5535,419 @@ int pr_fsio_fsync(pr_fh_t *fh) { return res; } -/* If the wrapped chroot() function suceeds (eg returns 0), then all - * pr_fs_ts currently registered in the fs_map will have their paths - * rewritten to reflect the new root. - */ -int pr_fsio_chroot(const char *path) { - int res = 0, xerrno = 0; +ssize_t pr_fsio_getxattr(pool *p, const char *path, const char *name, void *val, + size_t valsz) { + ssize_t res; pr_fs_t *fs; - if (path == NULL) { + if (p == NULL || + path == NULL || + name == NULL) { errno = EINVAL; return -1; } - fs = lookup_dir_fs(path, FSIO_DIR_CHROOT); - if (fs == NULL) { + if (fsio_opts & PR_FSIO_OPT_IGNORE_XATTR) { + errno = ENOSYS; + return -1; + } + + fs = lookup_file_fs(path, NULL, FSIO_FILE_GETXATTR); + if (fs == NULL) { + return -1; + } + + /* Find the first non-NULL custom getxattr handler. If there are none, + * use the system getxattr. + */ + while (fs && fs->fs_next && !fs->getxattr) { + fs = fs->fs_next; + } + + pr_trace_msg(trace_channel, 8, "using %s getxattr() for path '%s'", + fs->fs_name, path); + res = (fs->getxattr)(p, fs, path, name, val, valsz); + return res; +} + +ssize_t pr_fsio_lgetxattr(pool *p, const char *path, const char *name, + void *val, size_t valsz) { + ssize_t res; + pr_fs_t *fs; + + if (p == NULL || + path == NULL || + name == NULL) { + errno = EINVAL; + return -1; + } + + if (fsio_opts & PR_FSIO_OPT_IGNORE_XATTR) { + errno = ENOSYS; + return -1; + } + + fs = lookup_file_fs(path, NULL, FSIO_FILE_LGETXATTR); + if (fs == NULL) { + return -1; + } + + /* Find the first non-NULL custom lgetxattr handler. If there are none, + * use the system lgetxattr. + */ + while (fs && fs->fs_next && !fs->lgetxattr) { + fs = fs->fs_next; + } + + pr_trace_msg(trace_channel, 8, "using %s lgetxattr() for path '%s'", + fs->fs_name, path); + res = (fs->lgetxattr)(p, fs, path, name, val, valsz); + return res; +} + +ssize_t pr_fsio_fgetxattr(pool *p, pr_fh_t *fh, const char *name, void *val, + size_t valsz) { + ssize_t res; + pr_fs_t *fs; + + if (p == NULL || + fh == NULL || + name == NULL) { + errno = EINVAL; + return -1; + } + + if (fsio_opts & PR_FSIO_OPT_IGNORE_XATTR) { + errno = ENOSYS; + return -1; + } + + /* Find the first non-NULL custom fgetxattr handler. If there are none, + * use the system fgetxattr. + */ + fs = fh->fh_fs; + while (fs && fs->fs_next && !fs->fgetxattr) { + fs = fs->fs_next; + } + + pr_trace_msg(trace_channel, 8, "using %s fgetxattr() for path '%s'", + fs->fs_name, fh->fh_path); + res = (fs->fgetxattr)(p, fh, fh->fh_fd, name, val, valsz); + return res; +} + +int pr_fsio_listxattr(pool *p, const char *path, array_header **names) { + int res; + pr_fs_t *fs; + + if (p == NULL || + path == NULL || + names == NULL) { + errno = EINVAL; + return -1; + } + + if (fsio_opts & PR_FSIO_OPT_IGNORE_XATTR) { + errno = ENOSYS; + return -1; + } + + fs = lookup_file_fs(path, NULL, FSIO_FILE_LISTXATTR); + if (fs == NULL) { + return -1; + } + + /* Find the first non-NULL custom listxattr handler. If there are none, + * use the system listxattr. + */ + while (fs && fs->fs_next && !fs->listxattr) { + fs = fs->fs_next; + } + + pr_trace_msg(trace_channel, 8, "using %s listxattr() for path '%s'", + fs->fs_name, path); + res = (fs->listxattr)(p, fs, path, names); + return res; +} + +int pr_fsio_llistxattr(pool *p, const char *path, array_header **names) { + int res; + pr_fs_t *fs; + + if (p == NULL || + path == NULL || + names == NULL) { + errno = EINVAL; + return -1; + } + + if (fsio_opts & PR_FSIO_OPT_IGNORE_XATTR) { + errno = ENOSYS; + return -1; + } + + fs = lookup_file_fs(path, NULL, FSIO_FILE_LLISTXATTR); + if (fs == NULL) { + return -1; + } + + /* Find the first non-NULL custom llistxattr handler. If there are none, + * use the system llistxattr. + */ + while (fs && fs->fs_next && !fs->llistxattr) { + fs = fs->fs_next; + } + + pr_trace_msg(trace_channel, 8, "using %s llistxattr() for path '%s'", + fs->fs_name, path); + res = (fs->llistxattr)(p, fs, path, names); + return res; +} + +int pr_fsio_flistxattr(pool *p, pr_fh_t *fh, array_header **names) { + int res; + pr_fs_t *fs; + + if (p == NULL || + fh == NULL || + names == NULL) { + errno = EINVAL; + return -1; + } + + if (fsio_opts & PR_FSIO_OPT_IGNORE_XATTR) { + errno = ENOSYS; + return -1; + } + + /* Find the first non-NULL custom flistxattr handler. If there are none, + * use the system flistxattr. + */ + fs = fh->fh_fs; + while (fs && fs->fs_next && !fs->flistxattr) { + fs = fs->fs_next; + } + + pr_trace_msg(trace_channel, 8, "using %s flistxattr() for path '%s'", + fs->fs_name, fh->fh_path); + res = (fs->flistxattr)(p, fh, fh->fh_fd, names); + return res; +} + +int pr_fsio_removexattr(pool *p, const char *path, const char *name) { + int res; + pr_fs_t *fs; + + if (p == NULL || + path == NULL || + name == NULL) { + errno = EINVAL; + return -1; + } + + if (fsio_opts & PR_FSIO_OPT_IGNORE_XATTR) { + errno = ENOSYS; + return -1; + } + + fs = lookup_file_fs(path, NULL, FSIO_FILE_REMOVEXATTR); + if (fs == NULL) { + return -1; + } + + /* Find the first non-NULL custom removexattr handler. If there are none, + * use the system removexattr. + */ + while (fs && fs->fs_next && !fs->removexattr) { + fs = fs->fs_next; + } + + pr_trace_msg(trace_channel, 8, "using %s removexattr() for path '%s'", + fs->fs_name, path); + res = (fs->removexattr)(p, fs, path, name); + return res; +} + +int pr_fsio_lremovexattr(pool *p, const char *path, const char *name) { + int res; + pr_fs_t *fs; + + if (p == NULL || + path == NULL || + name == NULL) { + errno = EINVAL; + return -1; + } + + if (fsio_opts & PR_FSIO_OPT_IGNORE_XATTR) { + errno = ENOSYS; + return -1; + } + + fs = lookup_file_fs(path, NULL, FSIO_FILE_LREMOVEXATTR); + if (fs == NULL) { + return -1; + } + + /* Find the first non-NULL custom lremovexattr handler. If there are none, + * use the system lremovexattr. + */ + while (fs && fs->fs_next && !fs->lremovexattr) { + fs = fs->fs_next; + } + + pr_trace_msg(trace_channel, 8, "using %s lremovexattr() for path '%s'", + fs->fs_name, path); + res = (fs->lremovexattr)(p, fs, path, name); + return res; +} + +int pr_fsio_fremovexattr(pool *p, pr_fh_t *fh, const char *name) { + int res; + pr_fs_t *fs; + + if (p == NULL || + fh == NULL || + name == NULL) { + errno = EINVAL; + return -1; + } + + if (fsio_opts & PR_FSIO_OPT_IGNORE_XATTR) { + errno = ENOSYS; + return -1; + } + + /* Find the first non-NULL custom fremovexattr handler. If there are none, + * use the system fremovexattr. + */ + fs = fh->fh_fs; + while (fs && fs->fs_next && !fs->fremovexattr) { + fs = fs->fs_next; + } + + pr_trace_msg(trace_channel, 8, "using %s fremovexattr() for path '%s'", + fs->fs_name, fh->fh_path); + res = (fs->fremovexattr)(p, fh, fh->fh_fd, name); + return res; +} + +int pr_fsio_setxattr(pool *p, const char *path, const char *name, void *val, + size_t valsz, int flags) { + int res; + pr_fs_t *fs; + + if (p == NULL || + path == NULL || + name == NULL) { + errno = EINVAL; + return -1; + } + + if (fsio_opts & PR_FSIO_OPT_IGNORE_XATTR) { + errno = ENOSYS; + return -1; + } + + fs = lookup_file_fs(path, NULL, FSIO_FILE_SETXATTR); + if (fs == NULL) { + return -1; + } + + /* Find the first non-NULL custom setxattr handler. If there are none, + * use the system setxattr. + */ + while (fs && fs->fs_next && !fs->setxattr) { + fs = fs->fs_next; + } + + pr_trace_msg(trace_channel, 8, "using %s setxattr() for path '%s'", + fs->fs_name, path); + res = (fs->setxattr)(p, fs, path, name, val, valsz, flags); + return res; +} + +int pr_fsio_lsetxattr(pool *p, const char *path, const char *name, void *val, + size_t valsz, int flags) { + int res; + pr_fs_t *fs; + + if (p == NULL || + path == NULL || + name == NULL) { + errno = EINVAL; + return -1; + } + + if (fsio_opts & PR_FSIO_OPT_IGNORE_XATTR) { + errno = ENOSYS; + return -1; + } + + fs = lookup_file_fs(path, NULL, FSIO_FILE_LSETXATTR); + if (fs == NULL) { + return -1; + } + + /* Find the first non-NULL custom lsetxattr handler. If there are none, + * use the system lsetxattr. + */ + while (fs && fs->fs_next && !fs->lsetxattr) { + fs = fs->fs_next; + } + + pr_trace_msg(trace_channel, 8, "using %s lsetxattr() for path '%s'", + fs->fs_name, path); + res = (fs->lsetxattr)(p, fs, path, name, val, valsz, flags); + return res; +} + +int pr_fsio_fsetxattr(pool *p, pr_fh_t *fh, const char *name, void *val, + size_t valsz, int flags) { + int res; + pr_fs_t *fs; + + if (p == NULL || + fh == NULL || + name == NULL) { + errno = EINVAL; + return -1; + } + + if (fsio_opts & PR_FSIO_OPT_IGNORE_XATTR) { + errno = ENOSYS; + return -1; + } + + /* Find the first non-NULL custom fsetxattr handler. If there are none, + * use the system fsetxattr. + */ + fs = fh->fh_fs; + while (fs && fs->fs_next && !fs->fsetxattr) { + fs = fs->fs_next; + } + + pr_trace_msg(trace_channel, 8, "using %s fsetxattr() for path '%s'", + fs->fs_name, fh->fh_path); + res = (fs->fsetxattr)(p, fh, fh->fh_fd, name, val, valsz, flags); + return res; +} + +/* If the wrapped chroot() function suceeds (eg returns 0), then all + * pr_fs_ts currently registered in the fs_map will have their paths + * rewritten to reflect the new root. + */ +int pr_fsio_chroot(const char *path) { + int res = 0, xerrno = 0; + pr_fs_t *fs; + + if (path == NULL) { + errno = EINVAL; + return -1; + } + + fs = lookup_dir_fs(path, FSIO_DIR_CHROOT); + if (fs == NULL) { return -1; } @@ -5770,6 +6806,19 @@ int init_fs(void) { root_fs->futimes = sys_futimes; root_fs->fsync = sys_fsync; + root_fs->getxattr = sys_getxattr; + root_fs->lgetxattr = sys_lgetxattr; + root_fs->fgetxattr = sys_fgetxattr; + root_fs->listxattr = sys_listxattr; + root_fs->llistxattr = sys_llistxattr; + root_fs->flistxattr = sys_flistxattr; + root_fs->removexattr = sys_removexattr; + root_fs->lremovexattr = sys_lremovexattr; + root_fs->fremovexattr = sys_fremovexattr; + root_fs->setxattr = sys_setxattr; + root_fs->lsetxattr = sys_lsetxattr; + root_fs->fsetxattr = sys_fsetxattr; + root_fs->chdir = sys_chdir; root_fs->chroot = sys_chroot; root_fs->opendir = sys_opendir; diff --git a/src/main.c b/src/main.c index 1cb64a2f47..a3ebc9ddb1 100644 --- a/src/main.c +++ b/src/main.c @@ -2096,6 +2096,12 @@ static void show_settings(void) { printf("%s", " - Trace support\n"); #endif /* PR_USE_TRACE */ +#ifdef PR_USE_XATTR + printf("%s", " + xattr support\n"); +#else + printf("%s", " - xattr support\n"); +#endif /* PR_USE_XATTR */ + /* Tunable settings */ printf("%s", "\n Tunable Options:\n"); printf(" PR_TUNABLE_BUFFER_SIZE = %u\n", PR_TUNABLE_BUFFER_SIZE); diff --git a/tests/api/fsio.c b/tests/api/fsio.c index 8be3bfc9f8..76de8de0db 100644 --- a/tests/api/fsio.c +++ b/tests/api/fsio.c @@ -1721,6 +1721,627 @@ START_TEST (fsio_sys_fsync_test) { } END_TEST +START_TEST (fsio_sys_getxattr_test) { + ssize_t res; + const char *path, *name; + unsigned long fsio_opts; + + res = pr_fsio_getxattr(NULL, NULL, NULL, NULL, 0); + fail_unless(res < 0, "Failed to handle null arguments"); + fail_unless(errno == EINVAL, "Expected EINVAL (%d), got %s (%d)", EINVAL, + strerror(errno), errno); + + res = pr_fsio_getxattr(p, NULL, NULL, NULL, 0); + fail_unless(res < 0, "Failed to handle null path"); + fail_unless(errno == EINVAL, "Expected EINVAL (%d), got %s (%d)", EINVAL, + strerror(errno), errno); + + path = fsio_test_path; + res = pr_fsio_getxattr(p, path, NULL, NULL, 0); + fail_unless(res < 0, "Failed to handle null arguments"); + fail_unless(errno == EINVAL, "Expected EINVAL (%d), got %s (%d)", EINVAL, + strerror(errno), errno); + + name = "foo.bar"; + + fsio_opts = pr_fsio_set_options(PR_FSIO_OPT_IGNORE_XATTR); + res = pr_fsio_getxattr(p, path, name, NULL, 0); + fail_unless(res < 0, "Failed to handle disabled xattr"); + fail_unless(errno == ENOSYS, "Expected ENOSYS (%d), got %s (%d)", ENOSYS, + strerror(errno), errno); + + (void) pr_fsio_set_options(fsio_opts); + res = pr_fsio_getxattr(p, path, name, NULL, 0); +#ifdef PR_USE_XATTR + fail_unless(res < 0, "Failed to handle nonexist attribute '%s'", name); + fail_unless(errno == ENOENT || errno == ENOATTR || errno == ENOTSUP, + "Expected ENOENT (%d), ENOATTR (%d) or ENOTSUP (%d), got %s (%d)", + ENOENT, ENOATTR, ENOTSUP, strerror(errno), errno); + +#else + fail_unless(res < 0, "Failed to handle --disable-xattr"); + fail_unless(errno == ENOSYS, "Expected ENOSYS (%d), got %s (%d)", ENOSYS, + strerror(errno), errno); +#endif /* PR_USE_XATTR */ +} +END_TEST + +START_TEST (fsio_sys_lgetxattr_test) { + ssize_t res; + const char *path, *name; + unsigned long fsio_opts; + + res = pr_fsio_lgetxattr(NULL, NULL, NULL, NULL, 0); + fail_unless(res < 0, "Failed to handle null arguments"); + fail_unless(errno == EINVAL, "Expected EINVAL (%d), got %s (%d)", EINVAL, + strerror(errno), errno); + + res = pr_fsio_lgetxattr(p, NULL, NULL, NULL, 0); + fail_unless(res < 0, "Failed to handle null path"); + fail_unless(errno == EINVAL, "Expected EINVAL (%d), got %s (%d)", EINVAL, + strerror(errno), errno); + + path = fsio_test_path; + res = pr_fsio_lgetxattr(p, path, NULL, NULL, 0); + fail_unless(res < 0, "Failed to handle null xattr name"); + fail_unless(errno == EINVAL, "Expected EINVAL (%d), got %s (%d)", EINVAL, + strerror(errno), errno); + + name = "foo.bar"; + + fsio_opts = pr_fsio_set_options(PR_FSIO_OPT_IGNORE_XATTR); + res = pr_fsio_lgetxattr(p, path, name, NULL, 0); + fail_unless(res < 0, "Failed to handle disabled xattr"); + fail_unless(errno == ENOSYS, "Expected ENOSYS (%d), got %s (%d)", ENOSYS, + strerror(errno), errno); + + pr_fsio_set_options(fsio_opts); + res = pr_fsio_lgetxattr(p, path, name, NULL, 0); +#ifdef PR_USE_XATTR + fail_unless(res < 0, "Failed to handle nonexist attribute '%s'", name); + fail_unless(errno == ENOENT || errno == ENOATTR || errno == ENOTSUP, + "Expected ENOENT (%d), ENOATTR (%d) or ENOTSUP (%d), got %s (%d)", + ENOENT, ENOATTR, ENOTSUP, strerror(errno), errno); + +#else + fail_unless(res < 0, "Failed to handle --disable-xattr"); + fail_unless(errno == ENOSYS, "Expected ENOSYS (%d), got %s (%d)", ENOSYS, + strerror(errno), errno); +#endif /* PR_USE_XATTR */ +} +END_TEST + +START_TEST (fsio_sys_fgetxattr_test) { + ssize_t res; + pr_fh_t *fh; + const char *name; + unsigned long fsio_opts; + + res = pr_fsio_fgetxattr(NULL, NULL, NULL, NULL, 0); + fail_unless(res < 0, "Failed to handle null arguments"); + fail_unless(errno == EINVAL, "Expected EINVAL (%d), got %s (%d)", EINVAL, + strerror(errno), errno); + + res = pr_fsio_fgetxattr(p, NULL, NULL, NULL, 0); + fail_unless(res < 0, "Failed to handle null file handle"); + fail_unless(errno == EINVAL, "Expected EINVAL (%d), got %s (%d)", EINVAL, + strerror(errno), errno); + + (void) unlink(fsio_test_path); + fh = pr_fsio_open(fsio_test_path, O_CREAT|O_EXCL|O_RDWR); + fail_unless(fh != NULL, "Failed to open '%s': %s", fsio_test_path, + strerror(errno)); + + res = pr_fsio_fgetxattr(p, fh, NULL, NULL, 0); + fail_unless(res < 0, "Failed to handle null xattr name"); + fail_unless(errno == EINVAL, "Expected EINVAL (%d), got %s (%d)", EINVAL, + strerror(errno), errno); + + name = "foo.bar"; + + fsio_opts = pr_fsio_set_options(PR_FSIO_OPT_IGNORE_XATTR); + res = pr_fsio_fgetxattr(p, fh, name, NULL, 0); + fail_unless(res < 0, "Failed to handle disabled xattr"); + fail_unless(errno == ENOSYS, "Expected ENOSYS (%d), got %s (%d)", ENOSYS, + strerror(errno), errno); + + pr_fsio_set_options(fsio_opts); + res = pr_fsio_fgetxattr(p, fh, name, NULL, 0); +#ifdef PR_USE_XATTR + fail_unless(res < 0, "Failed to handle nonexist attribute '%s'", name); + fail_unless(errno == ENOENT || errno == ENOATTR || errno == ENOTSUP, + "Expected ENOENT (%d), ENOATTR (%d) or ENOTSUP (%d), got %s (%d)", + ENOENT, ENOATTR, ENOTSUP, strerror(errno), errno); + +#else + fail_unless(res < 0, "Failed to handle --disable-xattr"); + fail_unless(errno == ENOSYS, "Expected ENOSYS (%d), got %s (%d)", ENOSYS, + strerror(errno), errno); +#endif /* PR_USE_XATTR */ + + pr_fsio_close(fh); + (void) unlink(fsio_test_path); +} +END_TEST + +START_TEST (fsio_sys_listxattr_test) { + int res; + const char *path; + pr_fh_t *fh = NULL; + array_header *names = NULL; + unsigned long fsio_opts; + + res = pr_fsio_listxattr(NULL, NULL, NULL); + fail_unless(res < 0, "Failed to handle null arguments"); + fail_unless(errno == EINVAL, "Expected EINVAL (%d), got %s (%d)", EINVAL, + strerror(errno), errno); + + res = pr_fsio_listxattr(p, NULL, NULL); + fail_unless(res < 0, "Failed to handle null path"); + fail_unless(errno == EINVAL, "Expected EINVAL (%d), got %s (%d)", EINVAL, + strerror(errno), errno); + + path = fsio_test_path; + res = pr_fsio_listxattr(p, path, NULL); + fail_unless(res < 0, "Failed to handle null array"); + fail_unless(errno == EINVAL, "Expected EINVAL (%d), got %s (%d)", EINVAL, + strerror(errno), errno); + + fsio_opts = pr_fsio_set_options(PR_FSIO_OPT_IGNORE_XATTR); + res = pr_fsio_listxattr(p, path, &names); + fail_unless(res < 0, "Failed to handle disabled xattr"); + fail_unless(errno == ENOSYS, "Expected ENOSYS (%d), got %s (%d)", ENOSYS, + strerror(errno), errno); + + pr_fsio_set_options(fsio_opts); + res = pr_fsio_listxattr(p, path, &names); +#ifdef PR_USE_XATTR + fail_unless(res < 0, "Failed to handle nonexistent path '%s'", path); + fail_unless(errno == ENOENT, "Expected ENOENT (%d), got %s (%d)", ENOENT, + strerror(errno), errno); + + (void) unlink(fsio_test_path); + fh = pr_fsio_open(fsio_test_path, O_CREAT|O_EXCL|O_WRONLY); + fail_unless(fh != NULL, "Failed to open '%s': %s", fsio_test_path, + strerror(errno)); + pr_fsio_close(fh); + + res = pr_fsio_listxattr(p, path, &names); + fail_if(res < 0, "Failed to list xattrs for '%s': %s", path, strerror(errno)); + + pr_fsio_close(fh); + (void) unlink(fsio_test_path); +#else + (void) fh; + fail_unless(res < 0, "Failed to handle --disable-xattr"); + fail_unless(errno == ENOSYS, "Expected ENOSYS (%d), got %s (%d)", ENOSYS, + strerror(errno), errno); +#endif /* PR_USE_XATTR */ +} +END_TEST + +START_TEST (fsio_sys_llistxattr_test) { + int res; + const char *path; + pr_fh_t *fh = NULL; + array_header *names = NULL; + unsigned long fsio_opts; + + res = pr_fsio_llistxattr(NULL, NULL, NULL); + fail_unless(res < 0, "Failed to handle null arguments"); + fail_unless(errno == EINVAL, "Expected EINVAL (%d), got %s (%d)", EINVAL, + strerror(errno), errno); + + res = pr_fsio_llistxattr(p, NULL, NULL); + fail_unless(res < 0, "Failed to handle null path"); + fail_unless(errno == EINVAL, "Expected EINVAL (%d), got %s (%d)", EINVAL, + strerror(errno), errno); + + path = fsio_test_path; + res = pr_fsio_llistxattr(p, path, NULL); + fail_unless(res < 0, "Failed to handle null array"); + fail_unless(errno == EINVAL, "Expected EINVAL (%d), got %s (%d)", EINVAL, + strerror(errno), errno); + + fsio_opts = pr_fsio_set_options(PR_FSIO_OPT_IGNORE_XATTR); + res = pr_fsio_llistxattr(p, path, &names); + fail_unless(res < 0, "Failed to handle disabled xattr"); + fail_unless(errno == ENOSYS, "Expected ENOSYS (%d), got %s (%d)", ENOSYS, + strerror(errno), errno); + + pr_fsio_set_options(fsio_opts); + res = pr_fsio_llistxattr(p, path, &names); +#ifdef PR_USE_XATTR + fail_unless(res < 0, "Failed to handle nonexistent path '%s'", path); + fail_unless(errno == ENOENT, "Expected ENOENT (%d), got %s (%d)", ENOENT, + strerror(errno), errno); + + (void) unlink(fsio_test_path); + fh = pr_fsio_open(fsio_test_path, O_CREAT|O_EXCL|O_WRONLY); + fail_unless(fh != NULL, "Failed to open '%s': %s", fsio_test_path, + strerror(errno)); + pr_fsio_close(fh); + + res = pr_fsio_listxattr(p, path, &names); + fail_if(res < 0, "Failed to list xattrs for '%s': %s", path, strerror(errno)); + + pr_fsio_close(fh); + (void) unlink(fsio_test_path); +#else + (void) fh; + fail_unless(res < 0, "Failed to handle --disable-xattr"); + fail_unless(errno == ENOSYS, "Expected ENOSYS (%d), got %s (%d)", ENOSYS, + strerror(errno), errno); +#endif /* PR_USE_XATTR */ +} +END_TEST + +START_TEST (fsio_sys_flistxattr_test) { + int res; + pr_fh_t *fh; + array_header *names = NULL; + unsigned long fsio_opts; + + res = pr_fsio_flistxattr(NULL, NULL, NULL); + fail_unless(res < 0, "Failed to handle null arguments"); + fail_unless(errno == EINVAL, "Expected EINVAL (%d), got %s (%d)", EINVAL, + strerror(errno), errno); + + res = pr_fsio_flistxattr(p, NULL, NULL); + fail_unless(res < 0, "Failed to handle null file handle"); + fail_unless(errno == EINVAL, "Expected EINVAL (%d), got %s (%d)", EINVAL, + strerror(errno), errno); + + (void) unlink(fsio_test_path); + fh = pr_fsio_open(fsio_test_path, O_CREAT|O_EXCL|O_RDWR); + fail_unless(fh != NULL, "Failed to open '%s': %s", fsio_test_path, + strerror(errno)); + + res = pr_fsio_flistxattr(p, fh, NULL); + fail_unless(res < 0, "Failed to handle null array"); + fail_unless(errno == EINVAL, "Expected EINVAL (%d), got %s (%d)", EINVAL, + strerror(errno), errno); + + fsio_opts = pr_fsio_set_options(PR_FSIO_OPT_IGNORE_XATTR); + res = pr_fsio_flistxattr(p, fh, &names); + fail_unless(res < 0, "Failed to handle disabled xattr"); + fail_unless(errno == ENOSYS, "Expected ENOSYS (%d), got %s (%d)", ENOSYS, + strerror(errno), errno); + + pr_fsio_set_options(fsio_opts); + res = pr_fsio_flistxattr(p, fh, &names); +#ifdef PR_USE_XATTR + fail_if(res < 0, "Failed to list xattrs for '%s': %s", fsio_test_path, + strerror(errno)); + +#else + fail_unless(res < 0, "Failed to handle --disable-xattr"); + fail_unless(errno == ENOSYS, "Expected ENOSYS (%d), got %s (%d)", ENOSYS, + strerror(errno), errno); +#endif /* PR_USE_XATTR */ + + pr_fsio_close(fh); + (void) unlink(fsio_test_path); +} +END_TEST + +START_TEST (fsio_sys_removexattr_test) { + int res; + const char *path, *name; + unsigned long fsio_opts; + + res = pr_fsio_removexattr(NULL, NULL, NULL); + fail_unless(res < 0, "Failed to handle null arguments"); + fail_unless(errno == EINVAL, "Expected EINVAL (%d), got %s (%d)", EINVAL, + strerror(errno), errno); + + res = pr_fsio_removexattr(p, NULL, NULL); + fail_unless(res < 0, "Failed to handle null path"); + fail_unless(errno == EINVAL, "Expected EINVAL (%d), got %s (%d)", EINVAL, + strerror(errno), errno); + + path = fsio_test_path; + res = pr_fsio_removexattr(p, path, NULL); + fail_unless(res < 0, "Failed to handle null attribute name"); + fail_unless(errno == EINVAL, "Expected EINVAL (%d), got %s (%d)", EINVAL, + strerror(errno), errno); + + name = "foo.bar"; + + fsio_opts = pr_fsio_set_options(PR_FSIO_OPT_IGNORE_XATTR); + res = pr_fsio_removexattr(p, path, name); + fail_unless(res < 0, "Failed to handle disabled xattr"); + fail_unless(errno == ENOSYS, "Expected ENOSYS (%d), got %s (%d)", ENOSYS, + strerror(errno), errno); + + pr_fsio_set_options(fsio_opts); + res = pr_fsio_removexattr(p, path, name); +#ifdef PR_USE_XATTR + fail_unless(res < 0, "Failed to handle nonexistent attribute '%s'", name); + fail_unless(errno == ENOENT || errno == ENOATTR || errno == ENOTSUP, + "Expected ENOENT (%d), ENOATTR (%d) or ENOTSUP (%d), got %s (%d)", + ENOENT, ENOATTR, ENOTSUP, strerror(errno), errno); + +#else + fail_unless(res < 0, "Failed to handle --disable-xattr"); + fail_unless(errno == ENOSYS, "Expected ENOSYS (%d), got %s (%d)", ENOSYS, + strerror(errno), errno); +#endif /* PR_USE_XATTR */ +} +END_TEST + +START_TEST (fsio_sys_lremovexattr_test) { + int res; + const char *path, *name; + unsigned long fsio_opts; + + res = pr_fsio_lremovexattr(NULL, NULL, NULL); + fail_unless(res < 0, "Failed to handle null arguments"); + fail_unless(errno == EINVAL, "Expected EINVAL (%d), got %s (%d)", EINVAL, + strerror(errno), errno); + + res = pr_fsio_lremovexattr(p, NULL, NULL); + fail_unless(res < 0, "Failed to handle null path"); + fail_unless(errno == EINVAL, "Expected EINVAL (%d), got %s (%d)", EINVAL, + strerror(errno), errno); + + path = fsio_test_path; + res = pr_fsio_lremovexattr(p, path, NULL); + fail_unless(res < 0, "Failed to handle null attribute name"); + fail_unless(errno == EINVAL, "Expected EINVAL (%d), got %s (%d)", EINVAL, + strerror(errno), errno); + + name = "foo.bar"; + + fsio_opts = pr_fsio_set_options(PR_FSIO_OPT_IGNORE_XATTR); + res = pr_fsio_lremovexattr(p, path, name); + fail_unless(res < 0, "Failed to handle disabled xattr"); + fail_unless(errno == ENOSYS, "Expected ENOSYS (%d), got %s (%d)", ENOSYS, + strerror(errno), errno); + + pr_fsio_set_options(fsio_opts); + res = pr_fsio_lremovexattr(p, path, name); +#ifdef PR_USE_XATTR + fail_unless(res < 0, "Failed to handle nonexistent attribute '%s'", name); + fail_unless(errno == ENOENT || errno == ENOATTR || errno == ENOTSUP, + "Expected ENOENT (%d), ENOATTR (%d) or ENOTSUP (%d), got %s (%d)", + ENOENT, ENOATTR, ENOTSUP, strerror(errno), errno); + +#else + fail_unless(res < 0, "Failed to handle --disable-xattr"); + fail_unless(errno == ENOSYS, "Expected ENOSYS (%d), got %s (%d)", ENOSYS, + strerror(errno), errno); +#endif /* PR_USE_XATTR */ +} +END_TEST + +START_TEST (fsio_sys_fremovexattr_test) { + int res; + pr_fh_t *fh; + const char *name; + unsigned long fsio_opts; + + res = pr_fsio_fremovexattr(NULL, NULL, NULL); + fail_unless(res < 0, "Failed to handle null arguments"); + fail_unless(errno == EINVAL, "Expected EINVAL (%d), got %s (%d)", EINVAL, + strerror(errno), errno); + + res = pr_fsio_fremovexattr(p, NULL, NULL); + fail_unless(res < 0, "Failed to handle null arguments"); + fail_unless(errno == EINVAL, "Expected EINVAL (%d), got %s (%d)", EINVAL, + strerror(errno), errno); + + (void) unlink(fsio_test_path); + fh = pr_fsio_open(fsio_test_path, O_CREAT|O_EXCL|O_RDWR); + fail_unless(fh != NULL, "Failed to open '%s': %s", fsio_test_path, + strerror(errno)); + + res = pr_fsio_fremovexattr(p, fh, NULL); + fail_unless(res < 0, "Failed to handle null attribute name"); + fail_unless(errno == EINVAL, "Expected EINVAL (%d), got %s (%d)", EINVAL, + strerror(errno), errno); + + name = "foo.bar"; + + fsio_opts = pr_fsio_set_options(PR_FSIO_OPT_IGNORE_XATTR); + res = pr_fsio_fremovexattr(p, fh, name); + fail_unless(res < 0, "Failed to handle disabled xattr"); + fail_unless(errno == ENOSYS, "Expected ENOSYS (%d), got %s (%d)", ENOSYS, + strerror(errno), errno); + + pr_fsio_set_options(fsio_opts); + res = pr_fsio_fremovexattr(p, fh, name); +#ifdef PR_USE_XATTR + fail_unless(res < 0, "Failed to handle nonexistent attribute '%s'", name); + fail_unless(errno == ENOENT || errno == ENOATTR || errno == ENOTSUP, + "Expected ENOENT (%d), ENOATTR (%d) or ENOTSUP (%d), got %s (%d)", + ENOENT, ENOATTR, ENOTSUP, strerror(errno), errno); + +#else + fail_unless(res < 0, "Failed to handle --disable-xattr"); + fail_unless(errno == ENOSYS, "Expected ENOSYS (%d), got %s (%d)", ENOSYS, + strerror(errno), errno); +#endif /* PR_USE_XATTR */ + + pr_fsio_close(fh); + (void) unlink(fsio_test_path); +} +END_TEST + +START_TEST (fsio_sys_setxattr_test) { + int res, flags; + const char *path, *name; + pr_fh_t *fh; + unsigned long fsio_opts; + + res = pr_fsio_setxattr(NULL, NULL, NULL, NULL, 0, 0); + fail_unless(res < 0, "Failed to handle null arguments"); + fail_unless(errno == EINVAL, "Expected EINVAL (%d), got %s (%d)", EINVAL, + strerror(errno), errno); + + res = pr_fsio_setxattr(p, NULL, NULL, NULL, 0, 0); + fail_unless(res < 0, "Failed to handle null path"); + fail_unless(errno == EINVAL, "Expected EINVAL (%d), got %s (%d)", EINVAL, + strerror(errno), errno); + + path = fsio_test_path; + res = pr_fsio_setxattr(p, path, NULL, NULL, 0, 0); + fail_unless(res < 0, "Failed to handle null attribute name"); + fail_unless(errno == EINVAL, "Expected EINVAL (%d), got %s (%d)", EINVAL, + strerror(errno), errno); + + name = "foo.bar"; + flags = PR_FSIO_XATTR_FL_CREATE; + + fsio_opts = pr_fsio_set_options(PR_FSIO_OPT_IGNORE_XATTR); + res = pr_fsio_setxattr(p, path, name, NULL, 0, flags); + fail_unless(res < 0, "Failed to handle disabled xattr"); + fail_unless(errno == ENOSYS, "Expected ENOSYS (%d), got %s (%d)", ENOSYS, + strerror(errno), errno); + + pr_fsio_set_options(fsio_opts); + res = pr_fsio_setxattr(p, path, name, NULL, 0, flags); +#ifdef PR_USE_XATTR + fail_unless(res < 0, "Failed to handle nonexistent file '%s'", path); + fail_unless(errno == ENOENT, "Expected ENOENT (%d), got %s (%d)", ENOENT, + strerror(errno), errno); + + (void) unlink(fsio_test_path); + fh = pr_fsio_open(fsio_test_path, O_CREAT|O_EXCL|O_WRONLY); + fail_unless(fh != NULL, "Failed to open '%s': %s", fsio_test_path, + strerror(errno)); + pr_fsio_close(fh); + + res = pr_fsio_setxattr(p, path, name, NULL, 0, flags); + if (res < 0) { + fail_unless(errno == ENOTSUP, "Expected ENOTSUP (%d), got %s (%d)", ENOTSUP, + strerror(errno), errno); + } + + (void) unlink(fsio_test_path); +#else + (void) fh; + fail_unless(res < 0, "Failed to handle --disable-xattr"); + fail_unless(errno == ENOSYS, "Expected ENOSYS (%d), got %s (%d)", ENOSYS, + strerror(errno), errno); +#endif /* PR_USE_XATTR */ +} +END_TEST + +START_TEST (fsio_sys_lsetxattr_test) { + int res, flags; + const char *path, *name; + pr_fh_t *fh; + unsigned long fsio_opts; + + res = pr_fsio_lsetxattr(NULL, NULL, NULL, NULL, 0, 0); + fail_unless(res < 0, "Failed to handle null arguments"); + fail_unless(errno == EINVAL, "Expected EINVAL (%d), got %s (%d)", EINVAL, + strerror(errno), errno); + + res = pr_fsio_lsetxattr(p, NULL, NULL, NULL, 0, 0); + fail_unless(res < 0, "Failed to handle null path"); + fail_unless(errno == EINVAL, "Expected EINVAL (%d), got %s (%d)", EINVAL, + strerror(errno), errno); + + path = fsio_test_path; + res = pr_fsio_lsetxattr(p, path, NULL, NULL, 0, 0); + fail_unless(res < 0, "Failed to handle null attribute name"); + fail_unless(errno == EINVAL, "Expected EINVAL (%d), got %s (%d)", EINVAL, + strerror(errno), errno); + + name = "foo.bar"; + flags = PR_FSIO_XATTR_FL_CREATE; + + fsio_opts = pr_fsio_set_options(PR_FSIO_OPT_IGNORE_XATTR); + res = pr_fsio_lsetxattr(p, path, name, NULL, 0, flags); + fail_unless(res < 0, "Failed to handle disabled xattr"); + fail_unless(errno == ENOSYS, "Expected ENOSYS (%d), got %s (%d)", ENOSYS, + strerror(errno), errno); + + pr_fsio_set_options(fsio_opts); + res = pr_fsio_lsetxattr(p, path, name, NULL, 0, flags); +#ifdef PR_USE_XATTR + fail_unless(res < 0, "Failed to handle nonexistent file '%s'", path); + fail_unless(errno == ENOENT, "Expected ENOENT (%d), got %s (%d)", ENOENT, + strerror(errno), errno); + + (void) unlink(fsio_test_path); + fh = pr_fsio_open(fsio_test_path, O_CREAT|O_EXCL|O_WRONLY); + fail_unless(fh != NULL, "Failed to open '%s': %s", fsio_test_path, + strerror(errno)); + pr_fsio_close(fh); + + res = pr_fsio_lsetxattr(p, path, name, NULL, 0, flags); + if (res < 0) { + fail_unless(errno == ENOTSUP, "Expected ENOTSUP (%d), got %s (%d)", ENOTSUP, + strerror(errno), errno); + } + + (void) unlink(fsio_test_path); +#else + (void) fh; + fail_unless(res < 0, "Failed to handle --disable-xattr"); + fail_unless(errno == ENOSYS, "Expected ENOSYS (%d), got %s (%d)", ENOSYS, + strerror(errno), errno); +#endif /* PR_USE_XATTR */ +} +END_TEST + +START_TEST (fsio_sys_fsetxattr_test) { + int res, flags; + pr_fh_t *fh; + const char *name; + unsigned long fsio_opts; + + res = pr_fsio_fsetxattr(NULL, NULL, NULL, NULL, 0, 0); + fail_unless(res < 0, "Failed to handle null arguments"); + fail_unless(errno == EINVAL, "Expected EINVAL (%d), got %s (%d)", EINVAL, + strerror(errno), errno); + + res = pr_fsio_fsetxattr(p, NULL, NULL, NULL, 0, 0); + fail_unless(res < 0, "Failed to handle null file handle"); + fail_unless(errno == EINVAL, "Expected EINVAL (%d), got %s (%d)", EINVAL, + strerror(errno), errno); + + (void) unlink(fsio_test_path); + fh = pr_fsio_open(fsio_test_path, O_CREAT|O_EXCL|O_RDWR); + fail_unless(fh != NULL, "Failed to open '%s': %s", fsio_test_path, + strerror(errno)); + + res = pr_fsio_fsetxattr(p, fh, NULL, NULL, 0, 0); + fail_unless(res < 0, "Failed to handle null attribute name"); + fail_unless(errno == EINVAL, "Expected EINVAL (%d), got %s (%d)", EINVAL, + strerror(errno), errno); + + name = "foo.bar"; + flags = PR_FSIO_XATTR_FL_CREATE; + + fsio_opts = pr_fsio_set_options(PR_FSIO_OPT_IGNORE_XATTR); + res = pr_fsio_fsetxattr(p, fh, name, NULL, 0, flags); + fail_unless(res < 0, "Failed to handle disabled xattr"); + fail_unless(errno == ENOSYS, "Expected ENOSYS (%d), got %s (%d)", ENOSYS, + strerror(errno), errno); + + pr_fsio_set_options(fsio_opts); + res = pr_fsio_fsetxattr(p, fh, name, NULL, 0, flags); +#ifdef PR_USE_XATTR + if (res < 0) { + fail_unless(errno == ENOTSUP, "Expected ENOTSUP (%d), got %s (%d)", ENOTSUP, + strerror(errno), errno); + } + +#else + fail_unless(res < 0, "Failed to handle --disable-xattr"); + fail_unless(errno == ENOSYS, "Expected ENOSYS (%d), got %s (%d)", ENOSYS, + strerror(errno), errno); +#endif /* PR_USE_XATTR */ + + pr_fsio_close(fh); + (void) unlink(fsio_test_path); +} +END_TEST + START_TEST (fsio_sys_mkdir_test) { int res; mode_t mode = 0755; @@ -3482,6 +4103,21 @@ Suite *tests_get_fsio_suite(void) { tcase_add_test(testcase, fsio_sys_utimes_chroot_guard_test); tcase_add_test(testcase, fsio_sys_futimes_test); tcase_add_test(testcase, fsio_sys_fsync_test); + + /* Extended attribute tests */ + tcase_add_test(testcase, fsio_sys_getxattr_test); + tcase_add_test(testcase, fsio_sys_lgetxattr_test); + tcase_add_test(testcase, fsio_sys_fgetxattr_test); + tcase_add_test(testcase, fsio_sys_listxattr_test); + tcase_add_test(testcase, fsio_sys_llistxattr_test); + tcase_add_test(testcase, fsio_sys_flistxattr_test); + tcase_add_test(testcase, fsio_sys_removexattr_test); + tcase_add_test(testcase, fsio_sys_lremovexattr_test); + tcase_add_test(testcase, fsio_sys_fremovexattr_test); + tcase_add_test(testcase, fsio_sys_setxattr_test); + tcase_add_test(testcase, fsio_sys_lsetxattr_test); + tcase_add_test(testcase, fsio_sys_fsetxattr_test); + tcase_add_test(testcase, fsio_sys_mkdir_test); tcase_add_test(testcase, fsio_sys_mkdir_chroot_guard_test); tcase_add_test(testcase, fsio_sys_rmdir_test); diff --git a/tests/api/str.c b/tests/api/str.c index 70d9ebc214..80b87e4a02 100644 --- a/tests/api/str.c +++ b/tests/api/str.c @@ -1262,7 +1262,7 @@ START_TEST (bin2hex_test) { /* Empty string. */ str = (const unsigned char *) "foobar"; expected = ""; - res = pr_str_bin2hex(p, str, 0, 0); + res = pr_str_bin2hex(p, (const unsigned char *) str, 0, 0); fail_unless(res != NULL, "Failed to hexify '%s': %s", str, strerror(errno)); fail_unless(strcmp(res, expected) == 0, "Expected '%s', got '%s'", expected, res); @@ -1322,7 +1322,7 @@ START_TEST (hex2bin_test) { expected[1] = 34; expected[2] = 51; - res = pr_str_hex2bin(p, hex, hex_len, &len); + res = pr_str_hex2bin(p, (const unsigned char *) hex, hex_len, &len); fail_unless(res != NULL, "Failed to unhexify '%s': %s", hex, strerror(errno)); fail_unless(len == expected_len, "Expected len %lu, got %lu", (unsigned long) expected_len, len); @@ -1341,7 +1341,7 @@ START_TEST (hex2bin_test) { expected[4] = 'a'; expected[5] = 'r'; - res = pr_str_hex2bin(p, hex, hex_len, &len); + res = pr_str_hex2bin(p, (const unsigned char *) hex, hex_len, &len); fail_unless(res != NULL, "Failed to unhexify '%s': %s", hex, strerror(errno)); fail_unless(len == expected_len, "Expected len %lu, got %lu", (unsigned long) expected_len, len); @@ -1352,7 +1352,7 @@ START_TEST (hex2bin_test) { hex = (const unsigned char *) "666F6F626172"; hex_len = strlen((char *) hex); - res = pr_str_hex2bin(p, hex, hex_len, &len); + res = pr_str_hex2bin(p, (const unsigned char *) hex, hex_len, &len); fail_unless(res != NULL, "Failed to unhexify '%s': %s", hex, strerror(errno)); fail_unless(len == expected_len, "Expected len %lu, got %lu", (unsigned long) expected_len, len);