2007-11-17 04:07:11 +03:00
/*
2003-08-28 00:04:23 +04:00
* CAP VFS module for Samba 3. x Version 0.3
*
* Copyright ( C ) Tim Potter , 1999 - 2000
* Copyright ( C ) Alexander Bokovoy , 2002 - 2003
* Copyright ( C ) Stefan ( metze ) Metzmacher , 2003
* Copyright ( C ) TAKAHASHI Motonobu ( monyo ) , 2003
2007-11-17 04:07:11 +03:00
* Copyright ( C ) Jeremy Allison , 2007
2003-08-28 00:04:23 +04:00
*
* 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
2007-07-09 23:25:36 +04:00
* the Free Software Foundation ; either version 3 of the License , or
2003-08-28 00:04:23 +04:00
* ( at your option ) any later version .
2007-11-17 04:07:11 +03:00
*
2003-08-28 00:04:23 +04:00
* This program is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details .
2007-11-17 04:07:11 +03:00
*
2003-08-28 00:04:23 +04:00
* You should have received a copy of the GNU General Public License
2007-07-10 09:23:25 +04:00
* along with this program ; if not , see < http : //www.gnu.org/licenses/>.
2003-08-28 00:04:23 +04:00
*/
# include "includes.h"
2011-03-23 00:34:22 +03:00
# include "smbd/smbd.h"
2003-08-28 00:04:23 +04:00
/* cap functions */
2007-11-17 04:07:11 +03:00
static char * capencode ( TALLOC_CTX * ctx , const char * from ) ;
static char * capdecode ( TALLOC_CTX * ctx , const char * from ) ;
2003-08-28 00:04:23 +04:00
2017-05-23 20:40:47 +03:00
static uint64_t cap_disk_free ( vfs_handle_struct * handle ,
const struct smb_filename * smb_fname ,
uint64_t * bsize ,
uint64_t * dfree ,
uint64_t * dsize )
2003-08-28 00:04:23 +04:00
{
2017-05-23 20:40:47 +03:00
char * capname = capencode ( talloc_tos ( ) , smb_fname - > base_name ) ;
struct smb_filename * cap_smb_fname = NULL ;
2007-11-17 04:07:11 +03:00
2017-05-23 20:40:47 +03:00
if ( ! capname ) {
errno = ENOMEM ;
return ( uint64_t ) - 1 ;
}
cap_smb_fname = synthetic_smb_fname ( talloc_tos ( ) ,
capname ,
NULL ,
NULL ,
2020-04-30 12:48:32 +03:00
smb_fname - > twrp ,
2017-05-23 20:40:47 +03:00
smb_fname - > flags ) ;
if ( cap_smb_fname = = NULL ) {
TALLOC_FREE ( capname ) ;
2007-11-17 04:07:11 +03:00
errno = ENOMEM ;
2008-10-14 03:59:36 +04:00
return ( uint64_t ) - 1 ;
2007-11-17 04:07:11 +03:00
}
2017-05-23 20:40:47 +03:00
return SMB_VFS_NEXT_DISK_FREE ( handle , cap_smb_fname ,
bsize , dfree , dsize ) ;
2003-08-28 00:04:23 +04:00
}
2017-06-01 21:45:25 +03:00
static int cap_get_quota ( vfs_handle_struct * handle ,
const struct smb_filename * smb_fname ,
enum SMB_QUOTA_TYPE qtype ,
unid_t id ,
SMB_DISK_QUOTA * dq )
2016-01-10 15:10:10 +03:00
{
2017-06-01 21:45:25 +03:00
char * cappath = capencode ( talloc_tos ( ) , smb_fname - > base_name ) ;
struct smb_filename * cap_smb_fname = NULL ;
2016-01-10 15:10:10 +03:00
if ( ! cappath ) {
errno = ENOMEM ;
return - 1 ;
}
2017-06-01 21:45:25 +03:00
cap_smb_fname = synthetic_smb_fname ( talloc_tos ( ) ,
cappath ,
NULL ,
NULL ,
2020-04-30 12:48:32 +03:00
smb_fname - > twrp ,
2017-06-01 21:45:25 +03:00
smb_fname - > flags ) ;
if ( cap_smb_fname = = NULL ) {
TALLOC_FREE ( cappath ) ;
errno = ENOMEM ;
return - 1 ;
}
return SMB_VFS_NEXT_GET_QUOTA ( handle , cap_smb_fname , qtype , id , dq ) ;
2016-01-10 15:10:10 +03:00
}
2012-03-28 06:18:14 +04:00
static struct dirent * cap_readdir ( vfs_handle_struct * handle ,
2020-11-22 15:57:27 +03:00
struct files_struct * dirfsp ,
DIR * dirp ,
SMB_STRUCT_STAT * sbuf )
2003-08-28 00:04:23 +04:00
{
2012-03-28 06:18:14 +04:00
struct dirent * result ;
struct dirent * newdirent ;
2007-11-17 04:07:11 +03:00
char * newname ;
size_t newnamelen ;
2003-08-28 00:04:23 +04:00
DEBUG ( 3 , ( " cap: cap_readdir \n " ) ) ;
2007-11-17 04:07:11 +03:00
2020-11-22 15:57:27 +03:00
result = SMB_VFS_NEXT_READDIR ( handle , dirfsp , dirp , NULL ) ;
2007-11-17 04:07:11 +03:00
if ( ! result ) {
return NULL ;
}
newname = capdecode ( talloc_tos ( ) , result - > d_name ) ;
if ( ! newname ) {
return NULL ;
}
DEBUG ( 3 , ( " cap: cap_readdir: %s \n " , newname ) ) ;
newnamelen = strlen ( newname ) + 1 ;
2015-11-08 12:43:59 +03:00
newdirent = talloc_size (
talloc_tos ( ) , sizeof ( struct dirent ) + newnamelen ) ;
2007-11-17 04:07:11 +03:00
if ( ! newdirent ) {
return NULL ;
}
2015-11-08 12:43:59 +03:00
talloc_set_name_const ( newdirent , " struct dirent " ) ;
2012-03-28 06:18:14 +04:00
memcpy ( newdirent , result , sizeof ( struct dirent ) ) ;
2007-11-17 04:07:11 +03:00
memcpy ( & newdirent - > d_name , newname , newnamelen ) ;
return newdirent ;
2003-08-28 00:04:23 +04:00
}
2019-09-04 23:53:43 +03:00
static int cap_mkdirat ( vfs_handle_struct * handle ,
struct files_struct * dirfsp ,
const struct smb_filename * smb_fname ,
mode_t mode )
{
char * cappath = capencode ( talloc_tos ( ) , smb_fname - > base_name ) ;
struct smb_filename * cap_smb_fname = NULL ;
if ( ! cappath ) {
errno = ENOMEM ;
return - 1 ;
}
cap_smb_fname = synthetic_smb_fname ( talloc_tos ( ) ,
cappath ,
NULL ,
NULL ,
2020-04-30 12:48:32 +03:00
smb_fname - > twrp ,
2019-09-04 23:53:43 +03:00
smb_fname - > flags ) ;
if ( cap_smb_fname = = NULL ) {
TALLOC_FREE ( cappath ) ;
errno = ENOMEM ;
return - 1 ;
}
return SMB_VFS_NEXT_MKDIRAT ( handle ,
dirfsp ,
cap_smb_fname ,
mode ) ;
}
2020-05-20 23:58:56 +03:00
static int cap_openat ( vfs_handle_struct * handle ,
const struct files_struct * dirfsp ,
const struct smb_filename * smb_fname_in ,
files_struct * fsp ,
int flags ,
mode_t mode )
{
char * cappath = NULL ;
struct smb_filename * smb_fname = NULL ;
int ret ;
int saved_errno = 0 ;
2020-05-26 21:34:08 +03:00
cappath = capencode ( talloc_tos ( ) , smb_fname_in - > base_name ) ;
2020-05-20 23:58:56 +03:00
if ( cappath = = NULL ) {
errno = ENOMEM ;
return - 1 ;
}
smb_fname = cp_smb_filename ( talloc_tos ( ) , smb_fname_in ) ;
if ( smb_fname = = NULL ) {
TALLOC_FREE ( cappath ) ;
errno = ENOMEM ;
return - 1 ;
}
smb_fname - > base_name = cappath ;
DBG_DEBUG ( " cap_open for %s \n " , smb_fname_str_dbg ( smb_fname ) ) ;
ret = SMB_VFS_NEXT_OPENAT ( handle ,
dirfsp ,
smb_fname ,
fsp ,
flags ,
mode ) ;
if ( ret = = - 1 ) {
saved_errno = errno ;
}
TALLOC_FREE ( cappath ) ;
TALLOC_FREE ( smb_fname ) ;
if ( saved_errno ! = 0 ) {
errno = saved_errno ;
}
return ret ;
}
2019-08-09 02:13:20 +03:00
static int cap_renameat ( vfs_handle_struct * handle ,
files_struct * srcfsp ,
const struct smb_filename * smb_fname_src ,
files_struct * dstfsp ,
const struct smb_filename * smb_fname_dst )
{
char * capold = NULL ;
char * capnew = NULL ;
struct smb_filename * smb_fname_src_tmp = NULL ;
struct smb_filename * smb_fname_dst_tmp = NULL ;
int ret = - 1 ;
capold = capencode ( talloc_tos ( ) , smb_fname_src - > base_name ) ;
capnew = capencode ( talloc_tos ( ) , smb_fname_dst - > base_name ) ;
if ( ! capold | | ! capnew ) {
errno = ENOMEM ;
goto out ;
}
/* Setup temporary smb_filename structs. */
smb_fname_src_tmp = cp_smb_filename ( talloc_tos ( ) , smb_fname_src ) ;
if ( smb_fname_src_tmp = = NULL ) {
errno = ENOMEM ;
goto out ;
}
smb_fname_dst_tmp = cp_smb_filename ( talloc_tos ( ) , smb_fname_dst ) ;
if ( smb_fname_dst_tmp = = NULL ) {
errno = ENOMEM ;
goto out ;
}
smb_fname_src_tmp - > base_name = capold ;
smb_fname_dst_tmp - > base_name = capnew ;
ret = SMB_VFS_NEXT_RENAMEAT ( handle ,
srcfsp ,
smb_fname_src_tmp ,
dstfsp ,
smb_fname_dst_tmp ) ;
out :
TALLOC_FREE ( capold ) ;
TALLOC_FREE ( capnew ) ;
TALLOC_FREE ( smb_fname_src_tmp ) ;
TALLOC_FREE ( smb_fname_dst_tmp ) ;
return ret ;
}
2009-06-23 02:26:56 +04:00
static int cap_stat ( vfs_handle_struct * handle , struct smb_filename * smb_fname )
2003-08-28 00:04:23 +04:00
{
2009-06-23 02:26:56 +04:00
char * cappath ;
char * tmp_base_name = NULL ;
int ret ;
cappath = capencode ( talloc_tos ( ) , smb_fname - > base_name ) ;
2007-11-17 04:07:11 +03:00
if ( ! cappath ) {
errno = ENOMEM ;
return - 1 ;
}
2009-06-23 02:26:56 +04:00
tmp_base_name = smb_fname - > base_name ;
smb_fname - > base_name = cappath ;
ret = SMB_VFS_NEXT_STAT ( handle , smb_fname ) ;
smb_fname - > base_name = tmp_base_name ;
TALLOC_FREE ( cappath ) ;
return ret ;
2003-08-28 00:04:23 +04:00
}
2009-06-23 02:26:56 +04:00
static int cap_lstat ( vfs_handle_struct * handle , struct smb_filename * smb_fname )
2003-08-28 00:04:23 +04:00
{
2009-06-23 02:26:56 +04:00
char * cappath ;
char * tmp_base_name = NULL ;
int ret ;
cappath = capencode ( talloc_tos ( ) , smb_fname - > base_name ) ;
2007-11-17 04:07:11 +03:00
if ( ! cappath ) {
errno = ENOMEM ;
return - 1 ;
}
2009-06-23 02:26:56 +04:00
tmp_base_name = smb_fname - > base_name ;
smb_fname - > base_name = cappath ;
ret = SMB_VFS_NEXT_LSTAT ( handle , smb_fname ) ;
smb_fname - > base_name = tmp_base_name ;
TALLOC_FREE ( cappath ) ;
return ret ;
2003-08-28 00:04:23 +04:00
}
2019-09-12 19:36:34 +03:00
static int cap_unlinkat ( vfs_handle_struct * handle ,
struct files_struct * dirfsp ,
const struct smb_filename * smb_fname ,
int flags )
{
2021-01-20 16:57:39 +03:00
struct smb_filename * full_fname = NULL ;
2019-09-12 19:36:34 +03:00
struct smb_filename * smb_fname_tmp = NULL ;
char * cappath = NULL ;
int ret ;
2021-01-20 16:57:39 +03:00
full_fname = full_path_from_dirfsp_atname ( talloc_tos ( ) ,
dirfsp ,
smb_fname ) ;
if ( full_fname = = NULL ) {
return - 1 ;
}
cappath = capencode ( talloc_tos ( ) , full_fname - > base_name ) ;
2019-09-12 19:36:34 +03:00
if ( ! cappath ) {
2021-01-20 16:57:39 +03:00
TALLOC_FREE ( full_fname ) ;
2019-09-12 19:36:34 +03:00
errno = ENOMEM ;
return - 1 ;
}
/* Setup temporary smb_filename structs. */
2021-01-20 16:57:39 +03:00
smb_fname_tmp = cp_smb_filename ( talloc_tos ( ) , full_fname ) ;
TALLOC_FREE ( full_fname ) ;
2019-09-12 19:36:34 +03:00
if ( smb_fname_tmp = = NULL ) {
errno = ENOMEM ;
return - 1 ;
}
smb_fname_tmp - > base_name = cappath ;
ret = SMB_VFS_NEXT_UNLINKAT ( handle ,
2021-01-20 16:57:39 +03:00
dirfsp - > conn - > cwd_fsp ,
2019-09-12 19:36:34 +03:00
smb_fname_tmp ,
flags ) ;
TALLOC_FREE ( smb_fname_tmp ) ;
return ret ;
}
2016-03-04 01:34:57 +03:00
static int cap_lchown ( vfs_handle_struct * handle ,
const struct smb_filename * smb_fname ,
uid_t uid ,
gid_t gid )
2007-05-24 03:55:12 +04:00
{
2016-03-04 01:34:57 +03:00
struct smb_filename * cap_smb_fname = NULL ;
char * cappath = capencode ( talloc_tos ( ) , smb_fname - > base_name ) ;
int ret ;
int saved_errno ;
2007-11-17 04:07:11 +03:00
if ( ! cappath ) {
errno = ENOMEM ;
return - 1 ;
}
2016-03-04 01:34:57 +03:00
cap_smb_fname = synthetic_smb_fname ( talloc_tos ( ) ,
cappath ,
NULL ,
2016-03-19 07:19:38 +03:00
NULL ,
2020-04-30 12:48:32 +03:00
smb_fname - > twrp ,
2016-03-19 07:19:38 +03:00
smb_fname - > flags ) ;
2016-03-04 01:34:57 +03:00
if ( cap_smb_fname = = NULL ) {
TALLOC_FREE ( cappath ) ;
errno = ENOMEM ;
return - 1 ;
}
ret = SMB_VFS_NEXT_LCHOWN ( handle , cap_smb_fname , uid , gid ) ;
saved_errno = errno ;
TALLOC_FREE ( cappath ) ;
TALLOC_FREE ( cap_smb_fname ) ;
errno = saved_errno ;
return ret ;
2007-05-24 03:55:12 +04:00
}
2017-06-29 21:29:33 +03:00
static int cap_chdir ( vfs_handle_struct * handle ,
const struct smb_filename * smb_fname )
2003-08-28 00:04:23 +04:00
{
2017-06-29 21:29:33 +03:00
struct smb_filename * cap_smb_fname = NULL ;
char * cappath = capencode ( talloc_tos ( ) , smb_fname - > base_name ) ;
int ret ;
int saved_errno = 0 ;
2007-11-17 04:07:11 +03:00
if ( ! cappath ) {
errno = ENOMEM ;
return - 1 ;
}
2017-06-29 21:29:33 +03:00
DEBUG ( 3 , ( " cap: cap_chdir for %s \n " , smb_fname - > base_name ) ) ;
cap_smb_fname = synthetic_smb_fname ( talloc_tos ( ) ,
cappath ,
NULL ,
NULL ,
2020-04-30 12:48:32 +03:00
smb_fname - > twrp ,
2017-06-29 21:29:33 +03:00
smb_fname - > flags ) ;
if ( cap_smb_fname = = NULL ) {
TALLOC_FREE ( cappath ) ;
errno = ENOMEM ;
return - 1 ;
}
ret = SMB_VFS_NEXT_CHDIR ( handle , cap_smb_fname ) ;
if ( ret = = - 1 ) {
saved_errno = errno ;
}
TALLOC_FREE ( cappath ) ;
TALLOC_FREE ( cap_smb_fname ) ;
if ( saved_errno ! = 0 ) {
errno = saved_errno ;
}
return ret ;
2003-08-28 00:04:23 +04:00
}
2019-08-30 22:54:23 +03:00
static int cap_symlinkat ( vfs_handle_struct * handle ,
2020-04-30 20:30:50 +03:00
const struct smb_filename * link_contents ,
2019-08-30 22:54:23 +03:00
struct files_struct * dirfsp ,
const struct smb_filename * new_smb_fname )
{
2021-01-25 22:17:57 +03:00
struct smb_filename * full_fname = NULL ;
2020-04-30 20:30:50 +03:00
char * capold = capencode ( talloc_tos ( ) , link_contents - > base_name ) ;
2021-01-25 22:17:57 +03:00
char * capnew = NULL ;
2020-04-30 20:30:50 +03:00
struct smb_filename * new_link_target = NULL ;
2019-08-30 22:54:23 +03:00
struct smb_filename * new_cap_smb_fname = NULL ;
int saved_errno = 0 ;
int ret ;
if ( ! capold | | ! capnew ) {
errno = ENOMEM ;
return - 1 ;
}
2020-04-30 20:30:50 +03:00
2021-01-25 22:17:57 +03:00
full_fname = full_path_from_dirfsp_atname ( talloc_tos ( ) ,
dirfsp ,
new_smb_fname ) ;
if ( full_fname = = NULL ) {
return - 1 ;
}
capnew = capencode ( talloc_tos ( ) , full_fname - > base_name ) ;
if ( ! capnew ) {
TALLOC_FREE ( full_fname ) ;
errno = ENOMEM ;
return - 1 ;
}
2020-04-30 20:30:50 +03:00
new_link_target = synthetic_smb_fname ( talloc_tos ( ) ,
capold ,
NULL ,
NULL ,
new_smb_fname - > twrp ,
new_smb_fname - > flags ) ;
if ( new_link_target = = NULL ) {
2021-01-25 22:17:57 +03:00
TALLOC_FREE ( full_fname ) ;
2020-04-30 20:30:50 +03:00
TALLOC_FREE ( capold ) ;
TALLOC_FREE ( capnew ) ;
errno = ENOMEM ;
return - 1 ;
}
2019-08-30 22:54:23 +03:00
new_cap_smb_fname = synthetic_smb_fname ( talloc_tos ( ) ,
capnew ,
NULL ,
NULL ,
2020-04-30 12:48:32 +03:00
new_smb_fname - > twrp ,
2019-08-30 22:54:23 +03:00
new_smb_fname - > flags ) ;
if ( new_cap_smb_fname = = NULL ) {
2021-01-25 22:17:57 +03:00
TALLOC_FREE ( full_fname ) ;
2019-08-30 22:54:23 +03:00
TALLOC_FREE ( capold ) ;
TALLOC_FREE ( capnew ) ;
2020-04-30 20:30:50 +03:00
TALLOC_FREE ( new_link_target ) ;
2019-08-30 22:54:23 +03:00
errno = ENOMEM ;
return - 1 ;
}
ret = SMB_VFS_NEXT_SYMLINKAT ( handle ,
2020-04-30 20:30:50 +03:00
new_link_target ,
2021-01-25 22:17:57 +03:00
handle - > conn - > cwd_fsp ,
2019-08-30 22:54:23 +03:00
new_cap_smb_fname ) ;
if ( ret = = - 1 ) {
saved_errno = errno ;
}
2021-01-25 22:17:57 +03:00
TALLOC_FREE ( full_fname ) ;
2019-08-30 22:54:23 +03:00
TALLOC_FREE ( capold ) ;
TALLOC_FREE ( capnew ) ;
2020-04-30 20:30:50 +03:00
TALLOC_FREE ( new_link_target ) ;
2019-08-30 22:54:23 +03:00
TALLOC_FREE ( new_cap_smb_fname ) ;
if ( saved_errno ! = 0 ) {
errno = saved_errno ;
}
return ret ;
}
2019-08-22 23:49:28 +03:00
static int cap_readlinkat ( vfs_handle_struct * handle ,
2020-10-13 16:19:30 +03:00
const struct files_struct * dirfsp ,
2019-08-22 23:49:28 +03:00
const struct smb_filename * smb_fname ,
char * buf ,
size_t bufsiz )
{
2021-02-11 20:21:41 +03:00
struct smb_filename * full_fname = NULL ;
2019-08-22 23:49:28 +03:00
struct smb_filename * cap_smb_fname = NULL ;
2021-02-11 20:21:41 +03:00
char * cappath = NULL ;
2019-08-22 23:49:28 +03:00
int saved_errno = 0 ;
int ret ;
2021-02-11 20:21:41 +03:00
full_fname = full_path_from_dirfsp_atname ( talloc_tos ( ) ,
dirfsp ,
smb_fname ) ;
if ( full_fname = = NULL ) {
return - 1 ;
}
cappath = capencode ( talloc_tos ( ) , full_fname - > base_name ) ;
if ( cappath = = NULL ) {
TALLOC_FREE ( full_fname ) ;
2019-08-22 23:49:28 +03:00
errno = ENOMEM ;
return - 1 ;
}
cap_smb_fname = synthetic_smb_fname ( talloc_tos ( ) ,
cappath ,
NULL ,
NULL ,
2020-04-30 12:48:32 +03:00
smb_fname - > twrp ,
2019-08-22 23:49:28 +03:00
smb_fname - > flags ) ;
if ( cap_smb_fname = = NULL ) {
2021-02-11 20:21:41 +03:00
TALLOC_FREE ( full_fname ) ;
2019-08-22 23:49:28 +03:00
TALLOC_FREE ( cappath ) ;
errno = ENOMEM ;
return - 1 ;
}
ret = SMB_VFS_NEXT_READLINKAT ( handle ,
2021-02-11 20:21:41 +03:00
handle - > conn - > cwd_fsp ,
2019-08-22 23:49:28 +03:00
cap_smb_fname ,
buf ,
bufsiz ) ;
if ( ret = = - 1 ) {
saved_errno = errno ;
}
2021-02-11 20:21:41 +03:00
TALLOC_FREE ( full_fname ) ;
2019-08-22 23:49:28 +03:00
TALLOC_FREE ( cappath ) ;
TALLOC_FREE ( cap_smb_fname ) ;
if ( saved_errno ! = 0 ) {
errno = saved_errno ;
}
return ret ;
}
2019-08-14 00:59:05 +03:00
static int cap_linkat ( vfs_handle_struct * handle ,
files_struct * srcfsp ,
const struct smb_filename * old_smb_fname ,
files_struct * dstfsp ,
const struct smb_filename * new_smb_fname ,
int flags )
{
2021-02-02 00:04:34 +03:00
struct smb_filename * old_full_fname = NULL ;
struct smb_filename * new_full_fname = NULL ;
char * capold = NULL ;
char * capnew = NULL ;
2019-08-14 00:59:05 +03:00
struct smb_filename * old_cap_smb_fname = NULL ;
struct smb_filename * new_cap_smb_fname = NULL ;
int saved_errno = 0 ;
int ret ;
2021-02-02 00:04:34 +03:00
/* Process 'old' name. */
old_full_fname = full_path_from_dirfsp_atname ( talloc_tos ( ) ,
srcfsp ,
old_smb_fname ) ;
if ( old_full_fname = = NULL ) {
goto nomem_out ;
}
capold = capencode ( talloc_tos ( ) , old_full_fname - > base_name ) ;
if ( capold = = NULL ) {
goto nomem_out ;
}
TALLOC_FREE ( old_full_fname ) ;
2019-08-14 00:59:05 +03:00
old_cap_smb_fname = synthetic_smb_fname ( talloc_tos ( ) ,
capold ,
NULL ,
NULL ,
2020-04-30 12:48:32 +03:00
old_smb_fname - > twrp ,
2019-08-14 00:59:05 +03:00
old_smb_fname - > flags ) ;
if ( old_cap_smb_fname = = NULL ) {
2021-02-02 00:04:34 +03:00
goto nomem_out ;
2019-08-14 00:59:05 +03:00
}
2021-02-02 00:04:34 +03:00
/* Process 'new' name. */
new_full_fname = full_path_from_dirfsp_atname ( talloc_tos ( ) ,
dstfsp ,
new_smb_fname ) ;
if ( new_full_fname = = NULL ) {
goto nomem_out ;
}
capnew = capencode ( talloc_tos ( ) , new_full_fname - > base_name ) ;
if ( capnew = = NULL ) {
goto nomem_out ;
}
TALLOC_FREE ( new_full_fname ) ;
2019-08-14 00:59:05 +03:00
new_cap_smb_fname = synthetic_smb_fname ( talloc_tos ( ) ,
capnew ,
NULL ,
NULL ,
2020-04-30 12:48:32 +03:00
new_smb_fname - > twrp ,
2019-08-14 00:59:05 +03:00
new_smb_fname - > flags ) ;
if ( new_cap_smb_fname = = NULL ) {
2021-02-02 00:04:34 +03:00
goto nomem_out ;
2019-08-14 00:59:05 +03:00
}
2021-02-02 00:04:34 +03:00
2019-08-14 00:59:05 +03:00
ret = SMB_VFS_NEXT_LINKAT ( handle ,
2021-02-02 00:04:34 +03:00
handle - > conn - > cwd_fsp ,
2019-08-14 00:59:05 +03:00
old_cap_smb_fname ,
2021-02-02 00:04:34 +03:00
handle - > conn - > cwd_fsp ,
2019-08-14 00:59:05 +03:00
new_cap_smb_fname ,
flags ) ;
if ( ret = = - 1 ) {
saved_errno = errno ;
}
2021-02-02 00:04:34 +03:00
TALLOC_FREE ( old_full_fname ) ;
TALLOC_FREE ( old_full_fname ) ;
2019-08-14 00:59:05 +03:00
TALLOC_FREE ( capold ) ;
TALLOC_FREE ( capnew ) ;
TALLOC_FREE ( old_cap_smb_fname ) ;
TALLOC_FREE ( new_cap_smb_fname ) ;
if ( saved_errno ! = 0 ) {
errno = saved_errno ;
}
return ret ;
2021-02-02 00:04:34 +03:00
nomem_out :
TALLOC_FREE ( old_full_fname ) ;
TALLOC_FREE ( old_full_fname ) ;
TALLOC_FREE ( capold ) ;
TALLOC_FREE ( capnew ) ;
TALLOC_FREE ( old_cap_smb_fname ) ;
TALLOC_FREE ( new_cap_smb_fname ) ;
errno = ENOMEM ;
return - 1 ;
2019-08-14 00:59:05 +03:00
}
2019-08-21 02:33:57 +03:00
static int cap_mknodat ( vfs_handle_struct * handle ,
files_struct * dirfsp ,
const struct smb_filename * smb_fname ,
mode_t mode ,
SMB_DEV_T dev )
{
2021-01-20 22:33:03 +03:00
struct smb_filename * full_fname = NULL ;
2019-08-21 02:33:57 +03:00
struct smb_filename * cap_smb_fname = NULL ;
2021-01-20 22:33:03 +03:00
char * cappath = NULL ;
2019-08-21 02:33:57 +03:00
int ret ;
int saved_errno = 0 ;
2021-01-20 22:33:03 +03:00
full_fname = full_path_from_dirfsp_atname ( talloc_tos ( ) ,
dirfsp ,
smb_fname ) ;
if ( full_fname = = NULL ) {
return - 1 ;
}
cappath = capencode ( talloc_tos ( ) , full_fname - > base_name ) ;
2019-08-21 02:33:57 +03:00
if ( ! cappath ) {
2021-01-20 22:33:03 +03:00
TALLOC_FREE ( full_fname ) ;
2019-08-21 02:33:57 +03:00
errno = ENOMEM ;
return - 1 ;
}
cap_smb_fname = synthetic_smb_fname ( talloc_tos ( ) ,
cappath ,
NULL ,
NULL ,
2020-04-30 12:48:32 +03:00
smb_fname - > twrp ,
2019-08-21 02:33:57 +03:00
smb_fname - > flags ) ;
if ( cap_smb_fname = = NULL ) {
2021-01-20 22:33:03 +03:00
TALLOC_FREE ( full_fname ) ;
2019-08-21 02:33:57 +03:00
TALLOC_FREE ( cappath ) ;
errno = ENOMEM ;
return - 1 ;
}
ret = SMB_VFS_NEXT_MKNODAT ( handle ,
2021-01-20 22:33:03 +03:00
handle - > conn - > cwd_fsp ,
2019-08-21 02:33:57 +03:00
cap_smb_fname ,
mode ,
dev ) ;
if ( ret = = - 1 ) {
saved_errno = errno ;
}
2021-01-20 22:33:03 +03:00
TALLOC_FREE ( full_fname ) ;
2019-08-21 02:33:57 +03:00
TALLOC_FREE ( cappath ) ;
TALLOC_FREE ( cap_smb_fname ) ;
if ( saved_errno ! = 0 ) {
errno = saved_errno ;
}
return ret ;
}
2017-06-30 21:32:59 +03:00
static struct smb_filename * cap_realpath ( vfs_handle_struct * handle ,
TALLOC_CTX * ctx ,
const struct smb_filename * smb_fname )
2003-08-28 00:04:23 +04:00
{
/* monyo need capencode'ed and capdecode'ed? */
2017-06-30 21:32:59 +03:00
struct smb_filename * cap_smb_fname = NULL ;
struct smb_filename * return_fname = NULL ;
char * cappath = capencode ( talloc_tos ( ) , smb_fname - > base_name ) ;
int saved_errno = 0 ;
2007-11-17 04:07:11 +03:00
if ( ! cappath ) {
errno = ENOMEM ;
return NULL ;
}
2017-06-30 21:32:59 +03:00
cap_smb_fname = synthetic_smb_fname ( ctx ,
cappath ,
NULL ,
NULL ,
2020-04-30 12:48:32 +03:00
smb_fname - > twrp ,
2017-06-30 21:32:59 +03:00
smb_fname - > flags ) ;
if ( cap_smb_fname = = NULL ) {
TALLOC_FREE ( cappath ) ;
errno = ENOMEM ;
return NULL ;
}
return_fname = SMB_VFS_NEXT_REALPATH ( handle , ctx , cap_smb_fname ) ;
if ( return_fname = = NULL ) {
saved_errno = errno ;
}
TALLOC_FREE ( cappath ) ;
TALLOC_FREE ( cap_smb_fname ) ;
if ( saved_errno ! = 0 ) {
errno = saved_errno ;
}
return return_fname ;
2003-08-28 00:04:23 +04:00
}
2012-10-10 03:18:32 +04:00
static SMB_ACL_T cap_sys_acl_get_file ( vfs_handle_struct * handle ,
2017-05-24 03:11:18 +03:00
const struct smb_filename * smb_fname ,
SMB_ACL_TYPE_T type ,
TALLOC_CTX * mem_ctx )
2003-08-28 00:04:23 +04:00
{
2017-05-24 03:11:18 +03:00
struct smb_filename * cap_smb_fname = NULL ;
char * cappath = capencode ( talloc_tos ( ) , smb_fname - > base_name ) ;
SMB_ACL_T ret ;
int saved_errno = 0 ;
2007-11-17 04:07:11 +03:00
if ( ! cappath ) {
errno = ENOMEM ;
return ( SMB_ACL_T ) NULL ;
}
2017-05-24 03:11:18 +03:00
cap_smb_fname = synthetic_smb_fname ( talloc_tos ( ) ,
cappath ,
NULL ,
NULL ,
2020-04-30 12:48:32 +03:00
smb_fname - > twrp ,
2017-05-24 03:11:18 +03:00
smb_fname - > flags ) ;
if ( cap_smb_fname = = NULL ) {
TALLOC_FREE ( cappath ) ;
errno = ENOMEM ;
return ( SMB_ACL_T ) NULL ;
}
ret = SMB_VFS_NEXT_SYS_ACL_GET_FILE ( handle , cap_smb_fname ,
type , mem_ctx ) ;
if ( ret = = NULL ) {
saved_errno = errno ;
}
TALLOC_FREE ( cappath ) ;
TALLOC_FREE ( cap_smb_fname ) ;
if ( saved_errno ! = 0 ) {
errno = saved_errno ;
}
return ret ;
2003-08-28 00:04:23 +04:00
}
2017-05-24 01:33:31 +03:00
static int cap_sys_acl_delete_def_file ( vfs_handle_struct * handle ,
const struct smb_filename * smb_fname )
2003-08-28 00:04:23 +04:00
{
2017-05-24 01:33:31 +03:00
struct smb_filename * cap_smb_fname = NULL ;
char * cappath = capencode ( talloc_tos ( ) , smb_fname - > base_name ) ;
int ret ;
int saved_errno = 0 ;
2007-11-17 04:07:11 +03:00
if ( ! cappath ) {
errno = ENOMEM ;
return - 1 ;
}
2017-05-24 01:33:31 +03:00
cap_smb_fname = synthetic_smb_fname ( talloc_tos ( ) ,
cappath ,
NULL ,
NULL ,
2020-04-30 12:48:32 +03:00
smb_fname - > twrp ,
2017-05-24 01:33:31 +03:00
smb_fname - > flags ) ;
if ( cap_smb_fname = = NULL ) {
TALLOC_FREE ( cappath ) ;
errno = ENOMEM ;
return - 1 ;
}
ret = SMB_VFS_NEXT_SYS_ACL_DELETE_DEF_FILE ( handle , cap_smb_fname ) ;
if ( ret = = - 1 ) {
saved_errno = errno ;
}
TALLOC_FREE ( cappath ) ;
TALLOC_FREE ( cap_smb_fname ) ;
if ( saved_errno ) {
errno = saved_errno ;
}
return ret ;
2003-08-28 00:04:23 +04:00
}
2017-05-26 02:42:04 +03:00
static ssize_t cap_getxattr ( vfs_handle_struct * handle ,
const struct smb_filename * smb_fname ,
const char * name ,
void * value ,
size_t size )
2003-08-28 00:04:23 +04:00
{
2017-05-26 02:42:04 +03:00
struct smb_filename * cap_smb_fname = NULL ;
char * cappath = capencode ( talloc_tos ( ) , smb_fname - > base_name ) ;
2007-11-17 04:07:11 +03:00
char * capname = capencode ( talloc_tos ( ) , name ) ;
2017-05-26 02:42:04 +03:00
ssize_t ret ;
int saved_errno = 0 ;
2007-11-17 04:07:11 +03:00
if ( ! cappath | | ! capname ) {
errno = ENOMEM ;
return - 1 ;
}
2017-05-26 02:42:04 +03:00
cap_smb_fname = synthetic_smb_fname ( talloc_tos ( ) ,
cappath ,
NULL ,
NULL ,
2020-04-30 12:48:32 +03:00
smb_fname - > twrp ,
2017-05-26 02:42:04 +03:00
smb_fname - > flags ) ;
if ( cap_smb_fname = = NULL ) {
TALLOC_FREE ( cappath ) ;
TALLOC_FREE ( capname ) ;
errno = ENOMEM ;
return - 1 ;
}
ret = SMB_VFS_NEXT_GETXATTR ( handle , cap_smb_fname ,
capname , value , size ) ;
if ( ret = = - 1 ) {
saved_errno = errno ;
}
TALLOC_FREE ( cappath ) ;
TALLOC_FREE ( capname ) ;
TALLOC_FREE ( cap_smb_fname ) ;
if ( saved_errno ) {
errno = saved_errno ;
}
return ret ;
2003-08-28 00:04:23 +04:00
}
2008-01-08 12:00:47 +03:00
static ssize_t cap_fgetxattr ( vfs_handle_struct * handle , struct files_struct * fsp , const char * path , void * value , size_t size )
2003-08-28 00:04:23 +04:00
{
2007-11-17 04:07:11 +03:00
char * cappath = capencode ( talloc_tos ( ) , path ) ;
if ( ! cappath ) {
errno = ENOMEM ;
return - 1 ;
}
2008-01-08 12:00:47 +03:00
return SMB_VFS_NEXT_FGETXATTR ( handle , fsp , cappath , value , size ) ;
2003-08-28 00:04:23 +04:00
}
2008-01-08 13:29:09 +03:00
static int cap_fremovexattr ( vfs_handle_struct * handle , struct files_struct * fsp , const char * path )
2003-08-28 00:04:23 +04:00
{
2007-11-17 04:07:11 +03:00
char * cappath = capencode ( talloc_tos ( ) , path ) ;
if ( ! cappath ) {
errno = ENOMEM ;
return - 1 ;
}
2008-01-08 13:29:09 +03:00
return SMB_VFS_NEXT_FREMOVEXATTR ( handle , fsp , cappath ) ;
2003-08-28 00:04:23 +04:00
}
2008-01-08 13:47:33 +03:00
static int cap_fsetxattr ( vfs_handle_struct * handle , struct files_struct * fsp , const char * path , const void * value , size_t size , int flags )
2003-08-28 00:04:23 +04:00
{
2007-11-17 04:07:11 +03:00
char * cappath = capencode ( talloc_tos ( ) , path ) ;
if ( ! cappath ) {
errno = ENOMEM ;
return - 1 ;
}
2008-01-08 13:47:33 +03:00
return SMB_VFS_NEXT_FSETXATTR ( handle , fsp , cappath , value , size , flags ) ;
2003-08-28 00:04:23 +04:00
}
2020-01-29 01:59:46 +03:00
static NTSTATUS cap_create_dfs_pathat ( vfs_handle_struct * handle ,
files_struct * dirfsp ,
const struct smb_filename * smb_fname ,
const struct referral * reflist ,
size_t referral_count )
{
char * cappath = capencode ( talloc_tos ( ) , smb_fname - > base_name ) ;
struct smb_filename * cap_smb_fname = NULL ;
NTSTATUS status ;
if ( cappath = = NULL ) {
return NT_STATUS_NO_MEMORY ;
}
cap_smb_fname = synthetic_smb_fname ( talloc_tos ( ) ,
cappath ,
NULL ,
NULL ,
2020-04-30 12:48:32 +03:00
smb_fname - > twrp ,
2020-01-29 01:59:46 +03:00
smb_fname - > flags ) ;
if ( cap_smb_fname = = NULL ) {
TALLOC_FREE ( cappath ) ;
return NT_STATUS_NO_MEMORY ;
}
status = SMB_VFS_NEXT_CREATE_DFS_PATHAT ( handle ,
dirfsp ,
cap_smb_fname ,
reflist ,
referral_count ) ;
TALLOC_FREE ( cappath ) ;
TALLOC_FREE ( cap_smb_fname ) ;
return status ;
}
2020-02-11 20:48:05 +03:00
static NTSTATUS cap_read_dfs_pathat ( struct vfs_handle_struct * handle ,
TALLOC_CTX * mem_ctx ,
struct files_struct * dirfsp ,
2020-05-30 02:32:12 +03:00
struct smb_filename * smb_fname ,
2020-02-11 20:48:05 +03:00
struct referral * * ppreflist ,
size_t * preferral_count )
{
char * cappath = capencode ( talloc_tos ( ) , smb_fname - > base_name ) ;
struct smb_filename * cap_smb_fname = NULL ;
NTSTATUS status ;
if ( cappath = = NULL ) {
return NT_STATUS_NO_MEMORY ;
}
cap_smb_fname = synthetic_smb_fname ( talloc_tos ( ) ,
cappath ,
NULL ,
NULL ,
2020-04-30 12:48:32 +03:00
smb_fname - > twrp ,
2020-02-11 20:48:05 +03:00
smb_fname - > flags ) ;
if ( cap_smb_fname = = NULL ) {
TALLOC_FREE ( cappath ) ;
return NT_STATUS_NO_MEMORY ;
}
status = SMB_VFS_NEXT_READ_DFS_PATHAT ( handle ,
mem_ctx ,
dirfsp ,
cap_smb_fname ,
ppreflist ,
preferral_count ) ;
2020-05-30 02:36:55 +03:00
if ( NT_STATUS_IS_OK ( status ) ) {
/* Return any stat(2) info. */
smb_fname - > st = cap_smb_fname - > st ;
}
2020-02-11 20:48:05 +03:00
TALLOC_FREE ( cappath ) ;
TALLOC_FREE ( cap_smb_fname ) ;
return status ;
}
2009-07-24 04:28:58 +04:00
static struct vfs_fn_pointers vfs_cap_fns = {
2011-12-04 08:45:04 +04:00
. disk_free_fn = cap_disk_free ,
2016-01-10 15:10:10 +03:00
. get_quota_fn = cap_get_quota ,
2011-12-04 08:45:04 +04:00
. readdir_fn = cap_readdir ,
2019-09-04 23:53:43 +03:00
. mkdirat_fn = cap_mkdirat ,
2020-05-20 23:58:56 +03:00
. openat_fn = cap_openat ,
2019-08-09 02:13:20 +03:00
. renameat_fn = cap_renameat ,
2011-12-04 08:45:04 +04:00
. stat_fn = cap_stat ,
. lstat_fn = cap_lstat ,
2019-09-12 19:36:34 +03:00
. unlinkat_fn = cap_unlinkat ,
2011-12-04 08:45:04 +04:00
. lchown_fn = cap_lchown ,
. chdir_fn = cap_chdir ,
2019-08-30 22:54:23 +03:00
. symlinkat_fn = cap_symlinkat ,
2019-08-22 23:49:28 +03:00
. readlinkat_fn = cap_readlinkat ,
2019-08-14 00:59:05 +03:00
. linkat_fn = cap_linkat ,
2019-08-21 02:33:57 +03:00
. mknodat_fn = cap_mknodat ,
2011-12-04 08:45:04 +04:00
. realpath_fn = cap_realpath ,
. sys_acl_get_file_fn = cap_sys_acl_get_file ,
. sys_acl_delete_def_file_fn = cap_sys_acl_delete_def_file ,
. getxattr_fn = cap_getxattr ,
2018-03-13 10:14:53 +03:00
. getxattrat_send_fn = vfs_not_implemented_getxattrat_send ,
. getxattrat_recv_fn = vfs_not_implemented_getxattrat_recv ,
2011-12-04 08:45:04 +04:00
. fgetxattr_fn = cap_fgetxattr ,
. fremovexattr_fn = cap_fremovexattr ,
2020-01-29 01:59:46 +03:00
. fsetxattr_fn = cap_fsetxattr ,
2020-02-11 20:48:05 +03:00
. create_dfs_pathat_fn = cap_create_dfs_pathat ,
. read_dfs_pathat_fn = cap_read_dfs_pathat
2003-08-28 00:04:23 +04:00
} ;
2017-12-16 01:32:12 +03:00
static_decl_vfs ;
2017-04-20 22:24:43 +03:00
NTSTATUS vfs_cap_init ( TALLOC_CTX * ctx )
2003-08-28 00:04:23 +04:00
{
2009-07-24 04:28:58 +04:00
return smb_register_vfs ( SMB_VFS_INTERFACE_VERSION , " cap " ,
& vfs_cap_fns ) ;
2003-08-28 00:04:23 +04:00
}
/* For CAP functions */
# define hex_tag ':'
# define hex2bin(c) hex2bin_table[(unsigned char)(c)]
# define bin2hex(c) bin2hex_table[(unsigned char)(c)]
# define is_hex(s) ((s)[0] == hex_tag)
static unsigned char hex2bin_table [ 256 ] = {
0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , /* 0x00 */
0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , /* 0x10 */
0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , /* 0x20 */
0 , 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 , 0 , 0 , 0 , 0 , 0 , 0 , /* 0x30 */
0000 , 0x0a , 0x0b , 0x0c , 0x0d , 0x0e , 0x0f , 0000 , /* 0x40 */
0000 , 0000 , 0000 , 0000 , 0000 , 0000 , 0000 , 0000 ,
0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , /* 0x50 */
0000 , 0x0a , 0x0b , 0x0c , 0x0d , 0x0e , 0x0f , 0000 , /* 0x60 */
0000 , 0000 , 0000 , 0000 , 0000 , 0000 , 0000 , 0000 ,
0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , /* 0x70 */
0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , /* 0x80 */
0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , /* 0x90 */
0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , /* 0xa0 */
0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , /* 0xb0 */
0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , /* 0xc0 */
0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , /* 0xd0 */
0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , /* 0xe0 */
0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 /* 0xf0 */
} ;
static unsigned char bin2hex_table [ 256 ] = " 0123456789abcdef " ;
/*******************************************************************
original code - > " :xx " - CAP format
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2007-11-17 04:07:11 +03:00
static char * capencode ( TALLOC_CTX * ctx , const char * from )
{
char * out = NULL ;
const char * p1 ;
char * to = NULL ;
size_t len = 0 ;
for ( p1 = from ; * p1 ; p1 + + ) {
if ( ( unsigned char ) * p1 > = 0x80 ) {
len + = 3 ;
} else {
len + + ;
}
}
len + + ;
2011-06-07 05:30:12 +04:00
to = talloc_array ( ctx , char , len ) ;
2007-11-17 04:07:11 +03:00
if ( ! to ) {
return NULL ;
}
for ( out = to ; * from ; ) {
/* buffer husoku error */
if ( ( unsigned char ) * from > = 0x80 ) {
* out + + = hex_tag ;
* out + + = bin2hex ( ( ( * from ) > > 4 ) & 0x0f ) ;
* out + + = bin2hex ( ( * from ) & 0x0f ) ;
from + + ;
} else {
* out + + = * from + + ;
}
}
* out = ' \0 ' ;
return to ;
2003-08-28 00:04:23 +04:00
}
/*******************************************************************
CAP - > original code
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
/* ":xx" -> a byte */
2007-11-17 04:07:11 +03:00
static char * capdecode ( TALLOC_CTX * ctx , const char * from )
{
const char * p1 ;
char * out = NULL ;
char * to = NULL ;
size_t len = 0 ;
for ( p1 = from ; * p1 ; len + + ) {
2010-01-13 08:43:23 +03:00
if ( is_hex ( p1 ) ) {
2007-11-17 04:07:11 +03:00
p1 + = 3 ;
} else {
p1 + + ;
}
}
2010-01-13 08:43:23 +03:00
len + + ;
2007-11-17 04:07:11 +03:00
2011-06-07 05:30:12 +04:00
to = talloc_array ( ctx , char , len ) ;
2007-11-17 04:07:11 +03:00
if ( ! to ) {
return NULL ;
}
for ( out = to ; * from ; ) {
if ( is_hex ( from ) ) {
* out + + = ( hex2bin ( from [ 1 ] ) < < 4 ) | ( hex2bin ( from [ 2 ] ) ) ;
from + = 3 ;
} else {
* out + + = * from + + ;
}
}
* out = ' \0 ' ;
return to ;
2003-08-28 00:04:23 +04:00
}