2008-01-20 01:33:11 +03:00
/*
* Store streams in xattrs
*
* Copyright ( C ) Volker Lendecke , 2008
*
* Partly based on James Peach ' s Darwin module , which is
*
* Copyright ( C ) James Peach 2006 - 2007
*
* 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
* the Free Software Foundation ; either version 3 of the License , or
* ( at your option ) any later version .
*
* 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 .
*
* You should have received a copy of the GNU General Public License
* along with this program ; if not , see < http : //www.gnu.org/licenses/>.
*/
# include "includes.h"
2011-03-23 00:34:22 +03:00
# include "smbd/smbd.h"
2011-02-26 01:20:06 +03:00
# include "system/filesys.h"
2010-05-18 02:16:40 +04:00
# include "../lib/crypto/md5.h"
2008-01-20 01:33:11 +03:00
# undef DBGC_CLASS
# define DBGC_CLASS DBGC_VFS
struct stream_io {
char * base ;
char * xattr_name ;
2008-12-02 01:25:20 +03:00
void * fsp_name_ptr ;
files_struct * fsp ;
vfs_handle_struct * handle ;
2008-01-20 01:33:11 +03:00
} ;
static SMB_INO_T stream_inode ( const SMB_STRUCT_STAT * sbuf , const char * sname )
{
struct MD5Context ctx ;
unsigned char hash [ 16 ] ;
SMB_INO_T result ;
char * upper_sname ;
DEBUG ( 10 , ( " stream_inode called for %lu/%lu [%s] \n " ,
2009-05-14 17:34:42 +04:00
( unsigned long ) sbuf - > st_ex_dev ,
( unsigned long ) sbuf - > st_ex_ino , sname ) ) ;
2008-01-20 01:33:11 +03:00
upper_sname = talloc_strdup_upper ( talloc_tos ( ) , sname ) ;
SMB_ASSERT ( upper_sname ! = NULL ) ;
MD5Init ( & ctx ) ;
2011-05-06 03:19:49 +04:00
MD5Update ( & ctx , ( const unsigned char * ) & ( sbuf - > st_ex_dev ) ,
2009-05-14 17:34:42 +04:00
sizeof ( sbuf - > st_ex_dev ) ) ;
2011-05-06 03:19:49 +04:00
MD5Update ( & ctx , ( const unsigned char * ) & ( sbuf - > st_ex_ino ) ,
2009-05-14 17:34:42 +04:00
sizeof ( sbuf - > st_ex_ino ) ) ;
2008-01-20 01:33:11 +03:00
MD5Update ( & ctx , ( unsigned char * ) upper_sname ,
talloc_get_size ( upper_sname ) - 1 ) ;
MD5Final ( hash , & ctx ) ;
TALLOC_FREE ( upper_sname ) ;
/* Hopefully all the variation is in the lower 4 (or 8) bytes! */
memcpy ( & result , hash , sizeof ( result ) ) ;
DEBUG ( 10 , ( " stream_inode returns %lu \n " , ( unsigned long ) result ) ) ;
return result ;
}
2008-11-22 03:02:31 +03:00
static ssize_t get_xattr_size ( connection_struct * conn ,
files_struct * fsp ,
const char * fname ,
const char * xattr_name )
2008-01-20 01:33:11 +03:00
{
NTSTATUS status ;
struct ea_struct ea ;
ssize_t result ;
2008-11-22 03:02:31 +03:00
status = get_ea_value ( talloc_tos ( ) , conn , fsp , fname ,
2008-01-20 01:33:11 +03:00
xattr_name , & ea ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
return - 1 ;
}
result = ea . value . length - 1 ;
TALLOC_FREE ( ea . value . data ) ;
return result ;
}
2009-06-16 23:01:13 +04:00
/**
* Given a stream name , populate xattr_name with the xattr name to use for
* accessing the stream .
*/
static NTSTATUS streams_xattr_get_name ( TALLOC_CTX * ctx ,
const char * stream_name ,
char * * xattr_name )
{
char * stype ;
stype = strchr_m ( stream_name + 1 , ' : ' ) ;
* xattr_name = talloc_asprintf ( ctx , " %s%s " ,
SAMBA_XATTR_DOSSTREAM_PREFIX ,
stream_name + 1 ) ;
if ( * xattr_name = = NULL ) {
return NT_STATUS_NO_MEMORY ;
}
if ( stype = = NULL ) {
/* Append an explicit stream type if one wasn't specified. */
* xattr_name = talloc_asprintf ( ctx , " %s:$DATA " ,
* xattr_name ) ;
if ( * xattr_name = = NULL ) {
return NT_STATUS_NO_MEMORY ;
}
} else {
/* Normalize the stream type to upercase. */
2012-08-09 02:35:28 +04:00
if ( ! strupper_m ( strrchr_m ( * xattr_name , ' : ' ) + 1 ) ) {
return NT_STATUS_INVALID_PARAMETER ;
}
2009-06-16 23:01:13 +04:00
}
DEBUG ( 10 , ( " xattr_name: %s, stream_name: %s \n " , * xattr_name ,
stream_name ) ) ;
return NT_STATUS_OK ;
}
2008-12-02 01:25:20 +03:00
static bool streams_xattr_recheck ( struct stream_io * sio )
{
NTSTATUS status ;
char * xattr_name = NULL ;
if ( sio - > fsp - > fsp_name = = sio - > fsp_name_ptr ) {
return true ;
}
2009-07-11 05:11:32 +04:00
if ( sio - > fsp - > fsp_name - > stream_name = = NULL ) {
2008-12-02 01:25:20 +03:00
/* how can this happen */
errno = EINVAL ;
return false ;
}
2009-07-11 05:11:32 +04:00
status = streams_xattr_get_name ( talloc_tos ( ) ,
sio - > fsp - > fsp_name - > stream_name ,
2009-07-09 01:08:04 +04:00
& xattr_name ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
2008-12-02 01:25:20 +03:00
return false ;
}
TALLOC_FREE ( sio - > xattr_name ) ;
TALLOC_FREE ( sio - > base ) ;
sio - > xattr_name = talloc_strdup ( VFS_MEMCTX_FSP_EXTENSION ( sio - > handle , sio - > fsp ) ,
xattr_name ) ;
sio - > base = talloc_strdup ( VFS_MEMCTX_FSP_EXTENSION ( sio - > handle , sio - > fsp ) ,
2009-07-11 05:11:32 +04:00
sio - > fsp - > fsp_name - > base_name ) ;
2008-12-02 01:25:20 +03:00
sio - > fsp_name_ptr = sio - > fsp - > fsp_name ;
2009-07-09 01:08:04 +04:00
TALLOC_FREE ( xattr_name ) ;
2008-12-02 01:25:20 +03:00
if ( ( sio - > xattr_name = = NULL ) | | ( sio - > base = = NULL ) ) {
return false ;
}
return true ;
}
2008-01-20 01:33:11 +03:00
2009-06-23 02:26:56 +04:00
/**
* Helper to stat / lstat the base file of an smb_fname .
*/
static int streams_xattr_stat_base ( vfs_handle_struct * handle ,
struct smb_filename * smb_fname ,
bool follow_links )
{
char * tmp_stream_name ;
int result ;
tmp_stream_name = smb_fname - > stream_name ;
smb_fname - > stream_name = NULL ;
if ( follow_links ) {
result = SMB_VFS_NEXT_STAT ( handle , smb_fname ) ;
} else {
result = SMB_VFS_NEXT_LSTAT ( handle , smb_fname ) ;
}
smb_fname - > stream_name = tmp_stream_name ;
return result ;
}
2008-01-20 01:33:11 +03:00
static int streams_xattr_fstat ( vfs_handle_struct * handle , files_struct * fsp ,
SMB_STRUCT_STAT * sbuf )
{
2009-06-23 02:26:56 +04:00
struct smb_filename * smb_fname_base = NULL ;
NTSTATUS status ;
2009-02-26 00:46:21 +03:00
int ret = - 1 ;
2008-01-20 01:33:11 +03:00
struct stream_io * io = ( struct stream_io * )
VFS_FETCH_FSP_EXTENSION ( handle , fsp ) ;
2008-01-20 15:55:27 +03:00
DEBUG ( 10 , ( " streams_xattr_fstat called for %d \n " , fsp - > fh - > fd ) ) ;
2008-12-02 00:54:53 +03:00
if ( io = = NULL | | fsp - > base_fsp = = NULL ) {
2008-01-20 01:33:11 +03:00
return SMB_VFS_NEXT_FSTAT ( handle , fsp , sbuf ) ;
}
2008-12-02 01:25:20 +03:00
if ( ! streams_xattr_recheck ( io ) ) {
return - 1 ;
}
2009-06-23 02:26:56 +04:00
/* Create an smb_filename with stream_name == NULL. */
status = create_synthetic_smb_fname ( talloc_tos ( ) ,
io - > base ,
NULL , NULL ,
& smb_fname_base ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
errno = map_errno_from_nt_status ( status ) ;
return - 1 ;
}
2009-02-26 00:46:21 +03:00
if ( lp_posix_pathnames ( ) ) {
2009-06-23 02:26:56 +04:00
ret = SMB_VFS_LSTAT ( handle - > conn , smb_fname_base ) ;
2009-02-26 00:46:21 +03:00
} else {
2009-06-23 02:26:56 +04:00
ret = SMB_VFS_STAT ( handle - > conn , smb_fname_base ) ;
2009-02-26 00:46:21 +03:00
}
2009-06-23 02:26:56 +04:00
* sbuf = smb_fname_base - > st ;
TALLOC_FREE ( smb_fname_base ) ;
2009-02-26 00:46:21 +03:00
if ( ret = = - 1 ) {
2008-01-20 01:33:11 +03:00
return - 1 ;
}
2009-05-14 17:34:42 +04:00
sbuf - > st_ex_size = get_xattr_size ( handle - > conn , fsp - > base_fsp ,
2008-11-22 03:02:31 +03:00
io - > base , io - > xattr_name ) ;
2009-05-14 17:34:42 +04:00
if ( sbuf - > st_ex_size = = - 1 ) {
2008-01-20 01:33:11 +03:00
return - 1 ;
}
2009-05-14 17:34:42 +04:00
DEBUG ( 10 , ( " sbuf->st_ex_size = %d \n " , ( int ) sbuf - > st_ex_size ) ) ;
2008-01-20 15:55:27 +03:00
2009-05-14 17:34:42 +04:00
sbuf - > st_ex_ino = stream_inode ( sbuf , io - > xattr_name ) ;
sbuf - > st_ex_mode & = ~ S_IFMT ;
sbuf - > st_ex_mode | = S_IFREG ;
2010-06-09 17:24:26 +04:00
sbuf - > st_ex_blocks = sbuf - > st_ex_size / STAT_ST_BLOCKSIZE + 1 ;
2008-01-20 01:33:11 +03:00
return 0 ;
}
2009-06-23 02:26:56 +04:00
static int streams_xattr_stat ( vfs_handle_struct * handle ,
struct smb_filename * smb_fname )
2008-01-20 01:33:11 +03:00
{
NTSTATUS status ;
int result = - 1 ;
2009-06-23 02:26:56 +04:00
char * xattr_name = NULL ;
2008-01-20 01:33:11 +03:00
2009-06-23 02:26:56 +04:00
if ( ! is_ntfs_stream_smb_fname ( smb_fname ) ) {
return SMB_VFS_NEXT_STAT ( handle , smb_fname ) ;
2008-01-20 01:33:11 +03:00
}
2009-10-17 05:13:06 +04:00
/* Note if lp_posix_paths() is true, we can never
* get here as is_ntfs_stream_smb_fname ( ) is
* always false . So we never need worry about
* not following links here . */
2009-06-23 02:26:56 +04:00
/* If the default stream is requested, just stat the base file. */
if ( is_ntfs_default_stream_smb_fname ( smb_fname ) ) {
return streams_xattr_stat_base ( handle , smb_fname , true ) ;
2008-12-02 00:54:53 +03:00
}
2009-06-23 02:26:56 +04:00
/* Populate the stat struct with info from the base file. */
if ( streams_xattr_stat_base ( handle , smb_fname , true ) = = - 1 ) {
return - 1 ;
2008-01-20 01:33:11 +03:00
}
2009-06-23 02:26:56 +04:00
/* Derive the xattr name to lookup. */
status = streams_xattr_get_name ( talloc_tos ( ) , smb_fname - > stream_name ,
& xattr_name ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
errno = map_errno_from_nt_status ( status ) ;
return - 1 ;
2008-01-20 01:33:11 +03:00
}
2009-06-23 02:26:56 +04:00
/* Augment the base file's stat information before returning. */
smb_fname - > st . st_ex_size = get_xattr_size ( handle - > conn , NULL ,
smb_fname - > base_name ,
xattr_name ) ;
if ( smb_fname - > st . st_ex_size = = - 1 ) {
2008-01-20 01:33:11 +03:00
errno = ENOENT ;
2009-06-23 02:26:56 +04:00
result = - 1 ;
2008-01-20 01:33:11 +03:00
goto fail ;
}
2009-06-23 02:26:56 +04:00
smb_fname - > st . st_ex_ino = stream_inode ( & smb_fname - > st , xattr_name ) ;
smb_fname - > st . st_ex_mode & = ~ S_IFMT ;
smb_fname - > st . st_ex_mode | = S_IFREG ;
smb_fname - > st . st_ex_blocks =
2010-06-09 17:24:26 +04:00
smb_fname - > st . st_ex_size / STAT_ST_BLOCKSIZE + 1 ;
2008-01-20 01:33:11 +03:00
result = 0 ;
fail :
2009-06-23 02:26:56 +04:00
TALLOC_FREE ( xattr_name ) ;
2008-01-20 01:33:11 +03:00
return result ;
}
2009-06-23 02:26:56 +04:00
static int streams_xattr_lstat ( vfs_handle_struct * handle ,
struct smb_filename * smb_fname )
2008-01-20 01:33:11 +03:00
{
NTSTATUS status ;
int result = - 1 ;
2009-06-23 02:26:56 +04:00
char * xattr_name = NULL ;
2008-01-20 01:33:11 +03:00
2009-06-23 02:26:56 +04:00
if ( ! is_ntfs_stream_smb_fname ( smb_fname ) ) {
return SMB_VFS_NEXT_LSTAT ( handle , smb_fname ) ;
2008-01-20 01:33:11 +03:00
}
2009-10-17 05:13:06 +04:00
/* If the default stream is requested, just stat the base file. */
2009-06-23 02:26:56 +04:00
if ( is_ntfs_default_stream_smb_fname ( smb_fname ) ) {
return streams_xattr_stat_base ( handle , smb_fname , false ) ;
2008-12-02 00:54:53 +03:00
}
2009-06-23 02:26:56 +04:00
/* Populate the stat struct with info from the base file. */
if ( streams_xattr_stat_base ( handle , smb_fname , false ) = = - 1 ) {
return - 1 ;
2008-01-20 01:33:11 +03:00
}
2009-06-23 02:26:56 +04:00
/* Derive the xattr name to lookup. */
status = streams_xattr_get_name ( talloc_tos ( ) , smb_fname - > stream_name ,
& xattr_name ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
errno = map_errno_from_nt_status ( status ) ;
return - 1 ;
2008-01-20 01:33:11 +03:00
}
2009-06-23 02:26:56 +04:00
/* Augment the base file's stat information before returning. */
smb_fname - > st . st_ex_size = get_xattr_size ( handle - > conn , NULL ,
smb_fname - > base_name ,
xattr_name ) ;
if ( smb_fname - > st . st_ex_size = = - 1 ) {
2008-01-20 01:33:11 +03:00
errno = ENOENT ;
2009-06-23 02:26:56 +04:00
result = - 1 ;
2008-01-20 01:33:11 +03:00
goto fail ;
}
2009-06-23 02:26:56 +04:00
smb_fname - > st . st_ex_ino = stream_inode ( & smb_fname - > st , xattr_name ) ;
smb_fname - > st . st_ex_mode & = ~ S_IFMT ;
smb_fname - > st . st_ex_mode | = S_IFREG ;
smb_fname - > st . st_ex_blocks =
2010-06-09 17:24:26 +04:00
smb_fname - > st . st_ex_size / STAT_ST_BLOCKSIZE + 1 ;
2008-01-20 01:33:11 +03:00
result = 0 ;
2009-06-23 02:26:56 +04:00
2008-01-20 01:33:11 +03:00
fail :
2009-06-23 02:26:56 +04:00
TALLOC_FREE ( xattr_name ) ;
2008-01-20 01:33:11 +03:00
return result ;
}
2009-06-16 23:01:13 +04:00
static int streams_xattr_open ( vfs_handle_struct * handle ,
struct smb_filename * smb_fname ,
2008-01-20 01:33:11 +03:00
files_struct * fsp , int flags , mode_t mode )
{
NTSTATUS status ;
2009-06-16 23:01:13 +04:00
struct smb_filename * smb_fname_base = NULL ;
2008-01-20 01:33:11 +03:00
struct stream_io * sio ;
struct ea_struct ea ;
2009-06-16 23:01:13 +04:00
char * xattr_name = NULL ;
2008-01-20 01:33:11 +03:00
int baseflags ;
int hostfd = - 1 ;
2009-06-16 23:01:13 +04:00
DEBUG ( 10 , ( " streams_xattr_open called for %s \n " ,
smb_fname_str_dbg ( smb_fname ) ) ) ;
2008-01-20 15:55:27 +03:00
2009-06-16 23:01:13 +04:00
if ( ! is_ntfs_stream_smb_fname ( smb_fname ) ) {
return SMB_VFS_NEXT_OPEN ( handle , smb_fname , fsp , flags , mode ) ;
2008-01-20 01:33:11 +03:00
}
2009-06-16 23:01:13 +04:00
/* If the default stream is requested, just open the base file. */
if ( is_ntfs_default_stream_smb_fname ( smb_fname ) ) {
char * tmp_stream_name ;
int ret ;
2008-01-20 01:33:11 +03:00
2009-06-16 23:01:13 +04:00
tmp_stream_name = smb_fname - > stream_name ;
smb_fname - > stream_name = NULL ;
ret = SMB_VFS_NEXT_OPEN ( handle , smb_fname , fsp , flags , mode ) ;
smb_fname - > stream_name = tmp_stream_name ;
return ret ;
2008-01-20 01:33:11 +03:00
}
2009-06-16 23:01:13 +04:00
status = streams_xattr_get_name ( talloc_tos ( ) , smb_fname - > stream_name ,
& xattr_name ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
errno = map_errno_from_nt_status ( status ) ;
goto fail ;
2008-12-02 00:54:53 +03:00
}
2009-06-16 23:01:13 +04:00
/* Create an smb_filename with stream_name == NULL. */
status = create_synthetic_smb_fname ( talloc_tos ( ) ,
smb_fname - > base_name ,
NULL , NULL ,
& smb_fname_base ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
errno = map_errno_from_nt_status ( status ) ;
2008-01-20 01:33:11 +03:00
goto fail ;
}
/*
* We use baseflags to turn off nasty side - effects when opening the
* underlying file .
2008-11-22 09:48:37 +03:00
*/
baseflags = flags ;
baseflags & = ~ O_TRUNC ;
baseflags & = ~ O_EXCL ;
baseflags & = ~ O_CREAT ;
2009-06-16 23:01:13 +04:00
hostfd = SMB_VFS_OPEN ( handle - > conn , smb_fname_base , fsp ,
baseflags , mode ) ;
TALLOC_FREE ( smb_fname_base ) ;
2008-11-22 09:48:37 +03:00
/* It is legit to open a stream on a directory, but the base
* fd has to be read - only .
*/
if ( ( hostfd = = - 1 ) & & ( errno = = EISDIR ) ) {
baseflags & = ~ O_ACCMODE ;
baseflags | = O_RDONLY ;
2009-06-16 23:01:13 +04:00
hostfd = SMB_VFS_OPEN ( handle - > conn , smb_fname , fsp , baseflags ,
2008-11-22 09:48:37 +03:00
mode ) ;
}
2008-01-20 01:33:11 +03:00
2008-11-22 09:48:37 +03:00
if ( hostfd = = - 1 ) {
2008-01-20 01:33:11 +03:00
goto fail ;
2008-11-22 09:48:37 +03:00
}
2008-01-20 01:33:11 +03:00
2009-06-16 23:01:13 +04:00
status = get_ea_value ( talloc_tos ( ) , handle - > conn , NULL ,
smb_fname - > base_name , xattr_name , & ea ) ;
2008-01-20 01:33:11 +03:00
2008-01-20 15:55:27 +03:00
DEBUG ( 10 , ( " get_ea_value returned %s \n " , nt_errstr ( status ) ) ) ;
2008-01-20 01:33:11 +03:00
if ( ! NT_STATUS_IS_OK ( status )
& & ! NT_STATUS_EQUAL ( status , NT_STATUS_NOT_FOUND ) ) {
/*
* The base file is not there . This is an error even if we got
* O_CREAT , the higher levels should have created the base
* file for us .
*/
2008-01-20 15:55:27 +03:00
DEBUG ( 10 , ( " streams_xattr_open: base file %s not around, "
2009-06-16 23:01:13 +04:00
" returning ENOENT \n " , smb_fname - > base_name ) ) ;
2008-01-20 01:33:11 +03:00
errno = ENOENT ;
goto fail ;
}
if ( ! NT_STATUS_IS_OK ( status ) ) {
/*
* The attribute does not exist
*/
if ( flags & O_CREAT ) {
/*
* Darn , xattrs need at least 1 byte
*/
char null = ' \0 ' ;
2008-01-20 15:49:38 +03:00
DEBUG ( 10 , ( " creating attribute %s on file %s \n " ,
2009-06-16 23:01:13 +04:00
xattr_name , smb_fname - > base_name ) ) ;
2008-01-20 15:49:38 +03:00
2008-11-22 09:48:37 +03:00
if ( fsp - > base_fsp - > fh - > fd ! = - 1 ) {
2008-11-22 03:02:31 +03:00
if ( SMB_VFS_FSETXATTR (
2008-11-22 09:48:37 +03:00
fsp - > base_fsp , xattr_name ,
2008-11-22 03:02:31 +03:00
& null , sizeof ( null ) ,
flags & O_EXCL ? XATTR_CREATE : 0 ) = = - 1 ) {
goto fail ;
}
} else {
if ( SMB_VFS_SETXATTR (
2009-06-16 23:01:13 +04:00
handle - > conn , smb_fname - > base_name ,
xattr_name , & null , sizeof ( null ) ,
2008-11-22 03:02:31 +03:00
flags & O_EXCL ? XATTR_CREATE : 0 ) = = - 1 ) {
goto fail ;
}
2008-01-20 01:33:11 +03:00
}
}
}
if ( flags & O_TRUNC ) {
char null = ' \0 ' ;
2008-11-22 09:48:37 +03:00
if ( fsp - > base_fsp - > fh - > fd ! = - 1 ) {
2008-11-22 03:02:31 +03:00
if ( SMB_VFS_FSETXATTR (
2008-11-22 09:48:37 +03:00
fsp - > base_fsp , xattr_name ,
2008-11-22 03:02:31 +03:00
& null , sizeof ( null ) ,
flags & O_EXCL ? XATTR_CREATE : 0 ) = = - 1 ) {
goto fail ;
}
} else {
if ( SMB_VFS_SETXATTR (
2009-06-16 23:01:13 +04:00
handle - > conn , smb_fname - > base_name ,
xattr_name , & null , sizeof ( null ) ,
2008-11-22 03:02:31 +03:00
flags & O_EXCL ? XATTR_CREATE : 0 ) = = - 1 ) {
goto fail ;
}
2008-01-20 01:33:11 +03:00
}
}
sio = ( struct stream_io * ) VFS_ADD_FSP_EXTENSION ( handle , fsp ,
2009-02-01 07:51:04 +03:00
struct stream_io ,
NULL ) ;
2008-01-20 01:33:11 +03:00
if ( sio = = NULL ) {
errno = ENOMEM ;
goto fail ;
}
sio - > xattr_name = talloc_strdup ( VFS_MEMCTX_FSP_EXTENSION ( handle , fsp ) ,
xattr_name ) ;
sio - > base = talloc_strdup ( VFS_MEMCTX_FSP_EXTENSION ( handle , fsp ) ,
2009-06-16 23:01:13 +04:00
smb_fname - > base_name ) ;
2008-12-02 01:25:20 +03:00
sio - > fsp_name_ptr = fsp - > fsp_name ;
sio - > handle = handle ;
sio - > fsp = fsp ;
2008-01-20 01:33:11 +03:00
if ( ( sio - > xattr_name = = NULL ) | | ( sio - > base = = NULL ) ) {
errno = ENOMEM ;
goto fail ;
}
return hostfd ;
fail :
if ( hostfd > = 0 ) {
/*
* BUGBUGBUG - - we would need to call fd_close_posix here , but
* we don ' t have a full fsp yet
*/
2008-01-11 16:19:28 +03:00
SMB_VFS_CLOSE ( fsp ) ;
2008-01-20 01:33:11 +03:00
}
return - 1 ;
}
2009-07-02 20:27:44 +04:00
static int streams_xattr_unlink ( vfs_handle_struct * handle ,
const struct smb_filename * smb_fname )
2008-01-20 01:33:11 +03:00
{
NTSTATUS status ;
int ret = - 1 ;
char * xattr_name ;
2009-07-02 20:27:44 +04:00
if ( ! is_ntfs_stream_smb_fname ( smb_fname ) ) {
return SMB_VFS_NEXT_UNLINK ( handle , smb_fname ) ;
2008-01-20 01:33:11 +03:00
}
2009-07-02 20:27:44 +04:00
/* If the default stream is requested, just open the base file. */
if ( is_ntfs_default_stream_smb_fname ( smb_fname ) ) {
struct smb_filename * smb_fname_base = NULL ;
status = copy_smb_filename ( talloc_tos ( ) , smb_fname ,
& smb_fname_base ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
errno = map_errno_from_nt_status ( status ) ;
return - 1 ;
}
2008-01-20 01:33:11 +03:00
2009-07-02 20:27:44 +04:00
ret = SMB_VFS_NEXT_UNLINK ( handle , smb_fname_base ) ;
TALLOC_FREE ( smb_fname_base ) ;
return ret ;
2008-12-02 00:54:53 +03:00
}
2009-07-02 20:27:44 +04:00
status = streams_xattr_get_name ( talloc_tos ( ) , smb_fname - > stream_name ,
& xattr_name ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
errno = map_errno_from_nt_status ( status ) ;
2008-01-20 01:33:11 +03:00
goto fail ;
}
2009-07-02 20:27:44 +04:00
ret = SMB_VFS_REMOVEXATTR ( handle - > conn , smb_fname - > base_name , xattr_name ) ;
2008-01-20 01:33:11 +03:00
if ( ( ret = = - 1 ) & & ( errno = = ENOATTR ) ) {
errno = ENOENT ;
goto fail ;
}
ret = 0 ;
fail :
2009-07-02 20:27:44 +04:00
TALLOC_FREE ( xattr_name ) ;
2008-01-20 01:33:11 +03:00
return ret ;
}
2008-12-02 00:54:53 +03:00
static int streams_xattr_rename ( vfs_handle_struct * handle ,
2009-07-01 04:04:38 +04:00
const struct smb_filename * smb_fname_src ,
const struct smb_filename * smb_fname_dst )
2008-12-02 00:54:53 +03:00
{
NTSTATUS status ;
int ret = - 1 ;
2009-07-01 04:04:38 +04:00
char * src_xattr_name = NULL ;
char * dst_xattr_name = NULL ;
bool src_is_stream , dst_is_stream ;
2008-12-02 00:54:53 +03:00
ssize_t oret ;
ssize_t nret ;
struct ea_struct ea ;
2009-07-01 04:04:38 +04:00
src_is_stream = is_ntfs_stream_smb_fname ( smb_fname_src ) ;
dst_is_stream = is_ntfs_stream_smb_fname ( smb_fname_dst ) ;
2008-12-02 00:54:53 +03:00
2009-07-01 04:04:38 +04:00
if ( ! src_is_stream & & ! dst_is_stream ) {
return SMB_VFS_NEXT_RENAME ( handle , smb_fname_src ,
smb_fname_dst ) ;
2008-12-02 00:54:53 +03:00
}
2009-07-01 04:04:38 +04:00
/* For now don't allow renames from or to the default stream. */
if ( is_ntfs_default_stream_smb_fname ( smb_fname_src ) | |
is_ntfs_default_stream_smb_fname ( smb_fname_dst ) ) {
2008-12-02 00:54:53 +03:00
errno = ENOSYS ;
2009-07-01 04:04:38 +04:00
goto done ;
2008-12-02 00:54:53 +03:00
}
2009-07-01 04:04:38 +04:00
/* Don't rename if the streams are identical. */
2011-05-13 22:21:30 +04:00
if ( strcasecmp_m ( smb_fname_src - > stream_name ,
2009-07-01 04:04:38 +04:00
smb_fname_dst - > stream_name ) = = 0 ) {
2008-12-02 00:54:53 +03:00
goto done ;
}
2009-07-01 04:04:38 +04:00
/* Get the xattr names. */
status = streams_xattr_get_name ( talloc_tos ( ) ,
smb_fname_src - > stream_name ,
& src_xattr_name ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
errno = map_errno_from_nt_status ( status ) ;
2008-12-02 00:54:53 +03:00
goto fail ;
}
2009-07-01 04:04:38 +04:00
status = streams_xattr_get_name ( talloc_tos ( ) ,
smb_fname_dst - > stream_name ,
& dst_xattr_name ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
errno = map_errno_from_nt_status ( status ) ;
2008-12-02 00:54:53 +03:00
goto fail ;
}
/* read the old stream */
status = get_ea_value ( talloc_tos ( ) , handle - > conn , NULL ,
2009-07-01 04:04:38 +04:00
smb_fname_src - > base_name , src_xattr_name , & ea ) ;
2008-12-02 00:54:53 +03:00
if ( ! NT_STATUS_IS_OK ( status ) ) {
errno = ENOENT ;
goto fail ;
}
/* (over)write the new stream */
2009-07-01 04:04:38 +04:00
nret = SMB_VFS_SETXATTR ( handle - > conn , smb_fname_src - > base_name ,
dst_xattr_name , ea . value . data , ea . value . length ,
0 ) ;
2008-12-02 00:54:53 +03:00
if ( nret < 0 ) {
if ( errno = = ENOATTR ) {
errno = ENOENT ;
}
goto fail ;
}
/* remove the old stream */
2009-07-01 04:04:38 +04:00
oret = SMB_VFS_REMOVEXATTR ( handle - > conn , smb_fname_src - > base_name ,
src_xattr_name ) ;
2008-12-02 00:54:53 +03:00
if ( oret < 0 ) {
if ( errno = = ENOATTR ) {
errno = ENOENT ;
}
goto fail ;
}
done :
errno = 0 ;
ret = 0 ;
fail :
2009-07-01 04:04:38 +04:00
TALLOC_FREE ( src_xattr_name ) ;
TALLOC_FREE ( dst_xattr_name ) ;
2008-12-02 00:54:53 +03:00
return ret ;
}
2008-01-20 01:33:11 +03:00
static NTSTATUS walk_xattr_streams ( connection_struct * conn , files_struct * fsp ,
const char * fname ,
bool ( * fn ) ( struct ea_struct * ea ,
void * private_data ) ,
void * private_data )
{
NTSTATUS status ;
char * * names ;
size_t i , num_names ;
2008-01-21 22:24:23 +03:00
size_t prefix_len = strlen ( SAMBA_XATTR_DOSSTREAM_PREFIX ) ;
2008-01-20 01:33:11 +03:00
status = get_ea_names_from_file ( talloc_tos ( ) , conn , fsp , fname ,
& names , & num_names ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
return status ;
}
for ( i = 0 ; i < num_names ; i + + ) {
struct ea_struct ea ;
2008-01-21 22:24:23 +03:00
if ( strncmp ( names [ i ] , SAMBA_XATTR_DOSSTREAM_PREFIX ,
2008-01-20 01:33:11 +03:00
prefix_len ) ! = 0 ) {
continue ;
}
status = get_ea_value ( names , conn , fsp , fname , names [ i ] , & ea ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
DEBUG ( 10 , ( " Could not get ea %s for file %s: %s \n " ,
names [ i ] , fname , nt_errstr ( status ) ) ) ;
continue ;
}
ea . name = talloc_asprintf ( ea . value . data , " :%s " ,
names [ i ] + prefix_len ) ;
if ( ea . name = = NULL ) {
DEBUG ( 0 , ( " talloc failed \n " ) ) ;
continue ;
}
if ( ! fn ( & ea , private_data ) ) {
TALLOC_FREE ( ea . value . data ) ;
return NT_STATUS_OK ;
}
TALLOC_FREE ( ea . value . data ) ;
}
TALLOC_FREE ( names ) ;
return NT_STATUS_OK ;
}
static bool add_one_stream ( TALLOC_CTX * mem_ctx , unsigned int * num_streams ,
struct stream_struct * * streams ,
2012-04-05 08:53:08 +04:00
const char * name , off_t size ,
off_t alloc_size )
2008-01-20 01:33:11 +03:00
{
struct stream_struct * tmp ;
2011-06-07 05:10:15 +04:00
tmp = talloc_realloc ( mem_ctx , * streams , struct stream_struct ,
2008-01-20 01:33:11 +03:00
( * num_streams ) + 1 ) ;
if ( tmp = = NULL ) {
return false ;
}
tmp [ * num_streams ] . name = talloc_strdup ( tmp , name ) ;
if ( tmp [ * num_streams ] . name = = NULL ) {
return false ;
}
tmp [ * num_streams ] . size = size ;
tmp [ * num_streams ] . alloc_size = alloc_size ;
* streams = tmp ;
* num_streams + = 1 ;
return true ;
}
struct streaminfo_state {
TALLOC_CTX * mem_ctx ;
vfs_handle_struct * handle ;
unsigned int num_streams ;
struct stream_struct * streams ;
NTSTATUS status ;
} ;
static bool collect_one_stream ( struct ea_struct * ea , void * private_data )
{
struct streaminfo_state * state =
( struct streaminfo_state * ) private_data ;
if ( ! add_one_stream ( state - > mem_ctx ,
& state - > num_streams , & state - > streams ,
ea - > name , ea - > value . length - 1 ,
smb_roundup ( state - > handle - > conn ,
ea - > value . length - 1 ) ) ) {
state - > status = NT_STATUS_NO_MEMORY ;
return false ;
}
return true ;
}
static NTSTATUS streams_xattr_streaminfo ( vfs_handle_struct * handle ,
struct files_struct * fsp ,
const char * fname ,
TALLOC_CTX * mem_ctx ,
unsigned int * pnum_streams ,
struct stream_struct * * pstreams )
{
SMB_STRUCT_STAT sbuf ;
int ret ;
NTSTATUS status ;
struct streaminfo_state state ;
if ( ( fsp ! = NULL ) & & ( fsp - > fh - > fd ! = - 1 ) ) {
2008-01-20 15:49:38 +03:00
ret = SMB_VFS_FSTAT ( fsp , & sbuf ) ;
2008-01-20 01:33:11 +03:00
}
else {
2009-07-07 02:26:57 +04:00
struct smb_filename * smb_fname = NULL ;
status = create_synthetic_smb_fname ( talloc_tos ( ) , fname , NULL ,
NULL , & smb_fname ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
return status ;
2008-01-20 01:33:11 +03:00
}
2009-02-26 00:46:21 +03:00
if ( lp_posix_pathnames ( ) ) {
2009-07-07 02:26:57 +04:00
ret = SMB_VFS_LSTAT ( handle - > conn , smb_fname ) ;
2009-02-26 00:46:21 +03:00
} else {
2009-07-07 02:26:57 +04:00
ret = SMB_VFS_STAT ( handle - > conn , smb_fname ) ;
2009-02-26 00:46:21 +03:00
}
2009-07-07 02:26:57 +04:00
sbuf = smb_fname - > st ;
TALLOC_FREE ( smb_fname ) ;
2008-01-20 01:33:11 +03:00
}
if ( ret = = - 1 ) {
return map_nt_error_from_unix ( errno ) ;
}
2011-10-14 02:41:53 +04:00
state . streams = * pstreams ;
state . num_streams = * pnum_streams ;
2008-01-20 01:33:11 +03:00
state . mem_ctx = mem_ctx ;
state . handle = handle ;
state . status = NT_STATUS_OK ;
status = walk_xattr_streams ( handle - > conn , fsp , fname ,
collect_one_stream , & state ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
TALLOC_FREE ( state . streams ) ;
return status ;
}
if ( ! NT_STATUS_IS_OK ( state . status ) ) {
TALLOC_FREE ( state . streams ) ;
return state . status ;
}
* pnum_streams = state . num_streams ;
* pstreams = state . streams ;
2011-10-14 02:41:53 +04:00
return SMB_VFS_NEXT_STREAMINFO ( handle , fsp , fname , mem_ctx , pnum_streams , pstreams ) ;
2008-01-20 01:33:11 +03:00
}
2009-08-25 07:57:37 +04:00
static uint32_t streams_xattr_fs_capabilities ( struct vfs_handle_struct * handle ,
enum timestamp_set_resolution * p_ts_res )
2008-01-20 01:33:11 +03:00
{
2009-08-25 07:57:37 +04:00
return SMB_VFS_NEXT_FS_CAPABILITIES ( handle , p_ts_res ) | FILE_NAMED_STREAMS ;
2008-01-20 01:33:11 +03:00
}
static ssize_t streams_xattr_pwrite ( vfs_handle_struct * handle ,
files_struct * fsp , const void * data ,
2012-04-05 08:53:08 +04:00
size_t n , off_t offset )
2008-01-20 01:33:11 +03:00
{
struct stream_io * sio =
( struct stream_io * ) VFS_FETCH_FSP_EXTENSION ( handle , fsp ) ;
struct ea_struct ea ;
NTSTATUS status ;
int ret ;
2008-01-20 15:55:27 +03:00
DEBUG ( 10 , ( " streams_xattr_pwrite called for %d bytes \n " , ( int ) n ) ) ;
2008-01-20 01:33:11 +03:00
if ( sio = = NULL ) {
return SMB_VFS_NEXT_PWRITE ( handle , fsp , data , n , offset ) ;
}
2008-12-02 01:25:20 +03:00
if ( ! streams_xattr_recheck ( sio ) ) {
return - 1 ;
}
2008-11-22 09:48:37 +03:00
status = get_ea_value ( talloc_tos ( ) , handle - > conn , fsp - > base_fsp ,
2008-01-20 15:51:52 +03:00
sio - > base , sio - > xattr_name , & ea ) ;
2008-01-20 01:33:11 +03:00
if ( ! NT_STATUS_IS_OK ( status ) ) {
return - 1 ;
}
if ( ( offset + n ) > ea . value . length - 1 ) {
uint8 * tmp ;
2011-06-07 05:10:15 +04:00
tmp = talloc_realloc ( talloc_tos ( ) , ea . value . data , uint8 ,
2008-01-20 01:33:11 +03:00
offset + n + 1 ) ;
if ( tmp = = NULL ) {
TALLOC_FREE ( ea . value . data ) ;
errno = ENOMEM ;
return - 1 ;
}
ea . value . data = tmp ;
ea . value . length = offset + n + 1 ;
ea . value . data [ offset + n ] = 0 ;
}
memcpy ( ea . value . data + offset , data , n ) ;
2008-11-22 09:48:37 +03:00
if ( fsp - > base_fsp - > fh - > fd ! = - 1 ) {
ret = SMB_VFS_FSETXATTR ( fsp - > base_fsp ,
2008-01-20 16:05:36 +03:00
sio - > xattr_name ,
2008-01-20 15:49:38 +03:00
ea . value . data , ea . value . length , 0 ) ;
2008-11-22 03:02:31 +03:00
} else {
2009-07-11 05:11:32 +04:00
ret = SMB_VFS_SETXATTR ( fsp - > conn ,
fsp - > base_fsp - > fsp_name - > base_name ,
2008-11-22 03:02:31 +03:00
sio - > xattr_name ,
ea . value . data , ea . value . length , 0 ) ;
}
2008-01-20 01:33:11 +03:00
TALLOC_FREE ( ea . value . data ) ;
2008-01-20 19:35:25 +03:00
if ( ret = = - 1 ) {
return - 1 ;
}
2008-01-20 01:33:11 +03:00
return n ;
}
static ssize_t streams_xattr_pread ( vfs_handle_struct * handle ,
files_struct * fsp , void * data ,
2012-04-05 08:53:08 +04:00
size_t n , off_t offset )
2008-01-20 01:33:11 +03:00
{
struct stream_io * sio =
( struct stream_io * ) VFS_FETCH_FSP_EXTENSION ( handle , fsp ) ;
struct ea_struct ea ;
NTSTATUS status ;
2008-11-22 02:42:03 +03:00
size_t length , overlap ;
2008-01-20 01:33:11 +03:00
2009-09-16 05:15:53 +04:00
DEBUG ( 10 , ( " streams_xattr_pread: offset=%d, size=%d \n " ,
( int ) offset , ( int ) n ) ) ;
2008-01-20 01:33:11 +03:00
if ( sio = = NULL ) {
return SMB_VFS_NEXT_PREAD ( handle , fsp , data , n , offset ) ;
}
2008-12-02 01:25:20 +03:00
if ( ! streams_xattr_recheck ( sio ) ) {
return - 1 ;
}
2008-11-22 09:48:37 +03:00
status = get_ea_value ( talloc_tos ( ) , handle - > conn , fsp - > base_fsp ,
2008-01-20 15:51:52 +03:00
sio - > base , sio - > xattr_name , & ea ) ;
2008-01-20 01:33:11 +03:00
if ( ! NT_STATUS_IS_OK ( status ) ) {
return - 1 ;
}
length = ea . value . length - 1 ;
2009-09-16 05:15:53 +04:00
DEBUG ( 10 , ( " streams_xattr_pread: get_ea_value returned %d bytes \n " ,
( int ) length ) ) ;
2008-01-20 01:33:11 +03:00
/* Attempt to read past EOF. */
if ( length < = offset ) {
2009-09-16 05:20:49 +04:00
return 0 ;
2008-01-20 01:33:11 +03:00
}
overlap = ( offset + n ) > length ? ( length - offset ) : n ;
memcpy ( data , ea . value . data + offset , overlap ) ;
TALLOC_FREE ( ea . value . data ) ;
return overlap ;
}
2008-11-22 02:42:03 +03:00
static int streams_xattr_ftruncate ( struct vfs_handle_struct * handle ,
struct files_struct * fsp ,
2012-04-05 08:53:08 +04:00
off_t offset )
2008-11-22 02:42:03 +03:00
{
int ret ;
uint8 * tmp ;
struct ea_struct ea ;
NTSTATUS status ;
struct stream_io * sio =
( struct stream_io * ) VFS_FETCH_FSP_EXTENSION ( handle , fsp ) ;
DEBUG ( 10 , ( " streams_xattr_ftruncate called for file %s offset %.0f \n " ,
2009-07-11 05:11:32 +04:00
fsp_str_dbg ( fsp ) , ( double ) offset ) ) ;
2008-11-22 02:42:03 +03:00
if ( sio = = NULL ) {
return SMB_VFS_NEXT_FTRUNCATE ( handle , fsp , offset ) ;
}
2008-12-02 01:25:20 +03:00
if ( ! streams_xattr_recheck ( sio ) ) {
return - 1 ;
}
2008-11-22 09:48:37 +03:00
status = get_ea_value ( talloc_tos ( ) , handle - > conn , fsp - > base_fsp ,
2008-11-22 02:42:03 +03:00
sio - > base , sio - > xattr_name , & ea ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
return - 1 ;
}
2011-06-07 05:10:15 +04:00
tmp = talloc_realloc ( talloc_tos ( ) , ea . value . data , uint8 ,
2008-11-22 02:42:03 +03:00
offset + 1 ) ;
if ( tmp = = NULL ) {
TALLOC_FREE ( ea . value . data ) ;
errno = ENOMEM ;
return - 1 ;
}
/* Did we expand ? */
if ( ea . value . length < offset + 1 ) {
memset ( & tmp [ ea . value . length ] , ' \0 ' ,
offset + 1 - ea . value . length ) ;
}
ea . value . data = tmp ;
ea . value . length = offset + 1 ;
ea . value . data [ offset ] = 0 ;
2008-11-22 09:48:37 +03:00
if ( fsp - > base_fsp - > fh - > fd ! = - 1 ) {
ret = SMB_VFS_FSETXATTR ( fsp - > base_fsp ,
2008-11-22 02:42:03 +03:00
sio - > xattr_name ,
ea . value . data , ea . value . length , 0 ) ;
2008-11-22 03:02:31 +03:00
} else {
2009-07-11 05:11:32 +04:00
ret = SMB_VFS_SETXATTR ( fsp - > conn ,
fsp - > base_fsp - > fsp_name - > base_name ,
2008-11-22 03:02:31 +03:00
sio - > xattr_name ,
ea . value . data , ea . value . length , 0 ) ;
}
2008-11-22 02:42:03 +03:00
TALLOC_FREE ( ea . value . data ) ;
if ( ret = = - 1 ) {
return - 1 ;
}
return 0 ;
}
2010-12-18 10:08:01 +03:00
static int streams_xattr_fallocate ( struct vfs_handle_struct * handle ,
2010-12-03 03:25:59 +03:00
struct files_struct * fsp ,
2010-12-18 10:08:01 +03:00
enum vfs_fallocate_mode mode ,
2012-04-05 08:53:08 +04:00
off_t offset ,
off_t len )
2010-12-03 03:25:59 +03:00
{
struct stream_io * sio =
( struct stream_io * ) VFS_FETCH_FSP_EXTENSION ( handle , fsp ) ;
2010-12-18 10:08:01 +03:00
DEBUG ( 10 , ( " streams_xattr_fallocate called for file %s offset %.0f "
2010-12-03 03:25:59 +03:00
" len = %.0f \n " ,
fsp_str_dbg ( fsp ) , ( double ) offset , ( double ) len ) ) ;
if ( sio = = NULL ) {
2010-12-18 10:08:01 +03:00
return SMB_VFS_NEXT_FALLOCATE ( handle , fsp , mode , offset , len ) ;
2010-12-03 03:25:59 +03:00
}
if ( ! streams_xattr_recheck ( sio ) ) {
2010-12-03 04:46:30 +03:00
return errno ;
2010-12-03 03:25:59 +03:00
}
/* Let the pwrite code path handle it. */
return ENOSYS ;
}
2009-07-24 04:28:58 +04:00
static struct vfs_fn_pointers vfs_streams_xattr_fns = {
2011-12-04 08:45:04 +04:00
. fs_capabilities_fn = streams_xattr_fs_capabilities ,
2011-04-21 00:55:25 +04:00
. open_fn = streams_xattr_open ,
2011-12-04 08:45:04 +04:00
. stat_fn = streams_xattr_stat ,
. fstat_fn = streams_xattr_fstat ,
. lstat_fn = streams_xattr_lstat ,
. pread_fn = streams_xattr_pread ,
. pwrite_fn = streams_xattr_pwrite ,
. unlink_fn = streams_xattr_unlink ,
. rename_fn = streams_xattr_rename ,
. ftruncate_fn = streams_xattr_ftruncate ,
. fallocate_fn = streams_xattr_fallocate ,
. streaminfo_fn = streams_xattr_streaminfo ,
2008-01-20 01:33:11 +03:00
} ;
NTSTATUS vfs_streams_xattr_init ( void ) ;
NTSTATUS vfs_streams_xattr_init ( void )
{
return smb_register_vfs ( SMB_VFS_INTERFACE_VERSION , " streams_xattr " ,
2009-07-24 04:28:58 +04:00
& vfs_streams_xattr_fns ) ;
2008-01-20 01:33:11 +03:00
}