├── CREDITS ├── README.md ├── tests ├── 001.phpt ├── 004-create-replace.phpt ├── 002-set-get.phpt └── 003-set-get-link.phpt ├── composer.json ├── config.m4 ├── .gitignore ├── xattr.stub.php ├── REFLECTION ├── php_xattr.h ├── LICENSE ├── xattr_arginfo.h ├── package.xml └── xattr.c /CREDITS: -------------------------------------------------------------------------------- 1 | xattr php extension 2 | Marcin Gibula, Remi Collet -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | This package allows to manipulate extended attributes on filesystems 2 | that support them. 3 | -------------------------------------------------------------------------------- /tests/001.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Check for xattr presence 3 | --SKIPIF-- 4 | 5 | --FILE-- 6 | 9 | --EXPECT-- 10 | xattr extension is available 11 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "pecl/xattr", 3 | "type": "php-ext", 4 | "license": "PHP-3.01", 5 | "description": "Extended attributes", 6 | "require": { 7 | "php": ">= 7.2.0" 8 | }, 9 | "php-ext": { 10 | "extension-name": "xattr", 11 | "configure-options": [] 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /config.m4: -------------------------------------------------------------------------------- 1 | dnl $Id$ 2 | dnl config.m4 for extension xattr 3 | 4 | PHP_ARG_WITH(xattr, for xattr support, 5 | Make sure that the comment is aligned: 6 | [ --with-xattr Include xattr support]) 7 | 8 | if test "$PHP_XATTR" != "no"; then 9 | AC_CHECK_HEADER([sys/xattr.h], [HAVE_XATTR_H="yes"], [HAVE_XATTR_H="no"]) 10 | if test $HAVE_XATTR_H = "no" ; then 11 | AC_MSG_ERROR([You need to install glibc development package]) 12 | fi 13 | 14 | PHP_NEW_EXTENSION(xattr, xattr.c, $ext_shared) 15 | fi 16 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .deps 2 | *.dep 3 | *.lo 4 | *.la 5 | .libs 6 | sendpackagist 7 | Makefile 8 | Makefile.fragments 9 | Makefile.global 10 | Makefile.objects 11 | acinclude.m4 12 | aclocal.m4 13 | autom4te.cache/ 14 | build/ 15 | config.guess 16 | config.h 17 | config.h.in 18 | config.h.in~ 19 | config.log 20 | config.nice 21 | config.status 22 | config.sub 23 | configure 24 | configure~ 25 | configure.in 26 | configure.ac 27 | install-sh 28 | libtool 29 | ltmain.sh 30 | ltmain.sh.backup 31 | missing 32 | mkinstalldirs 33 | run-tests.php 34 | tmp-php.ini 35 | modules 36 | xattr-*.tgz 37 | -------------------------------------------------------------------------------- /tests/004-create-replace.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | set function with create/replace options 3 | --SKIPIF-- 4 | 8 | --FILE-- 9 | 27 | Done 28 | --CLEAN-- 29 | 30 | --EXPECTF-- 31 | 32 | Warning: xattr_set Attribute user.php.test doesn't exists in %s004-create-replace.php on line 7 33 | bool(false) 34 | bool(false) 35 | bool(true) 36 | string(1) "2" 37 | 38 | Warning: xattr_set Attribute user.php.test already exists in %s004-create-replace.php on line 11 39 | bool(false) 40 | string(1) "2" 41 | bool(true) 42 | string(1) "4" 43 | bool(true) 44 | string(1) "5" 45 | Done 46 | -------------------------------------------------------------------------------- /tests/002-set-get.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | set/get/list/remove functions 3 | --SKIPIF-- 4 | 8 | --FILE-- 9 | 37 | Done 38 | --CLEAN-- 39 | 40 | --EXPECT-- 41 | -- Supported 42 | bool(true) 43 | -- Set/Get 44 | bool(true) 45 | string(3) "foo" 46 | string(3) "foo" 47 | string(3) "foo" 48 | bool(true) 49 | string(3) "bar" 50 | bool(true) 51 | string(2) "42" 52 | -- List 53 | array(1) { 54 | [0]=> 55 | string(8) "php.test" 56 | } 57 | bool(true) 58 | -- Remove 59 | bool(true) 60 | bool(false) 61 | Done 62 | -------------------------------------------------------------------------------- /xattr.stub.php: -------------------------------------------------------------------------------- 1 | extension #16 xattr version 1.3.0 ] { 2 | 3 | - Constants [9] { 4 | Constant [ integer XATTR_CREATE ] { 1 } 5 | Constant [ integer XATTR_REPLACE ] { 2 } 6 | Constant [ integer XATTR_DONTFOLLOW ] { 4 } 7 | Constant [ integer XATTR_USER ] { 8 } 8 | Constant [ integer XATTR_ROOT ] { 16 } 9 | Constant [ integer XATTR_TRUSTED ] { 16 } 10 | Constant [ integer XATTR_SYSTEM ] { 32 } 11 | Constant [ integer XATTR_SECURITY ] { 64 } 12 | Constant [ integer XATTR_ALL ] { 128 } 13 | } 14 | 15 | - Functions { 16 | Function [ function xattr_set ] { 17 | 18 | - Parameters [4] { 19 | Parameter #0 [ $path ] 20 | Parameter #1 [ $name ] 21 | Parameter #2 [ $value ] 22 | Parameter #3 [ $flags ] 23 | } 24 | } 25 | Function [ function xattr_get ] { 26 | 27 | - Parameters [3] { 28 | Parameter #0 [ $path ] 29 | Parameter #1 [ $name ] 30 | Parameter #2 [ $flags ] 31 | } 32 | } 33 | Function [ function xattr_remove ] { 34 | 35 | - Parameters [3] { 36 | Parameter #0 [ $path ] 37 | Parameter #1 [ $name ] 38 | Parameter #2 [ $flags ] 39 | } 40 | } 41 | Function [ function xattr_list ] { 42 | 43 | - Parameters [2] { 44 | Parameter #0 [ $path ] 45 | Parameter #1 [ $flags ] 46 | } 47 | } 48 | Function [ function xattr_supported ] { 49 | 50 | - Parameters [2] { 51 | Parameter #0 [ $path ] 52 | Parameter #1 [ $flags ] 53 | } 54 | } 55 | } 56 | } 57 | 58 | -------------------------------------------------------------------------------- /tests/003-set-get-link.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | set/get/list functions and symlink 3 | --SKIPIF-- 4 | 9 | --FILE-- 10 | 34 | Done 35 | --CLEAN-- 36 | 40 | --EXPECT-- 41 | -- Set 42 | bool(true) 43 | bool(true) 44 | bool(true) 45 | -- Get 46 | string(3) "foo" 47 | string(3) "bar" 48 | string(3) "xxx" 49 | -- List 50 | array(3) { 51 | [0]=> 52 | string(16) "security.selinux" 53 | [1]=> 54 | string(17) "trusted.php.test1" 55 | [2]=> 56 | string(17) "trusted.php.test2" 57 | } 58 | array(3) { 59 | [0]=> 60 | string(16) "security.selinux" 61 | [1]=> 62 | string(17) "trusted.php.test1" 63 | [2]=> 64 | string(17) "trusted.php.test2" 65 | } 66 | array(2) { 67 | [0]=> 68 | string(16) "security.selinux" 69 | [1]=> 70 | string(17) "trusted.php.test3" 71 | } 72 | Done 73 | -------------------------------------------------------------------------------- /php_xattr.h: -------------------------------------------------------------------------------- 1 | /* 2 | +----------------------------------------------------------------------+ 3 | | Copyright (c) The PHP Group | 4 | +----------------------------------------------------------------------+ 5 | | This source file is subject to version 3.01 of the PHP license, | 6 | | that is bundled with this package in the file LICENSE, and is | 7 | | available through the world-wide-web at the following url: | 8 | | http://www.php.net/license/3_01.txt. | 9 | | If you did not receive a copy of the PHP license and are unable to | 10 | | obtain it through the world-wide-web, please send a note to | 11 | | license@php.net so we can mail you a copy immediately. | 12 | +----------------------------------------------------------------------+ 13 | | Author: Marcin Gibula | 14 | | Remi Collet | 15 | +----------------------------------------------------------------------+ 16 | */ 17 | 18 | #ifndef PHP_XATTR_H 19 | #define PHP_XATTR_H 20 | 21 | extern zend_module_entry xattr_module_entry; 22 | #define phpext_xattr_ptr &xattr_module_entry 23 | 24 | #define PHP_XATTR_VERSION "1.4.1" 25 | 26 | #ifdef PHP_WIN32 27 | #define PHP_XATTR_API __declspec(dllexport) 28 | #else 29 | #define PHP_XATTR_API 30 | #endif 31 | 32 | #ifdef ZTS 33 | #include "TSRM.h" 34 | #endif 35 | 36 | PHP_MINIT_FUNCTION(xattr); 37 | PHP_MINFO_FUNCTION(xattr); 38 | 39 | PHP_FUNCTION(xattr_set); 40 | PHP_FUNCTION(xattr_get); 41 | PHP_FUNCTION(xattr_remove); 42 | PHP_FUNCTION(xattr_list); 43 | PHP_FUNCTION(xattr_supported); 44 | 45 | #endif /* PHP_XATTR_H */ 46 | 47 | 48 | /* 49 | * Local variables: 50 | * tab-width: 4 51 | * c-basic-offset: 4 52 | * End: 53 | * vim600: noet sw=4 ts=4 fdm=marker 54 | * vim<600: noet sw=4 ts=4 55 | */ 56 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------- 2 | The PHP License, version 3.01 3 | Copyright (c) 1999 - 2014 The PHP Group. All rights reserved. 4 | -------------------------------------------------------------------- 5 | 6 | Redistribution and use in source and binary forms, with or without 7 | modification, is permitted provided that the following conditions 8 | are met: 9 | 10 | 1. Redistributions of source code must retain the above copyright 11 | notice, this list of conditions and the following disclaimer. 12 | 13 | 2. Redistributions in binary form must reproduce the above copyright 14 | notice, this list of conditions and the following disclaimer in 15 | the documentation and/or other materials provided with the 16 | distribution. 17 | 18 | 3. The name "PHP" must not be used to endorse or promote products 19 | derived from this software without prior written permission. For 20 | written permission, please contact group@php.net. 21 | 22 | 4. Products derived from this software may not be called "PHP", nor 23 | may "PHP" appear in their name, without prior written permission 24 | from group@php.net. You may indicate that your software works in 25 | conjunction with PHP by saying "Foo for PHP" instead of calling 26 | it "PHP Foo" or "phpfoo" 27 | 28 | 5. The PHP Group may publish revised and/or new versions of the 29 | license from time to time. Each version will be given a 30 | distinguishing version number. 31 | Once covered code has been published under a particular version 32 | of the license, you may always continue to use it under the terms 33 | of that version. You may also choose to use such covered code 34 | under the terms of any subsequent version of the license 35 | published by the PHP Group. No one other than the PHP Group has 36 | the right to modify the terms applicable to covered code created 37 | under this License. 38 | 39 | 6. Redistributions of any form whatsoever must retain the following 40 | acknowledgment: 41 | "This product includes PHP software, freely available from 42 | ". 43 | 44 | THIS SOFTWARE IS PROVIDED BY THE PHP DEVELOPMENT TEAM ``AS IS'' AND 45 | ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, 46 | THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 47 | PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE PHP 48 | DEVELOPMENT TEAM OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 49 | INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 50 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 51 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 52 | HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 53 | STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 54 | ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED 55 | OF THE POSSIBILITY OF SUCH DAMAGE. 56 | 57 | -------------------------------------------------------------------- 58 | 59 | This software consists of voluntary contributions made by many 60 | individuals on behalf of the PHP Group. 61 | 62 | The PHP Group can be contacted via Email at group@php.net. 63 | 64 | For more information on the PHP Group and the PHP project, 65 | please see . 66 | 67 | PHP includes the Zend Engine, freely available at 68 | . 69 | -------------------------------------------------------------------------------- /xattr_arginfo.h: -------------------------------------------------------------------------------- 1 | /* This is a generated file, edit the .stub.php file instead. 2 | * Stub hash: db4d314e2ba7f19a7f82b6d903a94336137fe46a */ 3 | 4 | ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_xattr_set, 0, 3, _IS_BOOL, 0) 5 | ZEND_ARG_TYPE_INFO(0, path, IS_STRING, 0) 6 | ZEND_ARG_TYPE_INFO(0, name, IS_STRING, 0) 7 | ZEND_ARG_TYPE_INFO(0, value, IS_STRING, 0) 8 | ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, flag, IS_LONG, 0, "0") 9 | ZEND_END_ARG_INFO() 10 | 11 | #if PHP_MAJOR_VERSION < 8 12 | ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_xattr_get, 0, 2, IS_STRING, 0) 13 | ZEND_ARG_TYPE_INFO(0, path, IS_STRING, 0) 14 | ZEND_ARG_TYPE_INFO(0, name, IS_STRING, 0) 15 | ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, flags, IS_LONG, 0, "0") 16 | ZEND_END_ARG_INFO() 17 | #endif 18 | 19 | #if !(PHP_MAJOR_VERSION < 8) 20 | ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_xattr_get, 0, 2, MAY_BE_STRING|MAY_BE_FALSE) 21 | ZEND_ARG_TYPE_INFO(0, path, IS_STRING, 0) 22 | ZEND_ARG_TYPE_INFO(0, name, IS_STRING, 0) 23 | ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, flags, IS_LONG, 0, "0") 24 | ZEND_END_ARG_INFO() 25 | #endif 26 | 27 | ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_xattr_supported, 0, 1, _IS_BOOL, 0) 28 | ZEND_ARG_TYPE_INFO(0, path, IS_STRING, 0) 29 | ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, flags, IS_LONG, 0, "0") 30 | ZEND_END_ARG_INFO() 31 | 32 | ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_xattr_remove, 0, 2, _IS_BOOL, 0) 33 | ZEND_ARG_TYPE_INFO(0, path, IS_STRING, 0) 34 | ZEND_ARG_TYPE_INFO(0, name, IS_STRING, 0) 35 | ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, flags, IS_LONG, 0, "0") 36 | ZEND_END_ARG_INFO() 37 | 38 | #if PHP_MAJOR_VERSION < 8 39 | ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_xattr_list, 0, 1, IS_ARRAY, 0) 40 | ZEND_ARG_TYPE_INFO(0, path, IS_STRING, 0) 41 | ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, flags, IS_LONG, 0, "0") 42 | ZEND_END_ARG_INFO() 43 | #endif 44 | 45 | #if !(PHP_MAJOR_VERSION < 8) 46 | ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_xattr_list, 0, 1, MAY_BE_ARRAY|MAY_BE_FALSE) 47 | ZEND_ARG_TYPE_INFO(0, path, IS_STRING, 0) 48 | ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, flags, IS_LONG, 0, "0") 49 | ZEND_END_ARG_INFO() 50 | #endif 51 | 52 | 53 | ZEND_FUNCTION(xattr_set); 54 | #if PHP_MAJOR_VERSION < 8 55 | ZEND_FUNCTION(xattr_get); 56 | #endif 57 | #if !(PHP_MAJOR_VERSION < 8) 58 | ZEND_FUNCTION(xattr_get); 59 | #endif 60 | ZEND_FUNCTION(xattr_supported); 61 | ZEND_FUNCTION(xattr_remove); 62 | #if PHP_MAJOR_VERSION < 8 63 | ZEND_FUNCTION(xattr_list); 64 | #endif 65 | #if !(PHP_MAJOR_VERSION < 8) 66 | ZEND_FUNCTION(xattr_list); 67 | #endif 68 | 69 | 70 | static const zend_function_entry ext_functions[] = { 71 | ZEND_FE(xattr_set, arginfo_xattr_set) 72 | #if PHP_MAJOR_VERSION < 8 73 | ZEND_FE(xattr_get, arginfo_xattr_get) 74 | #endif 75 | #if !(PHP_MAJOR_VERSION < 8) 76 | ZEND_FE(xattr_get, arginfo_xattr_get) 77 | #endif 78 | ZEND_FE(xattr_supported, arginfo_xattr_supported) 79 | ZEND_FE(xattr_remove, arginfo_xattr_remove) 80 | #if PHP_MAJOR_VERSION < 8 81 | ZEND_FE(xattr_list, arginfo_xattr_list) 82 | #endif 83 | #if !(PHP_MAJOR_VERSION < 8) 84 | ZEND_FE(xattr_list, arginfo_xattr_list) 85 | #endif 86 | ZEND_FE_END 87 | }; 88 | 89 | static void register_xattr_symbols(int module_number) 90 | { 91 | REGISTER_LONG_CONSTANT("XATTR_CREATE", XATTR_CREATE, CONST_CS | CONST_PERSISTENT); 92 | REGISTER_LONG_CONSTANT("XATTR_REPLACE", XATTR_REPLACE, CONST_CS | CONST_PERSISTENT); 93 | REGISTER_LONG_CONSTANT("XATTR_DONTFOLLOW", XATTR_DONTFOLLOW, CONST_CS | CONST_PERSISTENT); 94 | REGISTER_LONG_CONSTANT("XATTR_USER", XATTR_USER, CONST_CS | CONST_PERSISTENT); 95 | REGISTER_LONG_CONSTANT("XATTR_ROOT", XATTR_ROOT, CONST_CS | CONST_PERSISTENT); 96 | REGISTER_LONG_CONSTANT("XATTR_TRUSTED", XATTR_TRUSTED, CONST_CS | CONST_PERSISTENT); 97 | REGISTER_LONG_CONSTANT("XATTR_SYSTEM", XATTR_SYSTEM, CONST_CS | CONST_PERSISTENT); 98 | REGISTER_LONG_CONSTANT("XATTR_SECURITY", XATTR_SECURITY, CONST_CS | CONST_PERSISTENT); 99 | REGISTER_LONG_CONSTANT("XATTR_ALL", XATTR_ALL, CONST_CS | CONST_PERSISTENT); 100 | } 101 | -------------------------------------------------------------------------------- /package.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | xattr 7 | pecl.php.net 8 | Extended attributes. 9 | This package allows to manipulate extended attributes on filesystems that support them. 10 | 11 | 12 | Remi Collet 13 | remi 14 | remi@php.net 15 | yes 16 | 17 | 18 | Marcin Gibula 19 | mg 20 | mg@iceni.pl 21 | yes 22 | 23 | 2025-09-17 24 | 25 | 1.4.1 26 | 1.2.0 27 | 28 | 29 | stable 30 | stable 31 | 32 | PHP-3.01 33 | 34 | - functions and constants from stub 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 7.2.0 57 | 58 | 59 | 1.10 60 | 61 | 62 | 63 | xattr 64 | 65 | 66 | 67 | 68 | 69 | 2020-04-24 70 | 71 | 1.4.0 72 | 1.2.0 73 | 74 | 75 | stable 76 | stable 77 | 78 | PHP License 79 | 80 | - drop dupport for PHP older than 7.2 81 | - add type hinting in reflection 82 | 83 | 84 | 85 | 2020-04-22 86 | 87 | 1.3.1 88 | 1.2.0 89 | 90 | 91 | stable 92 | stable 93 | 94 | PHP License 95 | 96 | - preliminary support for PHP 8 97 | 98 | 99 | 100 | 2016-03-07 101 | 102 | 103 | 1.3.0 104 | 1.2.0 105 | 106 | 107 | stable 108 | stable 109 | 110 | PHP License 111 | 112 | - drop support for PHP older than 5.3 113 | - use glibc instead of libattr 114 | - add XATTR_TRUSTED, XATTR_USER, XATTR_SYSTEM, XATTR_SECURITY and XATTR_ALL 115 | - allow to manage attributes in all namespaces 116 | - more tests 117 | 118 | 119 | 120 | 2015-04-19 121 | 122 | 1.2.1 123 | 1.2.0 124 | 125 | 126 | stable 127 | stable 128 | 129 | PHP License 130 | 131 | - PHP 7 compatibility (Remi) 132 | - add LICENSE to documentation 133 | - add arginfo for reflection (Remi) 134 | - add a test for set/get/list/remove functions (Remi) 135 | 136 | 137 | 138 | 139 | 1.2.0 140 | 1.2.0 141 | 142 | 143 | stable 144 | stable 145 | 146 | 2012-12-13 147 | PHP License 148 | Fixed build on PHP 5.3+ 149 | 150 | 151 | 152 | 153 | 1.1.0 154 | 1.1.0 155 | 156 | 157 | stable 158 | stable 159 | 160 | 2008-07-01 161 | PHP License 162 | Enforce open_basedir and safe_mode checks. 163 | 164 | 165 | 166 | 1.0 167 | 1.0 168 | 169 | 170 | stable 171 | stable 172 | 173 | 2004-08-19 174 | PHP License 175 | New function added: xattr_supported. 176 | 177 | 178 | 179 | 0.9 180 | 0.9 181 | 182 | 183 | beta 184 | beta 185 | 186 | 2004-08-14 187 | PHP License 188 | First public release. Requires libattr from Linux XFS project. 189 | 190 | 191 | 192 | -------------------------------------------------------------------------------- /xattr.c: -------------------------------------------------------------------------------- 1 | /* 2 | +----------------------------------------------------------------------+ 3 | | Copyright (c) The PHP Group | 4 | +----------------------------------------------------------------------+ 5 | | This source file is subject to version 3.01 of the PHP license, | 6 | | that is bundled with this package in the file LICENSE, and is | 7 | | available through the world-wide-web at the following url: | 8 | | http://www.php.net/license/3_01.txt | 9 | | If you did not receive a copy of the PHP license and are unable to | 10 | | obtain it through the world-wide-web, please send a note to | 11 | | license@php.net so we can mail you a copy immediately. | 12 | +----------------------------------------------------------------------+ 13 | | Author: Marcin Gibula | 14 | | Remi Collet | 15 | +----------------------------------------------------------------------+ 16 | */ 17 | 18 | #ifdef HAVE_CONFIG_H 19 | #include "config.h" 20 | #endif 21 | 22 | #define XATTR_BUFFER_SIZE 1024 /* Initial size for internal buffers, feel free to change it */ 23 | 24 | /* XATTR_CREATE=1, XATTR_REPLACE=2 */ 25 | 26 | #define XATTR_DONTFOLLOW (1<<2) 27 | 28 | #define XATTR_USER (1<<3) 29 | #define XATTR_TRUSTED (1<<4) 30 | #define XATTR_SYSTEM (1<<5) 31 | #define XATTR_SECURITY (1<<6) 32 | #define XATTR_ALL (1<<7) 33 | #define XATTR_MASK (XATTR_TRUSTED|XATTR_SYSTEM|XATTR_SECURITY|XATTR_USER|XATTR_ALL) 34 | #define XATTR_ROOT XATTR_TRUSTED 35 | 36 | /* These prefixes have been taken from attr(5) man page */ 37 | #define XATTR_USER_PREFIX "user." 38 | #define XATTR_TRUSTED_PREFIX "trusted." 39 | #define XATTR_SYSTEM_PREFIX "system." 40 | #define XATTR_SECURITY_PREFIX "security." 41 | 42 | #include "php.h" 43 | #include "php_ini.h" 44 | #include "ext/standard/info.h" 45 | #include "php_xattr.h" 46 | 47 | #include 48 | 49 | #include 50 | #include 51 | 52 | #if PHP_VERSION_ID < 80000 53 | #define ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(pass_by_ref, name, type_hint, allow_null, default_value) \ 54 | ZEND_ARG_TYPE_INFO(pass_by_ref, name, type_hint, allow_null) 55 | #endif 56 | 57 | /* file generated with PHP 8+ used on PHP 7 thanks to above compatibility layer */ 58 | #include "xattr_arginfo.h" 59 | 60 | /* {{{ xattr_module_entry 61 | */ 62 | zend_module_entry xattr_module_entry = { 63 | STANDARD_MODULE_HEADER, 64 | "xattr", 65 | ext_functions, 66 | PHP_MINIT(xattr), 67 | NULL, 68 | NULL, 69 | NULL, 70 | PHP_MINFO(xattr), 71 | PHP_XATTR_VERSION, 72 | STANDARD_MODULE_PROPERTIES 73 | }; 74 | /* }}} */ 75 | 76 | #ifdef COMPILE_DL_XATTR 77 | ZEND_GET_MODULE(xattr) 78 | #endif 79 | 80 | /* {{{ PHP_MINIT_FUNCTION 81 | */ 82 | PHP_MINIT_FUNCTION(xattr) 83 | { 84 | register_xattr_symbols(module_number); 85 | 86 | return SUCCESS; 87 | } 88 | /* }}} */ 89 | 90 | /* {{{ PHP_MINFO_FUNCTION 91 | */ 92 | PHP_MINFO_FUNCTION(xattr) 93 | { 94 | php_info_print_table_start(); 95 | php_info_print_table_row(2, "xattr support", "enabled"); 96 | php_info_print_table_row(2, "PECL module version", PHP_XATTR_VERSION); 97 | php_info_print_table_end(); 98 | } 99 | /* }}} */ 100 | 101 | #define check_prefix(flags) add_prefix(NULL, flags) 102 | 103 | static char *add_prefix(char *name, zend_long flags) { 104 | char *ret; 105 | 106 | if ((flags & XATTR_MASK) > 0 && 107 | (flags & XATTR_MASK) != XATTR_TRUSTED && 108 | (flags & XATTR_MASK) != XATTR_SYSTEM && 109 | (flags & XATTR_MASK) != XATTR_SECURITY && 110 | (flags & XATTR_MASK) != XATTR_USER && 111 | (flags & XATTR_MASK) != XATTR_ALL) { 112 | php_error(E_NOTICE, "%s Bad option, single namespace expected", get_active_function_name()); 113 | } 114 | if (!name) { 115 | return NULL; 116 | } 117 | if ((flags & XATTR_MASK) == XATTR_ALL && !strchr(name, '.')) { 118 | php_error(E_NOTICE, "%s Bad option, missing namespace, XATTR_ALL ignored", get_active_function_name()); 119 | } 120 | 121 | if (flags & XATTR_TRUSTED) { 122 | spprintf(&ret, 0, "%s%s", XATTR_TRUSTED_PREFIX, name); 123 | 124 | } else if (flags & XATTR_SYSTEM) { 125 | spprintf(&ret, 0, "%s%s", XATTR_SYSTEM_PREFIX, name); 126 | 127 | } else if (flags & XATTR_SECURITY) { 128 | spprintf(&ret, 0, "%s%s", XATTR_SECURITY_PREFIX, name); 129 | 130 | } else if ((flags & XATTR_ALL) && strchr(name, '.')) { 131 | /* prefix provided in input */ 132 | ret = name; 133 | 134 | } else { 135 | spprintf(&ret, 0, "%s%s", XATTR_USER_PREFIX, name); 136 | } 137 | return ret; 138 | } 139 | 140 | /* {{{ proto bool xattr_set(string path, string name, string value [, int flags]) 141 | Set an extended attribute of file */ 142 | PHP_FUNCTION(xattr_set) 143 | { 144 | char *attr_name = NULL, *prefixed_name; 145 | char *attr_value = NULL; 146 | char *path = NULL; 147 | int error; 148 | zend_long flags = 0; 149 | size_t tmp, value_len; 150 | 151 | if (zend_parse_parameters(ZEND_NUM_ARGS(), "sss|l", &path, &tmp, &attr_name, &tmp, &attr_value, &value_len, &flags) == FAILURE) { 152 | return; 153 | } 154 | 155 | /* Enforce open_basedir and safe_mode */ 156 | if (php_check_open_basedir(path)) { 157 | RETURN_FALSE; 158 | } 159 | 160 | prefixed_name = add_prefix(attr_name, flags); 161 | /* Attempt to set an attribute, warn if failed. */ 162 | if (flags & XATTR_DONTFOLLOW) { 163 | error = lsetxattr(path, prefixed_name, attr_value, (int)value_len, (int)(flags & (XATTR_CREATE | XATTR_REPLACE))); 164 | } else { 165 | error = setxattr(path, prefixed_name, attr_value, (int)value_len, (int)(flags & (XATTR_CREATE | XATTR_REPLACE))); 166 | } 167 | if (error == -1) { 168 | switch (errno) { 169 | case E2BIG: 170 | php_error(E_WARNING, "%s The value of the given attribute is too large", get_active_function_name()); 171 | break; 172 | case EPERM: 173 | case EACCES: 174 | php_error(E_WARNING, "%s Permission denied", get_active_function_name()); 175 | break; 176 | case EOPNOTSUPP: 177 | php_error(E_WARNING, "%s Operation not supported", get_active_function_name()); 178 | break; 179 | case ENOENT: 180 | case ENOTDIR: 181 | php_error(E_WARNING, "%s File %s doesn't exists", get_active_function_name(), path); 182 | break; 183 | case EEXIST: 184 | php_error(E_WARNING, "%s Attribute %s already exists", get_active_function_name(), prefixed_name); 185 | break; 186 | case ENODATA: 187 | php_error(E_WARNING, "%s Attribute %s doesn't exists", get_active_function_name(), prefixed_name); 188 | break; 189 | } 190 | 191 | RETVAL_FALSE; 192 | } else { 193 | RETVAL_TRUE; 194 | } 195 | if (prefixed_name != attr_name) { 196 | efree(prefixed_name); 197 | } 198 | } 199 | /* }}} */ 200 | 201 | /* {{{ proto string xattr_get(string path, string name [, int flags]) 202 | Returns a value of an extended attribute */ 203 | PHP_FUNCTION(xattr_get) 204 | { 205 | char *attr_name = NULL, *prefixed_name; 206 | char *attr_value = NULL; 207 | char *path = NULL; 208 | size_t tmp; 209 | zend_long flags = 0; 210 | size_t buffer_size; 211 | 212 | if (zend_parse_parameters(ZEND_NUM_ARGS(), "ss|l", &path, &tmp, &attr_name, &tmp, &flags) == FAILURE) { 213 | return; 214 | } 215 | 216 | /* Enforce open_basedir and safe_mode */ 217 | if (php_check_open_basedir(path)) { 218 | RETURN_FALSE; 219 | } 220 | 221 | prefixed_name = add_prefix(attr_name, flags); 222 | 223 | /* 224 | * If buffer is too small then attr_get sets errno to E2BIG and tells us 225 | * how many bytes are required by setting buffer_size variable. 226 | */ 227 | if (flags & XATTR_DONTFOLLOW) { 228 | buffer_size = lgetxattr(path, prefixed_name, attr_value, 0); 229 | } else { 230 | buffer_size = getxattr(path, prefixed_name, attr_value, 0); 231 | } 232 | if (buffer_size != (size_t)-1) { 233 | attr_value = emalloc(buffer_size+1); 234 | 235 | if (flags & XATTR_DONTFOLLOW) { 236 | buffer_size = lgetxattr(path, prefixed_name, attr_value, buffer_size); 237 | } else { 238 | buffer_size = getxattr(path, prefixed_name, attr_value, buffer_size); 239 | } 240 | attr_value[buffer_size] = 0; 241 | } 242 | 243 | if (prefixed_name != attr_name) { 244 | efree(prefixed_name); 245 | } 246 | 247 | /* Return a string if everything is ok */ 248 | if (buffer_size != (size_t)-1) { 249 | RETVAL_STRINGL(attr_value, buffer_size); /* copy + free instead of realloc */ 250 | efree(attr_value); 251 | return; 252 | } 253 | 254 | /* Give warning for some common error conditions */ 255 | switch (errno) { 256 | case ENODATA: 257 | break; 258 | case ENOENT: 259 | case ENOTDIR: 260 | php_error(E_WARNING, "%s File %s doesn't exists", get_active_function_name(), path); 261 | break; 262 | case EPERM: 263 | case EACCES: 264 | php_error(E_WARNING, "%s Permission denied", get_active_function_name()); 265 | break; 266 | case EOPNOTSUPP: 267 | php_error(E_WARNING, "%s Operation not supported", get_active_function_name()); 268 | break; 269 | } 270 | 271 | RETURN_FALSE; 272 | } 273 | /* }}} */ 274 | 275 | /* {{{ proto bool xattr_supported(string path [, int flags]) 276 | Checks if filesystem supports extended attributes */ 277 | PHP_FUNCTION(xattr_supported) 278 | { 279 | char *buffer="", *path = NULL; 280 | int error; 281 | size_t tmp; 282 | zend_long flags = 0; 283 | 284 | if (zend_parse_parameters(ZEND_NUM_ARGS(), "s|l", &path, &tmp, &flags) == FAILURE) { 285 | return; 286 | } 287 | 288 | /* Enforce open_basedir and safe_mode */ 289 | if (php_check_open_basedir(path)) { 290 | RETURN_NULL(); 291 | } 292 | 293 | /* Is "user.test.is.supported" a good name? */ 294 | if (flags & XATTR_DONTFOLLOW) { 295 | error = lgetxattr(path, "user.test.is.supported", buffer, 0); 296 | } else { 297 | error = getxattr(path, "user.test.is.supported", buffer, 0); 298 | } 299 | 300 | if (error >= 0) { 301 | RETURN_TRUE; 302 | } 303 | 304 | switch (errno) { 305 | case ENODATA: 306 | RETURN_TRUE; 307 | case ENOTSUP: 308 | RETURN_FALSE; 309 | case ENOENT: 310 | case ENOTDIR: 311 | php_error(E_WARNING, "%s File %s doesn't exists", get_active_function_name(), path); 312 | break; 313 | case EACCES: 314 | php_error(E_WARNING, "%s Permission denied", get_active_function_name()); 315 | break; 316 | } 317 | 318 | RETURN_NULL(); 319 | } 320 | /* }}} */ 321 | 322 | /* {{{ proto bool xattr_remove(string path, string name [, int flags]) 323 | Remove an extended attribute of file */ 324 | PHP_FUNCTION(xattr_remove) 325 | { 326 | char *attr_name = NULL, *prefixed_name; 327 | char *path = NULL; 328 | int error; 329 | size_t tmp; 330 | zend_long flags = 0; 331 | 332 | if (zend_parse_parameters(ZEND_NUM_ARGS(), "ss|l", &path, &tmp, &attr_name, &tmp, &flags) == FAILURE) { 333 | return; 334 | } 335 | 336 | /* Enforce open_basedir and safe_mode */ 337 | if (php_check_open_basedir(path)) { 338 | RETURN_FALSE; 339 | } 340 | 341 | prefixed_name = add_prefix(attr_name, flags); 342 | 343 | /* Attempt to remove an attribute, warn if failed. */ 344 | if (flags & XATTR_DONTFOLLOW) { 345 | error = lremovexattr(path, prefixed_name); 346 | } else { 347 | error = removexattr(path, prefixed_name); 348 | } 349 | if (prefixed_name != attr_name) { 350 | efree(prefixed_name); 351 | } 352 | 353 | if (error == -1) { 354 | switch (errno) { 355 | case E2BIG: 356 | php_error(E_WARNING, "%s The value of the given attribute is too large", get_active_function_name()); 357 | break; 358 | case EPERM: 359 | case EACCES: 360 | php_error(E_WARNING, "%s Permission denied", get_active_function_name()); 361 | break; 362 | case EOPNOTSUPP: 363 | php_error(E_WARNING, "%s Operation not supported", get_active_function_name()); 364 | break; 365 | case ENOENT: 366 | case ENOTDIR: 367 | php_error(E_WARNING, "%s File %s doesn't exists", get_active_function_name(), path); 368 | break; 369 | } 370 | 371 | RETURN_FALSE; 372 | } 373 | 374 | RETURN_TRUE; 375 | } 376 | /* }}} */ 377 | 378 | /* {{{ proto array xattr_list(string path [, int flags]) 379 | Get list of extended attributes of file */ 380 | PHP_FUNCTION(xattr_list) 381 | { 382 | char *buffer, *path = NULL; 383 | char *p, *prefix; 384 | int error; 385 | size_t tmp; 386 | zend_long flags = 0; 387 | size_t i = 0, buffer_size = XATTR_BUFFER_SIZE; 388 | size_t len, prefix_len; 389 | 390 | if (zend_parse_parameters(ZEND_NUM_ARGS(), "s|l", &path, &tmp, &flags) == FAILURE) { 391 | return; 392 | } 393 | 394 | check_prefix(flags); 395 | 396 | /* Enforce open_basedir and safe_mode */ 397 | if (php_check_open_basedir(path)) { 398 | RETURN_FALSE; 399 | } 400 | 401 | buffer = emalloc(buffer_size); 402 | 403 | /* Loop is required to get a list reliably */ 404 | do { 405 | /* 406 | * Call to this function with zero size buffer will return us 407 | * required size of our buffer in return (or an error). 408 | */ 409 | if (flags & XATTR_DONTFOLLOW) { 410 | error = llistxattr(path, buffer, 0); 411 | } else { 412 | error = listxattr(path, buffer, 0); 413 | } 414 | 415 | /* Print warning on common errors */ 416 | if (error == -1) { 417 | switch (errno) { 418 | case ENOTSUP: 419 | php_error(E_WARNING, "%s Operation not supported", get_active_function_name()); 420 | break; 421 | case ENOENT: 422 | case ENOTDIR: 423 | php_error(E_WARNING, "%s File %s doesn't exists", get_active_function_name(), path); 424 | break; 425 | case EACCES: 426 | php_error(E_WARNING, "%s Permission denied", get_active_function_name()); 427 | break; 428 | } 429 | 430 | efree(buffer); 431 | RETURN_FALSE; 432 | } 433 | 434 | /* Resize buffer to the required size */ 435 | buffer_size = error; 436 | buffer = erealloc(buffer, buffer_size); 437 | 438 | if (flags & XATTR_DONTFOLLOW) { 439 | error = llistxattr(path, buffer, buffer_size); 440 | } else { 441 | error = listxattr(path, buffer, buffer_size); 442 | } 443 | 444 | /* 445 | * Preceding functions may fail if extended attributes 446 | * have been changed after we read required buffer size. 447 | * That's why we will retry if errno is ERANGE. 448 | */ 449 | } while (error == -1 && errno == ERANGE); 450 | 451 | /* If there is still an error and it's not ERANGE then return false */ 452 | if (error == -1) { 453 | efree(buffer); 454 | RETURN_FALSE; 455 | } 456 | 457 | buffer_size = error; 458 | buffer = erealloc(buffer, buffer_size); 459 | 460 | array_init(return_value); 461 | p = buffer; 462 | 463 | /* 464 | * Root namespace has the prefix "trusted." and users namespace "user.". 465 | */ 466 | if (flags & XATTR_SYSTEM) { 467 | prefix = XATTR_SYSTEM_PREFIX; 468 | } else if (flags & XATTR_SECURITY) { 469 | prefix = XATTR_SECURITY_PREFIX; 470 | } else if (flags & XATTR_TRUSTED) { 471 | prefix = XATTR_TRUSTED_PREFIX; 472 | } else { 473 | prefix = XATTR_USER_PREFIX; 474 | } 475 | 476 | prefix_len = strlen(prefix); 477 | 478 | /* 479 | * We go through the whole list and add entries beginning with selected 480 | * prefix to the return_value array. 481 | */ 482 | while (i != buffer_size) { 483 | len = strlen(p) + 1; /* +1 for NULL */ 484 | if (flags & XATTR_ALL) { 485 | add_next_index_stringl(return_value, p, len - 1); 486 | } else if (strstr(p, prefix) == p) { 487 | add_next_index_stringl(return_value, p + prefix_len, len - 1 - prefix_len); 488 | } 489 | 490 | p += len; 491 | i += len; 492 | } 493 | 494 | efree(buffer); 495 | } 496 | /* }}} */ 497 | 498 | /* 499 | * Local variables: 500 | * tab-width: 4 501 | * c-basic-offset: 4 502 | * End: 503 | * vim600: noet sw=4 ts=4 fdm=marker 504 | * vim<600: noet sw=4 ts=4 505 | */ 506 | --------------------------------------------------------------------------------