2014-06-23 18:59:45 +04:00
/*
* OS X and Netatalk interoperability VFS module for Samba - 3. x
*
* Copyright ( C ) Ralph Boehme , 2013 , 2014
*
* 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"
# include "MacExtensions.h"
# include "smbd/smbd.h"
# include "system/filesys.h"
# include "lib/util/time.h"
# include "../lib/crypto/md5.h"
# include "system/shmem.h"
# include "locking/proto.h"
# include "smbd/globals.h"
# include "messages.h"
# include "libcli/security/security.h"
2014-11-26 20:11:17 +03:00
# include "../libcli/smb/smb2_create_ctx.h"
2015-10-12 16:57:34 +03:00
# include "lib/util/sys_rw.h"
2015-04-22 23:29:16 +03:00
# include "lib/util/tevent_ntstatus.h"
2017-05-12 15:40:03 +03:00
# include "lib/util/tevent_unix.h"
2017-06-03 13:57:59 +03:00
# include "offload_token.h"
2017-10-11 13:58:59 +03:00
# include "string_replace.h"
2014-06-23 18:59:45 +04:00
/*
* Enhanced OS X and Netatalk compatibility
* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
*
* This modules takes advantage of vfs_streams_xattr and
* vfs_catia . VFS modules vfs_fruit and vfs_streams_xattr must be
* loaded in the correct order :
*
* vfs modules = catia fruit streams_xattr
*
* The module intercepts the OS X special streams " AFP_AfpInfo " and
* " AFP_Resource " and handles them in a special way . All other named
* streams are deferred to vfs_streams_xattr .
*
* The OS X client maps all NTFS illegal characters to the Unicode
* private range . This module optionally stores the charcters using
* their native ASCII encoding using vfs_catia . If you ' re not enabling
* this feature , you can skip catia from vfs modules .
*
* Finally , open modes are optionally checked against Netatalk AFP
* share modes .
*
* The " AFP_AfpInfo " named stream is a binary blob containing OS X
* extended metadata for files and directories . This module optionally
* reads and stores this metadata in a way compatible with Netatalk 3
* which stores the metadata in an EA " org.netatalk.metadata " . Cf
* source3 / include / MacExtensions . h for a description of the binary
* blobs content .
*
* The " AFP_Resource " named stream may be arbitrarily large , thus it
* can ' t be stored in an xattr on most filesystem . ZFS on Solaris is
* the only available filesystem where xattrs can be of any size and
* the OS supports using the file APIs for xattrs .
*
* The AFP_Resource stream is stored in an AppleDouble file prepending
* " ._ " to the filename . On Solaris with ZFS the stream is optionally
2016-11-08 14:35:12 +03:00
* stored in an EA " org.netatalk.resource " .
2014-06-23 18:59:45 +04:00
*
*
* Extended Attributes
* = = = = = = = = = = = = = = = = = = =
*
* The OS X SMB client sends xattrs as ADS too . For xattr interop with
* other protocols you may want to adjust the xattr names the VFS
* module vfs_streams_xattr uses for storing ADS ' s . This defaults to
* user . DosStream . ADS_NAME : $ DATA and can be changed by specifying
* these module parameters :
*
* streams_xattr : prefix = user .
* streams_xattr : store_stream_type = false
*
*
* TODO
* = = = =
*
* - log diagnostic if any needed VFS module is not loaded
* ( eg with lp_vfs_objects ( ) )
* - add tests
*/
static int vfs_fruit_debug_level = DBGC_VFS ;
2017-02-28 11:39:37 +03:00
static struct global_fruit_config {
bool nego_aapl ; /* client negotiated AAPL */
} global_fruit_config ;
2014-06-23 18:59:45 +04:00
# undef DBGC_CLASS
# define DBGC_CLASS vfs_fruit_debug_level
# define FRUIT_PARAM_TYPE_NAME "fruit"
# define ADOUBLE_NAME_PREFIX "._"
2015-08-24 18:42:35 +03:00
# define NETATALK_META_XATTR "org.netatalk.Metadata"
2017-02-12 11:05:50 +03:00
# define NETATALK_RSRC_XATTR "org.netatalk.ResourceFork"
2017-02-03 18:43:26 +03:00
2017-02-03 18:33:00 +03:00
# if defined(HAVE_ATTROPEN)
2015-08-24 18:42:35 +03:00
# define AFPINFO_EA_NETATALK NETATALK_META_XATTR
2017-02-03 18:43:26 +03:00
# define AFPRESOURCE_EA_NETATALK NETATALK_RSRC_XATTR
2014-06-23 18:59:45 +04:00
# else
2015-08-24 18:42:35 +03:00
# define AFPINFO_EA_NETATALK "user." NETATALK_META_XATTR
2017-02-03 18:43:26 +03:00
# define AFPRESOURCE_EA_NETATALK "user." NETATALK_RSRC_XATTR
2014-06-23 18:59:45 +04:00
# endif
enum apple_fork { APPLE_FORK_DATA , APPLE_FORK_RSRC } ;
enum fruit_rsrc { FRUIT_RSRC_STREAM , FRUIT_RSRC_ADFILE , FRUIT_RSRC_XATTR } ;
enum fruit_meta { FRUIT_META_STREAM , FRUIT_META_NETATALK } ;
enum fruit_locking { FRUIT_LOCKING_NETATALK , FRUIT_LOCKING_NONE } ;
enum fruit_encoding { FRUIT_ENC_NATIVE , FRUIT_ENC_PRIVATE } ;
struct fruit_config_data {
enum fruit_rsrc rsrc ;
enum fruit_meta meta ;
enum fruit_locking locking ;
enum fruit_encoding encoding ;
2015-11-25 11:12:55 +03:00
bool use_aapl ; /* config from smb.conf */
2015-04-22 23:29:16 +03:00
bool use_copyfile ;
2014-11-26 20:11:17 +03:00
bool readdir_attr_enabled ;
bool unix_info_enabled ;
2015-04-22 23:29:16 +03:00
bool copyfile_enabled ;
2015-05-09 09:31:24 +03:00
bool veto_appledouble ;
2016-04-11 13:17:22 +03:00
bool posix_rename ;
2017-03-23 15:08:45 +03:00
bool aapl_zero_file_id ;
2017-06-28 19:10:28 +03:00
const char * model ;
2016-11-14 21:14:44 +03:00
bool time_machine ;
2018-02-22 17:52:46 +03:00
off_t time_machine_max_size ;
2018-10-03 13:01:00 +03:00
bool wipe_intentionally_left_blank_rfork ;
2018-10-03 13:01:00 +03:00
bool delete_empty_adfiles ;
2014-11-26 20:11:17 +03:00
/*
2015-03-25 14:56:07 +03:00
* Additional options , all enabled by default ,
2014-11-26 20:11:17 +03:00
* possibly useful for analyzing performance . The associated
* operations with each of them may be expensive , so having
* the chance to disable them individually gives a chance
* tweaking the setup for the particular usecase .
*/
bool readdir_attr_rsize ;
bool readdir_attr_finder_info ;
bool readdir_attr_max_access ;
2014-06-23 18:59:45 +04:00
} ;
static const struct enum_list fruit_rsrc [ ] = {
{ FRUIT_RSRC_STREAM , " stream " } , /* pass on to vfs_streams_xattr */
{ FRUIT_RSRC_ADFILE , " file " } , /* ._ AppleDouble file */
{ FRUIT_RSRC_XATTR , " xattr " } , /* Netatalk compatible xattr (ZFS only) */
{ - 1 , NULL }
} ;
static const struct enum_list fruit_meta [ ] = {
{ FRUIT_META_STREAM , " stream " } , /* pass on to vfs_streams_xattr */
{ FRUIT_META_NETATALK , " netatalk " } , /* Netatalk compatible xattr */
{ - 1 , NULL }
} ;
static const struct enum_list fruit_locking [ ] = {
{ FRUIT_LOCKING_NETATALK , " netatalk " } , /* synchronize locks with Netatalk */
{ FRUIT_LOCKING_NONE , " none " } ,
{ - 1 , NULL }
} ;
static const struct enum_list fruit_encoding [ ] = {
{ FRUIT_ENC_NATIVE , " native " } , /* map unicode private chars to ASCII */
{ FRUIT_ENC_PRIVATE , " private " } , /* keep unicode private chars */
{ - 1 , NULL }
} ;
2017-10-10 20:13:36 +03:00
static const char * fruit_catia_maps =
" 0x01:0xf001,0x02:0xf002,0x03:0xf003,0x04:0xf004, "
" 0x05:0xf005,0x06:0xf006,0x07:0xf007,0x08:0xf008, "
" 0x09:0xf009,0x0a:0xf00a,0x0b:0xf00b,0x0c:0xf00c, "
" 0x0d:0xf00d,0x0e:0xf00e,0x0f:0xf00f,0x10:0xf010, "
" 0x11:0xf011,0x12:0xf012,0x13:0xf013,0x14:0xf014, "
" 0x15:0xf015,0x16:0xf016,0x17:0xf017,0x18:0xf018, "
" 0x19:0xf019,0x1a:0xf01a,0x1b:0xf01b,0x1c:0xf01c, "
" 0x1d:0xf01d,0x1e:0xf01e,0x1f:0xf01f, "
" 0x22:0xf020,0x2a:0xf021,0x3a:0xf022,0x3c:0xf023, "
" 0x3e:0xf024,0x3f:0xf025,0x5c:0xf026,0x7c:0xf027, "
" 0x0d:0xf00d " ;
2014-06-23 18:59:45 +04:00
/*****************************************************************************
* Defines , functions and data structures that deal with AppleDouble
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
/*
* There are two AppleDouble blobs we deal with :
*
* - ADOUBLE_META - AppleDouble blob used by Netatalk for storing
* metadata in an xattr
*
* - ADOUBLE_RSRC - AppleDouble blob used by OS X and Netatalk in
* . _ files
*/
typedef enum { ADOUBLE_META , ADOUBLE_RSRC } adouble_type_t ;
/* Version info */
# define AD_VERSION2 0x00020000
# define AD_VERSION AD_VERSION2
/*
* AppleDouble entry IDs .
*/
# define ADEID_DFORK 1
# define ADEID_RFORK 2
# define ADEID_NAME 3
# define ADEID_COMMENT 4
# define ADEID_ICONBW 5
# define ADEID_ICONCOL 6
# define ADEID_FILEI 7
# define ADEID_FILEDATESI 8
# define ADEID_FINDERI 9
# define ADEID_MACFILEI 10
# define ADEID_PRODOSFILEI 11
# define ADEID_MSDOSFILEI 12
# define ADEID_SHORTNAME 13
# define ADEID_AFPFILEI 14
# define ADEID_DID 15
/* Private Netatalk entries */
# define ADEID_PRIVDEV 16
# define ADEID_PRIVINO 17
# define ADEID_PRIVSYN 18
# define ADEID_PRIVID 19
# define ADEID_MAX (ADEID_PRIVID + 1)
/*
* These are the real ids for the private entries ,
* as stored in the adouble file
*/
# define AD_DEV 0x80444556
# define AD_INO 0x80494E4F
# define AD_SYN 0x8053594E
# define AD_ID 0x8053567E
/* Number of actually used entries */
# define ADEID_NUM_XATTR 8
# define ADEID_NUM_DOT_UND 2
# define ADEID_NUM_RSRC_XATTR 1
/* AppleDouble magic */
# define AD_APPLESINGLE_MAGIC 0x00051600
# define AD_APPLEDOUBLE_MAGIC 0x00051607
# define AD_MAGIC AD_APPLEDOUBLE_MAGIC
/* Sizes of relevant entry bits */
# define ADEDLEN_MAGIC 4
# define ADEDLEN_VERSION 4
# define ADEDLEN_FILLER 16
# define AD_FILLER_TAG "Netatalk " /* should be 16 bytes */
2018-10-05 16:12:44 +03:00
# define AD_FILLER_TAG_OSX "Mac OS X " /* should be 16 bytes */
2014-06-23 18:59:45 +04:00
# define ADEDLEN_NENTRIES 2
# define AD_HEADER_LEN (ADEDLEN_MAGIC + ADEDLEN_VERSION + \
ADEDLEN_FILLER + ADEDLEN_NENTRIES ) /* 26 */
# define AD_ENTRY_LEN_EID 4
# define AD_ENTRY_LEN_OFF 4
# define AD_ENTRY_LEN_LEN 4
# define AD_ENTRY_LEN (AD_ENTRY_LEN_EID + AD_ENTRY_LEN_OFF + AD_ENTRY_LEN_LEN)
/* Field widths */
# define ADEDLEN_NAME 255
# define ADEDLEN_COMMENT 200
# define ADEDLEN_FILEI 16
# define ADEDLEN_FINDERI 32
# define ADEDLEN_FILEDATESI 16
# define ADEDLEN_SHORTNAME 12 /* length up to 8.3 */
# define ADEDLEN_AFPFILEI 4
# define ADEDLEN_MACFILEI 4
# define ADEDLEN_PRODOSFILEI 8
# define ADEDLEN_MSDOSFILEI 2
# define ADEDLEN_DID 4
# define ADEDLEN_PRIVDEV 8
# define ADEDLEN_PRIVINO 8
# define ADEDLEN_PRIVSYN 8
# define ADEDLEN_PRIVID 4
/* Offsets */
# define ADEDOFF_MAGIC 0
# define ADEDOFF_VERSION (ADEDOFF_MAGIC + ADEDLEN_MAGIC)
# define ADEDOFF_FILLER (ADEDOFF_VERSION + ADEDLEN_VERSION)
# define ADEDOFF_NENTRIES (ADEDOFF_FILLER + ADEDLEN_FILLER)
# define ADEDOFF_FINDERI_XATTR (AD_HEADER_LEN + \
( ADEID_NUM_XATTR * AD_ENTRY_LEN ) )
# define ADEDOFF_COMMENT_XATTR (ADEDOFF_FINDERI_XATTR + ADEDLEN_FINDERI)
# define ADEDOFF_FILEDATESI_XATTR (ADEDOFF_COMMENT_XATTR + ADEDLEN_COMMENT)
# define ADEDOFF_AFPFILEI_XATTR (ADEDOFF_FILEDATESI_XATTR + \
ADEDLEN_FILEDATESI )
# define ADEDOFF_PRIVDEV_XATTR (ADEDOFF_AFPFILEI_XATTR + ADEDLEN_AFPFILEI)
# define ADEDOFF_PRIVINO_XATTR (ADEDOFF_PRIVDEV_XATTR + ADEDLEN_PRIVDEV)
# define ADEDOFF_PRIVSYN_XATTR (ADEDOFF_PRIVINO_XATTR + ADEDLEN_PRIVINO)
# define ADEDOFF_PRIVID_XATTR (ADEDOFF_PRIVSYN_XATTR + ADEDLEN_PRIVSYN)
# define ADEDOFF_FINDERI_DOT_UND (AD_HEADER_LEN + \
( ADEID_NUM_DOT_UND * AD_ENTRY_LEN ) )
# define ADEDOFF_RFORK_DOT_UND (ADEDOFF_FINDERI_DOT_UND + ADEDLEN_FINDERI)
# define AD_DATASZ_XATTR (AD_HEADER_LEN + \
( ADEID_NUM_XATTR * AD_ENTRY_LEN ) + \
ADEDLEN_FINDERI + ADEDLEN_COMMENT + \
ADEDLEN_FILEDATESI + ADEDLEN_AFPFILEI + \
ADEDLEN_PRIVDEV + ADEDLEN_PRIVINO + \
ADEDLEN_PRIVSYN + ADEDLEN_PRIVID )
# if AD_DATASZ_XATTR != 402
# error bad size for AD_DATASZ_XATTR
# endif
# define AD_DATASZ_DOT_UND (AD_HEADER_LEN + \
( ADEID_NUM_DOT_UND * AD_ENTRY_LEN ) + \
ADEDLEN_FINDERI )
# if AD_DATASZ_DOT_UND != 82
# error bad size for AD_DATASZ_DOT_UND
# endif
/*
* Sharemode locks fcntl ( ) offsets
*/
# if _FILE_OFFSET_BITS == 64 || defined(HAVE_LARGEFILE)
# define AD_FILELOCK_BASE (UINT64_C(0x7FFFFFFFFFFFFFFF) - 9)
# else
# define AD_FILELOCK_BASE (UINT32_C(0x7FFFFFFF) - 9)
# endif
# define BYTELOCK_MAX (AD_FILELOCK_BASE - 1)
# define AD_FILELOCK_OPEN_WR (AD_FILELOCK_BASE + 0)
# define AD_FILELOCK_OPEN_RD (AD_FILELOCK_BASE + 1)
# define AD_FILELOCK_RSRC_OPEN_WR (AD_FILELOCK_BASE + 2)
# define AD_FILELOCK_RSRC_OPEN_RD (AD_FILELOCK_BASE + 3)
# define AD_FILELOCK_DENY_WR (AD_FILELOCK_BASE + 4)
# define AD_FILELOCK_DENY_RD (AD_FILELOCK_BASE + 5)
# define AD_FILELOCK_RSRC_DENY_WR (AD_FILELOCK_BASE + 6)
# define AD_FILELOCK_RSRC_DENY_RD (AD_FILELOCK_BASE + 7)
# define AD_FILELOCK_OPEN_NONE (AD_FILELOCK_BASE + 8)
# define AD_FILELOCK_RSRC_OPEN_NONE (AD_FILELOCK_BASE + 9)
/* Time stuff we overload the bits a little */
# define AD_DATE_CREATE 0
# define AD_DATE_MODIFY 4
# define AD_DATE_BACKUP 8
# define AD_DATE_ACCESS 12
# define AD_DATE_MASK (AD_DATE_CREATE | AD_DATE_MODIFY | \
AD_DATE_BACKUP | AD_DATE_ACCESS )
# define AD_DATE_UNIX (1 << 10)
# define AD_DATE_START 0x80000000
# define AD_DATE_DELTA 946684800
# define AD_DATE_FROM_UNIX(x) (htonl((x) - AD_DATE_DELTA))
# define AD_DATE_TO_UNIX(x) (ntohl(x) + AD_DATE_DELTA)
2017-10-10 17:03:13 +03:00
# define AD_XATTR_HDR_MAGIC 0x41545452 /* 'ATTR' */
# define AD_XATTR_MAX_ENTRIES 1024 /* Some arbitrarily enforced limit */
# define AD_XATTR_HDR_SIZE 36
# define AD_XATTR_MAX_HDR_SIZE 65536
2014-06-23 18:59:45 +04:00
/* Accessor macros */
# define ad_getentrylen(ad,eid) ((ad)->ad_eid[(eid)].ade_len)
# define ad_getentryoff(ad,eid) ((ad)->ad_eid[(eid)].ade_off)
# define ad_setentrylen(ad,eid,len) ((ad)->ad_eid[(eid)].ade_len = (len))
# define ad_setentryoff(ad,eid,off) ((ad)->ad_eid[(eid)].ade_off = (off))
2017-10-10 17:03:13 +03:00
/*
* Both struct ad_xattr_header and struct ad_xattr_entry describe the in memory
* representation as well as the on - disk format .
*
* The ad_xattr_header follows the FinderInfo data in the FinderInfo entry if
* the length of the FinderInfo entry is larger then 32 bytes . It is then
* preceeded with 2 bytes padding .
*
* Cf : https : //opensource.apple.com/source/xnu/xnu-4570.1.46/bsd/vfs/vfs_xattr.c
*/
struct ad_xattr_header {
uint32_t adx_magic ; /* ATTR_HDR_MAGIC */
uint32_t adx_debug_tag ; /* for debugging == file id of owning file */
uint32_t adx_total_size ; /* file offset of end of attribute header + entries + data */
uint32_t adx_data_start ; /* file offset to attribute data area */
uint32_t adx_data_length ; /* length of attribute data area */
uint32_t adx_reserved [ 3 ] ;
uint16_t adx_flags ;
uint16_t adx_num_attrs ;
} ;
/* On-disk entries are aligned on 4 byte boundaries */
struct ad_xattr_entry {
uint32_t adx_offset ; /* file offset to data */
uint32_t adx_length ; /* size of attribute data */
uint16_t adx_flags ;
uint8_t adx_namelen ; /* included the NULL terminator */
char * adx_name ; /* NULL-terminated UTF-8 name */
} ;
2014-06-23 18:59:45 +04:00
struct ad_entry {
size_t ade_off ;
size_t ade_len ;
} ;
struct adouble {
vfs_handle_struct * ad_handle ;
2016-12-08 21:12:32 +03:00
int ad_fd ;
bool ad_opened ;
2014-06-23 18:59:45 +04:00
adouble_type_t ad_type ;
uint32_t ad_magic ;
uint32_t ad_version ;
2018-10-05 16:12:44 +03:00
uint8_t ad_filler [ ADEDLEN_FILLER ] ;
2014-06-23 18:59:45 +04:00
struct ad_entry ad_eid [ ADEID_MAX ] ;
char * ad_data ;
2017-10-10 17:03:13 +03:00
struct ad_xattr_header adx_header ;
struct ad_xattr_entry * adx_entries ;
2014-06-23 18:59:45 +04:00
} ;
struct ad_entry_order {
uint32_t id , offset , len ;
} ;
/* Netatalk AppleDouble metadata xattr */
static const
struct ad_entry_order entry_order_meta_xattr [ ADEID_NUM_XATTR + 1 ] = {
{ ADEID_FINDERI , ADEDOFF_FINDERI_XATTR , ADEDLEN_FINDERI } ,
{ ADEID_COMMENT , ADEDOFF_COMMENT_XATTR , 0 } ,
{ ADEID_FILEDATESI , ADEDOFF_FILEDATESI_XATTR , ADEDLEN_FILEDATESI } ,
{ ADEID_AFPFILEI , ADEDOFF_AFPFILEI_XATTR , ADEDLEN_AFPFILEI } ,
{ ADEID_PRIVDEV , ADEDOFF_PRIVDEV_XATTR , 0 } ,
{ ADEID_PRIVINO , ADEDOFF_PRIVINO_XATTR , 0 } ,
{ ADEID_PRIVSYN , ADEDOFF_PRIVSYN_XATTR , 0 } ,
{ ADEID_PRIVID , ADEDOFF_PRIVID_XATTR , 0 } ,
{ 0 , 0 , 0 }
} ;
2016-11-08 14:35:12 +03:00
/* AppleDouble resource fork file (the ones prefixed by "._") */
2014-06-23 18:59:45 +04:00
static const
struct ad_entry_order entry_order_dot_und [ ADEID_NUM_DOT_UND + 1 ] = {
{ ADEID_FINDERI , ADEDOFF_FINDERI_DOT_UND , ADEDLEN_FINDERI } ,
{ ADEID_RFORK , ADEDOFF_RFORK_DOT_UND , 0 } ,
{ 0 , 0 , 0 }
} ;
/*
2016-11-08 14:35:12 +03:00
* Fake AppleDouble entry oder for resource fork xattr . The xattr
* isn ' t an AppleDouble file , it simply contains the resource data ,
2014-06-23 18:59:45 +04:00
* but in order to be able to use some API calls like ad_getentryoff ( )
* we build a fake / helper struct adouble with this entry order struct .
*/
static const
struct ad_entry_order entry_order_rsrc_xattr [ ADEID_NUM_RSRC_XATTR + 1 ] = {
{ ADEID_RFORK , 0 , 0 } ,
{ 0 , 0 , 0 }
} ;
/* Conversion from enumerated id to on-disk AppleDouble id */
# define AD_EID_DISK(a) (set_eid[a])
static const uint32_t set_eid [ ] = {
0 , 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 , 10 , 11 , 12 , 13 , 14 , 15 ,
AD_DEV , AD_INO , AD_SYN , AD_ID
} ;
2018-10-02 17:05:28 +03:00
static char empty_resourcefork [ ] = {
0x00 , 0x00 , 0x01 , 0x00 , 0x00 , 0x00 , 0x01 , 0x00 ,
0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x1E ,
0x54 , 0x68 , 0x69 , 0x73 , 0x20 , 0x72 , 0x65 , 0x73 ,
0x6F , 0x75 , 0x72 , 0x63 , 0x65 , 0x20 , 0x66 , 0x6F ,
0x72 , 0x6B , 0x20 , 0x69 , 0x6E , 0x74 , 0x65 , 0x6E ,
0x74 , 0x69 , 0x6F , 0x6E , 0x61 , 0x6C , 0x6C , 0x79 ,
0x20 , 0x6C , 0x65 , 0x66 , 0x74 , 0x20 , 0x62 , 0x6C ,
0x61 , 0x6E , 0x6B , 0x20 , 0x20 , 0x20 , 0x00 , 0x00 ,
0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 ,
0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 ,
0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 ,
0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 ,
0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 ,
0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 ,
0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 ,
0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 ,
0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 ,
0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 ,
0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 ,
0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 ,
0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 ,
0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 ,
0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 ,
0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 ,
0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 ,
0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 ,
0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 ,
0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 ,
0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 ,
0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 ,
0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 ,
0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 ,
0x00 , 0x00 , 0x01 , 0x00 , 0x00 , 0x00 , 0x01 , 0x00 ,
0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x1E ,
0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 ,
0x00 , 0x1C , 0x00 , 0x1E , 0xFF , 0xFF
} ;
2016-12-08 21:12:32 +03:00
struct fio {
/* tcon config handle */
struct fruit_config_data * config ;
/* Denote stream type, meta or rsrc */
adouble_type_t type ;
2018-10-22 17:56:46 +03:00
/* Whether the create created the stream */
bool created ;
2018-08-22 16:22:57 +03:00
/*
* AFP_AfpInfo stream created , but not written yet , thus still a fake
* pipe fd . This is set to true in fruit_open_meta if there was no
* exisiting stream but the caller requested O_CREAT . It is later set to
* false when we get a write on the stream that then does open and
* create the stream .
*/
bool fake_fd ;
int flags ;
int mode ;
2016-12-08 21:12:32 +03:00
} ;
2014-06-23 18:59:45 +04:00
/*
* Forward declarations
*/
static struct adouble * ad_init ( TALLOC_CTX * ctx , vfs_handle_struct * handle ,
2016-12-08 21:12:32 +03:00
adouble_type_t type ) ;
2017-05-25 21:38:26 +03:00
static int ad_set ( struct adouble * ad , const struct smb_filename * smb_fname ) ;
2016-12-08 21:12:32 +03:00
static int ad_fset ( struct adouble * ad , files_struct * fsp ) ;
2017-05-25 21:38:26 +03:00
static int adouble_path ( TALLOC_CTX * ctx ,
const struct smb_filename * smb_fname__in ,
struct smb_filename * * ppsmb_fname_out ) ;
2017-11-15 18:52:16 +03:00
static AfpInfo * afpinfo_new ( TALLOC_CTX * ctx ) ;
static ssize_t afpinfo_pack ( const AfpInfo * ai , char * buf ) ;
static AfpInfo * afpinfo_unpack ( TALLOC_CTX * ctx , const void * data ) ;
2014-06-23 18:59:45 +04:00
2016-11-16 13:01:45 +03:00
/**
* Return a pointer to an AppleDouble entry
*
* Returns NULL if the entry is not present
* */
static char * ad_get_entry ( const struct adouble * ad , int eid )
{
off_t off = ad_getentryoff ( ad , eid ) ;
size_t len = ad_getentrylen ( ad , eid ) ;
if ( off = = 0 | | len = = 0 ) {
return NULL ;
}
return ad - > ad_data + off ;
}
2014-06-23 18:59:45 +04:00
/**
* Get a date
* */
static int ad_getdate ( const struct adouble * ad ,
unsigned int dateoff ,
uint32_t * date )
{
bool xlate = ( dateoff & AD_DATE_UNIX ) ;
2016-11-16 13:01:45 +03:00
char * p = NULL ;
2014-06-23 18:59:45 +04:00
dateoff & = AD_DATE_MASK ;
2016-11-16 13:01:45 +03:00
p = ad_get_entry ( ad , ADEID_FILEDATESI ) ;
if ( p = = NULL ) {
2014-06-23 18:59:45 +04:00
return - 1 ;
}
if ( dateoff > AD_DATE_ACCESS ) {
return - 1 ;
}
2016-11-16 13:01:45 +03:00
memcpy ( date , p + dateoff , sizeof ( uint32_t ) ) ;
2014-06-23 18:59:45 +04:00
if ( xlate ) {
* date = AD_DATE_TO_UNIX ( * date ) ;
}
return 0 ;
}
/**
* Set a date
* */
static int ad_setdate ( struct adouble * ad , unsigned int dateoff , uint32_t date )
{
bool xlate = ( dateoff & AD_DATE_UNIX ) ;
2016-11-16 13:01:45 +03:00
char * p = NULL ;
2014-06-23 18:59:45 +04:00
2016-11-16 13:01:45 +03:00
p = ad_get_entry ( ad , ADEID_FILEDATESI ) ;
if ( p = = NULL ) {
return - 1 ;
2014-06-23 18:59:45 +04:00
}
dateoff & = AD_DATE_MASK ;
if ( xlate ) {
date = AD_DATE_FROM_UNIX ( date ) ;
}
if ( dateoff > AD_DATE_ACCESS ) {
return - 1 ;
}
2016-11-16 13:01:45 +03:00
memcpy ( p + dateoff , & date , sizeof ( date ) ) ;
2014-06-23 18:59:45 +04:00
return 0 ;
}
/**
* Map on - disk AppleDouble id to enumerated id
* */
static uint32_t get_eid ( uint32_t eid )
{
if ( eid < = 15 ) {
return eid ;
}
switch ( eid ) {
case AD_DEV :
return ADEID_PRIVDEV ;
case AD_INO :
return ADEID_PRIVINO ;
case AD_SYN :
return ADEID_PRIVSYN ;
case AD_ID :
return ADEID_PRIVID ;
default :
break ;
}
return 0 ;
}
/**
* Pack AppleDouble structure into data buffer
* */
static bool ad_pack ( struct adouble * ad )
{
uint32_t eid ;
uint16_t nent ;
uint32_t bufsize ;
uint32_t offset = 0 ;
bufsize = talloc_get_size ( ad - > ad_data ) ;
2017-10-10 17:04:29 +03:00
if ( bufsize < AD_DATASZ_DOT_UND ) {
DBG_ERR ( " bad buffer size [0x% " PRIx32 " ] \n " , bufsize ) ;
return false ;
}
2014-06-23 18:59:45 +04:00
if ( offset + ADEDLEN_MAGIC < offset | |
offset + ADEDLEN_MAGIC > = bufsize ) {
return false ;
}
RSIVAL ( ad - > ad_data , offset , ad - > ad_magic ) ;
offset + = ADEDLEN_MAGIC ;
if ( offset + ADEDLEN_VERSION < offset | |
offset + ADEDLEN_VERSION > = bufsize ) {
return false ;
}
RSIVAL ( ad - > ad_data , offset , ad - > ad_version ) ;
offset + = ADEDLEN_VERSION ;
if ( offset + ADEDLEN_FILLER < offset | |
offset + ADEDLEN_FILLER > = bufsize ) {
return false ;
}
if ( ad - > ad_type = = ADOUBLE_RSRC ) {
memcpy ( ad - > ad_data + offset , AD_FILLER_TAG , ADEDLEN_FILLER ) ;
}
offset + = ADEDLEN_FILLER ;
if ( offset + ADEDLEN_NENTRIES < offset | |
offset + ADEDLEN_NENTRIES > = bufsize ) {
return false ;
}
offset + = ADEDLEN_NENTRIES ;
for ( eid = 0 , nent = 0 ; eid < ADEID_MAX ; eid + + ) {
2014-12-16 11:38:54 +03:00
if ( ad - > ad_eid [ eid ] . ade_off = = 0 ) {
2014-06-23 18:59:45 +04:00
/*
* ade_off is also used as indicator whether a
* specific entry is used or not
*/
continue ;
}
if ( offset + AD_ENTRY_LEN_EID < offset | |
offset + AD_ENTRY_LEN_EID > = bufsize ) {
return false ;
}
RSIVAL ( ad - > ad_data , offset , AD_EID_DISK ( eid ) ) ;
offset + = AD_ENTRY_LEN_EID ;
if ( offset + AD_ENTRY_LEN_OFF < offset | |
offset + AD_ENTRY_LEN_OFF > = bufsize ) {
return false ;
}
RSIVAL ( ad - > ad_data , offset , ad - > ad_eid [ eid ] . ade_off ) ;
offset + = AD_ENTRY_LEN_OFF ;
if ( offset + AD_ENTRY_LEN_LEN < offset | |
offset + AD_ENTRY_LEN_LEN > = bufsize ) {
return false ;
}
RSIVAL ( ad - > ad_data , offset , ad - > ad_eid [ eid ] . ade_len ) ;
offset + = AD_ENTRY_LEN_LEN ;
nent + + ;
}
if ( ADEDOFF_NENTRIES + 2 > = bufsize ) {
return false ;
}
RSSVAL ( ad - > ad_data , ADEDOFF_NENTRIES , nent ) ;
2015-09-27 13:11:31 +03:00
return true ;
2014-06-23 18:59:45 +04:00
}
2017-10-10 17:06:33 +03:00
static bool ad_unpack_xattrs ( struct adouble * ad )
{
struct ad_xattr_header * h = & ad - > adx_header ;
const char * p = ad - > ad_data ;
uint32_t hoff ;
uint32_t i ;
if ( ad_getentrylen ( ad , ADEID_FINDERI ) < = ADEDLEN_FINDERI ) {
return true ;
}
/* 2 bytes padding */
hoff = ad_getentryoff ( ad , ADEID_FINDERI ) + ADEDLEN_FINDERI + 2 ;
h - > adx_magic = RIVAL ( p , hoff + 0 ) ;
h - > adx_debug_tag = RIVAL ( p , hoff + 4 ) ; /* Not used -> not checked */
h - > adx_total_size = RIVAL ( p , hoff + 8 ) ;
h - > adx_data_start = RIVAL ( p , hoff + 12 ) ;
h - > adx_data_length = RIVAL ( p , hoff + 16 ) ;
h - > adx_flags = RSVAL ( p , hoff + 32 ) ; /* Not used -> not checked */
h - > adx_num_attrs = RSVAL ( p , hoff + 34 ) ;
if ( h - > adx_magic ! = AD_XATTR_HDR_MAGIC ) {
DBG_ERR ( " Bad magic: 0x% " PRIx32 " \n " , h - > adx_magic ) ;
return false ;
}
if ( h - > adx_total_size > ad_getentryoff ( ad , ADEID_RFORK ) ) {
DBG_ERR ( " Bad total size: 0x% " PRIx32 " \n " , h - > adx_total_size ) ;
return false ;
}
if ( h - > adx_total_size > AD_XATTR_MAX_HDR_SIZE ) {
DBG_ERR ( " Bad total size: 0x% " PRIx32 " \n " , h - > adx_total_size ) ;
return false ;
}
if ( h - > adx_data_start < ( hoff + AD_XATTR_HDR_SIZE ) ) {
DBG_ERR ( " Bad start: 0x% " PRIx32 " \n " , h - > adx_data_start ) ;
return false ;
}
if ( ( h - > adx_data_start + h - > adx_data_length ) < h - > adx_data_start ) {
DBG_ERR ( " Bad length: % " PRIu32 " \n " , h - > adx_data_length ) ;
return false ;
}
if ( ( h - > adx_data_start + h - > adx_data_length ) >
ad - > adx_header . adx_total_size )
{
DBG_ERR ( " Bad length: % " PRIu32 " \n " , h - > adx_data_length ) ;
return false ;
}
if ( h - > adx_num_attrs > AD_XATTR_MAX_ENTRIES ) {
DBG_ERR ( " Bad num xattrs: % " PRIu16 " \n " , h - > adx_num_attrs ) ;
return false ;
}
if ( h - > adx_num_attrs = = 0 ) {
return true ;
}
ad - > adx_entries = talloc_zero_array (
ad , struct ad_xattr_entry , h - > adx_num_attrs ) ;
if ( ad - > adx_entries = = NULL ) {
return false ;
}
hoff + = AD_XATTR_HDR_SIZE ;
for ( i = 0 ; i < h - > adx_num_attrs ; i + + ) {
struct ad_xattr_entry * e = & ad - > adx_entries [ i ] ;
hoff = ( hoff + 3 ) & ~ 3 ;
e - > adx_offset = RIVAL ( p , hoff + 0 ) ;
e - > adx_length = RIVAL ( p , hoff + 4 ) ;
e - > adx_flags = RSVAL ( p , hoff + 8 ) ;
e - > adx_namelen = * ( p + hoff + 10 ) ;
if ( e - > adx_offset > = ad - > adx_header . adx_total_size ) {
DBG_ERR ( " Bad adx_offset: % " PRIx32 " \n " ,
e - > adx_offset ) ;
return false ;
}
if ( ( e - > adx_offset + e - > adx_length ) < e - > adx_offset ) {
DBG_ERR ( " Bad adx_length: % " PRIx32 " \n " ,
e - > adx_length ) ;
return false ;
}
if ( ( e - > adx_offset + e - > adx_length ) >
ad - > adx_header . adx_total_size )
{
DBG_ERR ( " Bad adx_length: % " PRIx32 " \n " ,
e - > adx_length ) ;
return false ;
}
if ( e - > adx_namelen = = 0 ) {
DBG_ERR ( " Bad adx_namelen: % " PRIx32 " \n " ,
e - > adx_namelen ) ;
return false ;
}
if ( ( hoff + 11 + e - > adx_namelen ) < hoff + 11 ) {
DBG_ERR ( " Bad adx_namelen: % " PRIx32 " \n " ,
e - > adx_namelen ) ;
return false ;
}
if ( ( hoff + 11 + e - > adx_namelen ) >
ad - > adx_header . adx_data_start )
{
DBG_ERR ( " Bad adx_namelen: % " PRIx32 " \n " ,
e - > adx_namelen ) ;
return false ;
}
e - > adx_name = talloc_strndup ( ad - > adx_entries ,
p + hoff + 11 ,
e - > adx_namelen ) ;
if ( e - > adx_name = = NULL ) {
return false ;
}
DBG_DEBUG ( " xattr [%s] offset [0x%x] size [0x%x] \n " ,
e - > adx_name , e - > adx_offset , e - > adx_length ) ;
dump_data ( 10 , ( uint8_t * ) ( ad - > ad_data + e - > adx_offset ) ,
e - > adx_length ) ;
hoff + = 11 + e - > adx_namelen ;
}
return true ;
}
2014-06-23 18:59:45 +04:00
/**
* Unpack an AppleDouble blob into a struct adoble
* */
2016-05-19 19:42:04 +03:00
static bool ad_unpack ( struct adouble * ad , const size_t nentries ,
size_t filesize )
2014-06-23 18:59:45 +04:00
{
size_t bufsize = talloc_get_size ( ad - > ad_data ) ;
2016-05-19 19:42:04 +03:00
size_t adentries , i ;
2014-06-23 18:59:45 +04:00
uint32_t eid , len , off ;
2017-10-10 17:06:33 +03:00
bool ok ;
2014-06-23 18:59:45 +04:00
/*
* The size of the buffer ad - > ad_data is checked when read , so
* we wouldn ' t have to check our own offsets , a few extra
* checks won ' t hurt though . We have to check the offsets we
* read from the buffer anyway .
*/
if ( bufsize < ( AD_HEADER_LEN + ( AD_ENTRY_LEN * nentries ) ) ) {
DEBUG ( 1 , ( " bad size \n " ) ) ;
return false ;
}
ad - > ad_magic = RIVAL ( ad - > ad_data , 0 ) ;
ad - > ad_version = RIVAL ( ad - > ad_data , ADEDOFF_VERSION ) ;
if ( ( ad - > ad_magic ! = AD_MAGIC ) | | ( ad - > ad_version ! = AD_VERSION ) ) {
DEBUG ( 1 , ( " wrong magic or version \n " ) ) ;
return false ;
}
2018-10-05 16:12:44 +03:00
memcpy ( ad - > ad_filler , ad - > ad_data + ADEDOFF_FILLER , ADEDLEN_FILLER ) ;
2014-06-23 18:59:45 +04:00
adentries = RSVAL ( ad - > ad_data , ADEDOFF_NENTRIES ) ;
if ( adentries ! = nentries ) {
2016-05-19 19:42:04 +03:00
DEBUG ( 1 , ( " invalid number of entries: %zu \n " ,
adentries ) ) ;
2014-06-23 18:59:45 +04:00
return false ;
}
/* now, read in the entry bits */
for ( i = 0 ; i < adentries ; i + + ) {
eid = RIVAL ( ad - > ad_data , AD_HEADER_LEN + ( i * AD_ENTRY_LEN ) ) ;
eid = get_eid ( eid ) ;
off = RIVAL ( ad - > ad_data , AD_HEADER_LEN + ( i * AD_ENTRY_LEN ) + 4 ) ;
len = RIVAL ( ad - > ad_data , AD_HEADER_LEN + ( i * AD_ENTRY_LEN ) + 8 ) ;
2017-08-25 15:45:29 +03:00
if ( ! eid | | eid > = ADEID_MAX ) {
2014-06-23 18:59:45 +04:00
DEBUG ( 1 , ( " bogus eid %d \n " , eid ) ) ;
return false ;
}
2015-03-02 20:15:06 +03:00
/*
* All entries other than the resource fork are
* expected to be read into the ad_data buffer , so
* ensure the specified offset is within that bound
*/
2014-08-30 12:24:05 +04:00
if ( ( off > bufsize ) & & ( eid ! = ADEID_RFORK ) ) {
2014-06-23 18:59:45 +04:00
DEBUG ( 1 , ( " bogus eid %d: off: % " PRIu32 " , len: % " PRIu32 " \n " ,
eid , off , len ) ) ;
return false ;
}
2015-03-02 20:15:06 +03:00
/*
* All entries besides FinderInfo and resource fork
* must fit into the buffer . FinderInfo is special as
* it may be larger then the default 32 bytes ( if it
* contains marshalled xattrs ) , but we will fixup that
* in ad_convert ( ) . And the resource fork is never
* accessed directly by the ad_data buf ( also see
* comment above ) anyway .
*/
2014-08-30 12:24:05 +04:00
if ( ( eid ! = ADEID_RFORK ) & &
( eid ! = ADEID_FINDERI ) & &
( ( off + len ) > bufsize ) ) {
2014-06-23 18:59:45 +04:00
DEBUG ( 1 , ( " bogus eid %d: off: % " PRIu32 " , len: % " PRIu32 " \n " ,
eid , off , len ) ) ;
return false ;
}
2015-03-02 20:15:06 +03:00
/*
* That would be obviously broken
*/
if ( off > filesize ) {
DEBUG ( 1 , ( " bogus eid %d: off: % " PRIu32 " , len: % " PRIu32 " \n " ,
eid , off , len ) ) ;
return false ;
}
/*
* Check for any entry that has its end beyond the
* filesize .
*/
if ( off + len < off ) {
DEBUG ( 1 , ( " offset wrap in eid %d: off: % " PRIu32
" , len: % " PRIu32 " \n " ,
eid , off , len ) ) ;
return false ;
}
if ( off + len > filesize ) {
/*
* If this is the resource fork entry , we fix
* up the length , for any other entry we bail
* out .
*/
if ( eid ! = ADEID_RFORK ) {
DEBUG ( 1 , ( " bogus eid %d: off: % " PRIu32
" , len: % " PRIu32 " \n " ,
eid , off , len ) ) ;
return false ;
}
/*
* Fixup the resource fork entry by limiting
* the size to entryoffset - filesize .
*/
len = filesize - off ;
DEBUG ( 1 , ( " Limiting ADEID_RFORK: off: % " PRIu32
" , len: % " PRIu32 " \n " , off , len ) ) ;
}
2014-06-23 18:59:45 +04:00
ad - > ad_eid [ eid ] . ade_off = off ;
ad - > ad_eid [ eid ] . ade_len = len ;
}
2017-10-10 17:06:33 +03:00
ok = ad_unpack_xattrs ( ad ) ;
if ( ! ok ) {
return false ;
}
2014-06-23 18:59:45 +04:00
return true ;
}
2018-10-05 23:05:43 +03:00
static bool ad_convert_move_reso ( struct adouble * ad ,
const struct smb_filename * smb_fname )
{
char * map = MAP_FAILED ;
size_t maplen ;
ssize_t len ;
int rc ;
bool ok ;
if ( ad_getentrylen ( ad , ADEID_RFORK ) = = 0 ) {
return true ;
}
maplen = ad_getentryoff ( ad , ADEID_RFORK ) +
ad_getentrylen ( ad , ADEID_RFORK ) ;
/* FIXME: direct use of mmap(), vfs_aio_fork does it too */
map = mmap ( NULL , maplen , PROT_READ | PROT_WRITE , MAP_SHARED ,
ad - > ad_fd , 0 ) ;
if ( map = = MAP_FAILED ) {
DBG_ERR ( " mmap AppleDouble: %s \n " , strerror ( errno ) ) ;
return false ;
}
memmove ( map + ADEDOFF_RFORK_DOT_UND ,
map + ad_getentryoff ( ad , ADEID_RFORK ) ,
ad_getentrylen ( ad , ADEID_RFORK ) ) ;
rc = munmap ( map , maplen ) ;
if ( rc ! = 0 ) {
DBG_ERR ( " munmap failed: %s \n " , strerror ( errno ) ) ;
return false ;
}
ad_setentryoff ( ad , ADEID_RFORK , ADEDOFF_RFORK_DOT_UND ) ;
ok = ad_pack ( ad ) ;
if ( ! ok ) {
DBG_WARNING ( " ad_pack [%s] failed \n " , smb_fname - > base_name ) ;
return false ;
}
len = sys_pwrite ( ad - > ad_fd , ad - > ad_data , AD_DATASZ_DOT_UND , 0 ) ;
if ( len ! = AD_DATASZ_DOT_UND ) {
DBG_ERR ( " %s: bad size: %zd \n " , smb_fname - > base_name , len ) ;
return false ;
}
return true ;
}
2017-10-11 13:58:59 +03:00
static bool ad_convert_xattr ( struct adouble * ad ,
2018-10-08 19:43:51 +03:00
const struct smb_filename * smb_fname ,
bool * converted_xattr )
2017-10-11 13:58:59 +03:00
{
static struct char_mappings * * string_replace_cmaps = NULL ;
2018-10-05 17:59:18 +03:00
char * map = MAP_FAILED ;
size_t maplen ;
2017-10-11 13:58:59 +03:00
uint16_t i ;
2018-10-05 18:07:45 +03:00
ssize_t len ;
2017-10-11 13:58:59 +03:00
int saved_errno = 0 ;
NTSTATUS status ;
2018-10-05 17:59:18 +03:00
int rc ;
bool ok ;
2017-10-11 13:58:59 +03:00
2018-10-08 19:43:51 +03:00
* converted_xattr = false ;
2018-10-09 11:15:37 +03:00
if ( ad_getentrylen ( ad , ADEID_FINDERI ) = = ADEDLEN_FINDERI ) {
return true ;
}
2017-10-11 13:58:59 +03:00
if ( string_replace_cmaps = = NULL ) {
const char * * mappings = NULL ;
mappings = str_list_make_v3_const (
talloc_tos ( ) , fruit_catia_maps , NULL ) ;
if ( mappings = = NULL ) {
return false ;
}
string_replace_cmaps = string_replace_init_map ( mappings ) ;
TALLOC_FREE ( mappings ) ;
}
2018-10-05 17:59:18 +03:00
maplen = ad_getentryoff ( ad , ADEID_RFORK ) +
ad_getentrylen ( ad , ADEID_RFORK ) ;
/* FIXME: direct use of mmap(), vfs_aio_fork does it too */
map = mmap ( NULL , maplen , PROT_READ | PROT_WRITE , MAP_SHARED ,
ad - > ad_fd , 0 ) ;
if ( map = = MAP_FAILED ) {
DBG_ERR ( " mmap AppleDouble: %s \n " , strerror ( errno ) ) ;
return false ;
}
2017-10-11 13:58:59 +03:00
for ( i = 0 ; i < ad - > adx_header . adx_num_attrs ; i + + ) {
struct ad_xattr_entry * e = & ad - > adx_entries [ i ] ;
char * mapped_name = NULL ;
char * tmp = NULL ;
struct smb_filename * stream_name = NULL ;
files_struct * fsp = NULL ;
ssize_t nwritten ;
status = string_replace_allocate ( ad - > ad_handle - > conn ,
e - > adx_name ,
string_replace_cmaps ,
talloc_tos ( ) ,
& mapped_name ,
vfs_translate_to_windows ) ;
if ( ! NT_STATUS_IS_OK ( status ) & &
! NT_STATUS_EQUAL ( status , NT_STATUS_NONE_MAPPED ) )
{
DBG_ERR ( " string_replace_allocate failed \n " ) ;
2018-10-05 17:59:18 +03:00
ok = false ;
goto fail ;
2017-10-11 13:58:59 +03:00
}
tmp = mapped_name ;
mapped_name = talloc_asprintf ( talloc_tos ( ) , " :%s " , tmp ) ;
TALLOC_FREE ( tmp ) ;
if ( mapped_name = = NULL ) {
2018-10-05 17:59:18 +03:00
ok = false ;
goto fail ;
2017-10-11 13:58:59 +03:00
}
stream_name = synthetic_smb_fname ( talloc_tos ( ) ,
smb_fname - > base_name ,
mapped_name ,
NULL ,
smb_fname - > flags ) ;
TALLOC_FREE ( mapped_name ) ;
if ( stream_name = = NULL ) {
DBG_ERR ( " synthetic_smb_fname failed \n " ) ;
2018-10-05 17:59:18 +03:00
ok = false ;
goto fail ;
2017-10-11 13:58:59 +03:00
}
DBG_DEBUG ( " stream_name: %s \n " , smb_fname_str_dbg ( stream_name ) ) ;
status = SMB_VFS_CREATE_FILE (
ad - > ad_handle - > conn , /* conn */
NULL , /* req */
0 , /* root_dir_fid */
stream_name , /* fname */
FILE_GENERIC_WRITE , /* access_mask */
FILE_SHARE_READ | FILE_SHARE_WRITE , /* share_access */
FILE_OPEN_IF , /* create_disposition */
0 , /* create_options */
0 , /* file_attributes */
INTERNAL_OPEN_ONLY , /* oplock_request */
NULL , /* lease */
0 , /* allocation_size */
0 , /* private_flags */
NULL , /* sd */
NULL , /* ea_list */
& fsp , /* result */
NULL , /* psbuf */
NULL , NULL ) ; /* create context */
TALLOC_FREE ( stream_name ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
DBG_ERR ( " SMB_VFS_CREATE_FILE failed \n " ) ;
2018-10-05 17:59:18 +03:00
ok = false ;
goto fail ;
2017-10-11 13:58:59 +03:00
}
nwritten = SMB_VFS_PWRITE ( fsp ,
map + e - > adx_offset ,
e - > adx_length ,
0 ) ;
if ( nwritten = = - 1 ) {
DBG_ERR ( " SMB_VFS_PWRITE failed \n " ) ;
saved_errno = errno ;
close_file ( NULL , fsp , ERROR_CLOSE ) ;
errno = saved_errno ;
2018-10-05 17:59:18 +03:00
ok = false ;
goto fail ;
2017-10-11 13:58:59 +03:00
}
status = close_file ( NULL , fsp , NORMAL_CLOSE ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
2018-10-05 17:59:18 +03:00
ok = false ;
goto fail ;
2017-10-11 13:58:59 +03:00
}
fsp = NULL ;
}
2018-10-02 15:51:05 +03:00
ad_setentrylen ( ad , ADEID_FINDERI , ADEDLEN_FINDERI ) ;
2018-10-05 18:07:45 +03:00
ok = ad_pack ( ad ) ;
if ( ! ok ) {
DBG_WARNING ( " ad_pack [%s] failed \n " , smb_fname - > base_name ) ;
goto fail ;
}
len = sys_pwrite ( ad - > ad_fd , ad - > ad_data , AD_DATASZ_DOT_UND , 0 ) ;
if ( len ! = AD_DATASZ_DOT_UND ) {
DBG_ERR ( " %s: bad size: %zd \n " , smb_fname - > base_name , len ) ;
ok = false ;
goto fail ;
}
2018-10-05 23:05:43 +03:00
ok = ad_convert_move_reso ( ad , smb_fname ) ;
if ( ! ok ) {
goto fail ;
}
2018-10-08 19:43:51 +03:00
* converted_xattr = true ;
2018-10-05 17:59:18 +03:00
ok = true ;
fail :
rc = munmap ( map , maplen ) ;
if ( rc ! = 0 ) {
DBG_ERR ( " munmap failed: %s \n " , strerror ( errno ) ) ;
return false ;
}
return ok ;
2017-10-11 13:58:59 +03:00
}
2018-10-05 17:25:27 +03:00
static bool ad_convert_finderinfo ( struct adouble * ad ,
const struct smb_filename * smb_fname )
{
char * p_ad = NULL ;
AfpInfo * ai = NULL ;
DATA_BLOB aiblob ;
struct smb_filename * stream_name = NULL ;
files_struct * fsp = NULL ;
size_t size ;
ssize_t nwritten ;
NTSTATUS status ;
int saved_errno = 0 ;
2018-10-08 13:51:37 +03:00
int cmp ;
cmp = memcmp ( ad - > ad_filler , AD_FILLER_TAG_OSX , ADEDLEN_FILLER ) ;
if ( cmp ! = 0 ) {
return true ;
}
2018-10-05 17:25:27 +03:00
p_ad = ad_get_entry ( ad , ADEID_FINDERI ) ;
if ( p_ad = = NULL ) {
return false ;
}
ai = afpinfo_new ( talloc_tos ( ) ) ;
if ( ai = = NULL ) {
return false ;
}
memcpy ( ai - > afpi_FinderInfo , p_ad , ADEDLEN_FINDERI ) ;
aiblob = data_blob_talloc ( talloc_tos ( ) , NULL , AFP_INFO_SIZE ) ;
if ( aiblob . data = = NULL ) {
TALLOC_FREE ( ai ) ;
return false ;
}
size = afpinfo_pack ( ai , ( char * ) aiblob . data ) ;
TALLOC_FREE ( ai ) ;
if ( size ! = AFP_INFO_SIZE ) {
return false ;
}
stream_name = synthetic_smb_fname ( talloc_tos ( ) ,
smb_fname - > base_name ,
AFPINFO_STREAM ,
NULL ,
smb_fname - > flags ) ;
if ( stream_name = = NULL ) {
data_blob_free ( & aiblob ) ;
DBG_ERR ( " synthetic_smb_fname failed \n " ) ;
return false ;
}
DBG_DEBUG ( " stream_name: %s \n " , smb_fname_str_dbg ( stream_name ) ) ;
status = SMB_VFS_CREATE_FILE (
ad - > ad_handle - > conn , /* conn */
NULL , /* req */
0 , /* root_dir_fid */
stream_name , /* fname */
FILE_GENERIC_WRITE , /* access_mask */
FILE_SHARE_READ | FILE_SHARE_WRITE , /* share_access */
FILE_OPEN_IF , /* create_disposition */
0 , /* create_options */
0 , /* file_attributes */
INTERNAL_OPEN_ONLY , /* oplock_request */
NULL , /* lease */
0 , /* allocation_size */
0 , /* private_flags */
NULL , /* sd */
NULL , /* ea_list */
& fsp , /* result */
NULL , /* psbuf */
NULL , NULL ) ; /* create context */
TALLOC_FREE ( stream_name ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
DBG_ERR ( " SMB_VFS_CREATE_FILE failed \n " ) ;
return false ;
}
nwritten = SMB_VFS_PWRITE ( fsp ,
aiblob . data ,
aiblob . length ,
0 ) ;
if ( nwritten = = - 1 ) {
DBG_ERR ( " SMB_VFS_PWRITE failed \n " ) ;
saved_errno = errno ;
close_file ( NULL , fsp , ERROR_CLOSE ) ;
errno = saved_errno ;
return false ;
}
status = close_file ( NULL , fsp , NORMAL_CLOSE ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
return false ;
}
fsp = NULL ;
return true ;
}
2018-10-05 20:13:16 +03:00
static bool ad_convert_truncate ( struct adouble * ad ,
const struct smb_filename * smb_fname )
{
int rc ;
/*
* FIXME : direct ftruncate ( ) , but we don ' t have a fsp for the
* VFS call
*/
2018-10-05 20:15:04 +03:00
rc = ftruncate ( ad - > ad_fd , ADEDOFF_RFORK_DOT_UND +
ad_getentrylen ( ad , ADEID_RFORK ) ) ;
2018-10-05 20:13:16 +03:00
if ( rc ! = 0 ) {
return false ;
}
return true ;
}
2018-10-02 17:05:28 +03:00
static bool ad_convert_blank_rfork ( struct adouble * ad ,
bool * blank )
{
struct fruit_config_data * config = NULL ;
uint8_t * map = MAP_FAILED ;
size_t maplen ;
int cmp ;
ssize_t len ;
int rc ;
bool ok ;
* blank = false ;
SMB_VFS_HANDLE_GET_DATA ( ad - > ad_handle , config ,
struct fruit_config_data , return false ) ;
if ( ! config - > wipe_intentionally_left_blank_rfork ) {
return true ;
}
if ( ad_getentrylen ( ad , ADEID_RFORK ) ! = sizeof ( empty_resourcefork ) ) {
return true ;
}
maplen = ad_getentryoff ( ad , ADEID_RFORK ) +
ad_getentrylen ( ad , ADEID_RFORK ) ;
/* FIXME: direct use of mmap(), vfs_aio_fork does it too */
map = mmap ( NULL , maplen , PROT_READ | PROT_WRITE , MAP_SHARED ,
ad - > ad_fd , 0 ) ;
if ( map = = MAP_FAILED ) {
DBG_ERR ( " mmap AppleDouble: %s \n " , strerror ( errno ) ) ;
return false ;
}
cmp = memcmp ( map + ADEDOFF_RFORK_DOT_UND ,
empty_resourcefork ,
sizeof ( empty_resourcefork ) ) ;
rc = munmap ( map , maplen ) ;
if ( rc ! = 0 ) {
DBG_ERR ( " munmap failed: %s \n " , strerror ( errno ) ) ;
return false ;
}
if ( cmp ! = 0 ) {
return true ;
}
ad_setentrylen ( ad , ADEID_RFORK , 0 ) ;
ok = ad_pack ( ad ) ;
if ( ! ok ) {
return false ;
}
len = sys_pwrite ( ad - > ad_fd , ad - > ad_data , AD_DATASZ_DOT_UND , 0 ) ;
if ( len ! = AD_DATASZ_DOT_UND ) {
return false ;
}
* blank = true ;
return true ;
}
2018-10-09 15:54:31 +03:00
static bool ad_convert_delete_adfile ( struct adouble * ad ,
const struct smb_filename * smb_fname )
{
struct fruit_config_data * config = NULL ;
struct smb_filename * ad_name = NULL ;
int rc ;
if ( ad_getentrylen ( ad , ADEID_RFORK ) > 0 ) {
return true ;
}
SMB_VFS_HANDLE_GET_DATA ( ad - > ad_handle , config ,
struct fruit_config_data , return false ) ;
if ( ! config - > delete_empty_adfiles ) {
return true ;
}
rc = adouble_path ( talloc_tos ( ) , smb_fname , & ad_name ) ;
if ( rc ! = 0 ) {
return false ;
}
rc = SMB_VFS_NEXT_UNLINK ( ad - > ad_handle , ad_name ) ;
if ( rc ! = 0 ) {
DBG_ERR ( " Unlinking [%s] failed: %s \n " ,
smb_fname_str_dbg ( ad_name ) , strerror ( errno ) ) ;
TALLOC_FREE ( ad_name ) ;
return false ;
}
DBG_WARNING ( " Unlinked [%s] after conversion \n " , smb_fname_str_dbg ( ad_name ) ) ;
TALLOC_FREE ( ad_name ) ;
return true ;
}
2014-06-23 18:59:45 +04:00
/**
* Convert from Apple ' s . _ file to Netatalk
*
* Apple ' s AppleDouble may contain a FinderInfo entry longer then 32
2018-09-11 15:05:43 +03:00
* bytes containing packed xattrs .
2014-06-23 18:59:45 +04:00
*
2017-02-17 22:46:28 +03:00
* @ return - 1 in case an error occurred , 0 if no conversion was done , 1
2014-06-23 18:59:45 +04:00
* otherwise
* */
2017-10-10 17:15:49 +03:00
static int ad_convert ( struct adouble * ad ,
2018-10-04 09:51:28 +03:00
const struct smb_filename * smb_fname )
2014-06-23 18:59:45 +04:00
{
2017-10-11 13:58:59 +03:00
bool ok ;
2018-10-08 19:43:51 +03:00
bool converted_xattr = false ;
2018-10-02 17:05:28 +03:00
bool blank ;
2014-06-23 18:59:45 +04:00
2018-10-08 19:43:51 +03:00
ok = ad_convert_xattr ( ad , smb_fname , & converted_xattr ) ;
2017-10-11 13:58:59 +03:00
if ( ! ok ) {
return - 1 ;
}
2018-10-02 17:05:28 +03:00
ok = ad_convert_blank_rfork ( ad , & blank ) ;
if ( ! ok ) {
return - 1 ;
}
if ( converted_xattr | | blank ) {
2018-10-08 19:47:32 +03:00
ok = ad_convert_truncate ( ad , smb_fname ) ;
if ( ! ok ) {
return - 1 ;
}
2018-10-04 09:23:59 +03:00
}
2018-10-05 17:25:27 +03:00
ok = ad_convert_finderinfo ( ad , smb_fname ) ;
if ( ! ok ) {
DBG_ERR ( " Failed to convert [%s] \n " ,
smb_fname_str_dbg ( smb_fname ) ) ;
return - 1 ;
}
2018-10-09 15:54:31 +03:00
ok = ad_convert_delete_adfile ( ad , smb_fname ) ;
if ( ! ok ) {
return - 1 ;
}
2018-10-04 09:23:59 +03:00
return 0 ;
2014-06-23 18:59:45 +04:00
}
/**
* Read and parse Netatalk AppleDouble metadata xattr
* */
2017-05-25 21:38:26 +03:00
static ssize_t ad_read_meta ( struct adouble * ad ,
const struct smb_filename * smb_fname )
2014-06-23 18:59:45 +04:00
{
int rc = 0 ;
ssize_t ealen ;
bool ok ;
2017-05-25 21:38:26 +03:00
DEBUG ( 10 , ( " reading meta xattr for %s \n " , smb_fname - > base_name ) ) ;
2014-06-23 18:59:45 +04:00
2017-05-26 02:42:04 +03:00
ealen = SMB_VFS_GETXATTR ( ad - > ad_handle - > conn , smb_fname ,
2014-06-23 18:59:45 +04:00
AFPINFO_EA_NETATALK , ad - > ad_data ,
AD_DATASZ_XATTR ) ;
if ( ealen = = - 1 ) {
switch ( errno ) {
case ENOATTR :
case ENOENT :
if ( errno = = ENOATTR ) {
errno = ENOENT ;
}
rc = - 1 ;
goto exit ;
default :
DEBUG ( 2 , ( " error reading meta xattr: %s \n " ,
strerror ( errno ) ) ) ;
rc = - 1 ;
goto exit ;
}
}
if ( ealen ! = AD_DATASZ_XATTR ) {
DEBUG ( 2 , ( " bad size %zd \n " , ealen ) ) ;
errno = EINVAL ;
rc = - 1 ;
goto exit ;
}
/* Now parse entries */
2015-03-02 20:15:06 +03:00
ok = ad_unpack ( ad , ADEID_NUM_XATTR , AD_DATASZ_XATTR ) ;
2014-06-23 18:59:45 +04:00
if ( ! ok ) {
DEBUG ( 2 , ( " invalid AppleDouble metadata xattr \n " ) ) ;
errno = EINVAL ;
rc = - 1 ;
goto exit ;
}
if ( ! ad_getentryoff ( ad , ADEID_FINDERI )
| | ! ad_getentryoff ( ad , ADEID_COMMENT )
| | ! ad_getentryoff ( ad , ADEID_FILEDATESI )
| | ! ad_getentryoff ( ad , ADEID_AFPFILEI )
| | ! ad_getentryoff ( ad , ADEID_PRIVDEV )
| | ! ad_getentryoff ( ad , ADEID_PRIVINO )
| | ! ad_getentryoff ( ad , ADEID_PRIVSYN )
| | ! ad_getentryoff ( ad , ADEID_PRIVID ) ) {
DEBUG ( 2 , ( " invalid AppleDouble metadata xattr \n " ) ) ;
errno = EINVAL ;
rc = - 1 ;
goto exit ;
}
exit :
2017-05-25 21:38:26 +03:00
DEBUG ( 10 , ( " reading meta xattr for %s, rc: %d \n " ,
smb_fname - > base_name , rc ) ) ;
2014-06-23 18:59:45 +04:00
if ( rc ! = 0 ) {
ealen = - 1 ;
if ( errno = = EINVAL ) {
become_root ( ) ;
2017-05-25 21:38:26 +03:00
removexattr ( smb_fname - > base_name , AFPINFO_EA_NETATALK ) ;
2014-06-23 18:59:45 +04:00
unbecome_root ( ) ;
errno = ENOENT ;
}
}
return ealen ;
}
2017-05-25 21:38:26 +03:00
static int ad_open_rsrc_xattr ( const struct smb_filename * smb_fname ,
int flags ,
mode_t mode )
2016-12-08 21:12:32 +03:00
{
# ifdef HAVE_ATTROPEN
/* FIXME: direct Solaris xattr syscall */
2017-05-25 21:38:26 +03:00
return attropen ( smb_fname - > base_name ,
AFPRESOURCE_EA_NETATALK , flags , mode ) ;
2016-12-08 21:12:32 +03:00
# else
errno = ENOSYS ;
return - 1 ;
# endif
}
2017-05-25 21:38:26 +03:00
static int ad_open_rsrc_adouble ( const struct smb_filename * smb_fname ,
int flags ,
mode_t mode )
2016-12-08 21:12:32 +03:00
{
int ret ;
int fd ;
2017-05-25 21:38:26 +03:00
struct smb_filename * adp_smb_fname = NULL ;
2016-12-08 21:12:32 +03:00
2017-05-25 21:38:26 +03:00
ret = adouble_path ( talloc_tos ( ) , smb_fname , & adp_smb_fname ) ;
2016-12-08 21:12:32 +03:00
if ( ret ! = 0 ) {
return - 1 ;
}
2017-05-25 21:38:26 +03:00
fd = open ( adp_smb_fname - > base_name , flags , mode ) ;
TALLOC_FREE ( adp_smb_fname ) ;
2016-12-08 21:12:32 +03:00
return fd ;
}
static int ad_open_rsrc ( vfs_handle_struct * handle ,
2017-05-25 21:38:26 +03:00
const struct smb_filename * smb_fname ,
2016-12-08 21:12:32 +03:00
int flags ,
mode_t mode )
2014-06-23 18:59:45 +04:00
{
struct fruit_config_data * config = NULL ;
2016-12-08 21:12:32 +03:00
int fd ;
2014-06-23 18:59:45 +04:00
2016-12-08 21:12:32 +03:00
SMB_VFS_HANDLE_GET_DATA ( handle , config ,
2014-06-23 18:59:45 +04:00
struct fruit_config_data , return - 1 ) ;
2016-12-08 21:12:32 +03:00
if ( config - > rsrc = = FRUIT_RSRC_XATTR ) {
2017-05-25 21:38:26 +03:00
fd = ad_open_rsrc_xattr ( smb_fname , flags , mode ) ;
2014-06-23 18:59:45 +04:00
} else {
2017-05-25 21:38:26 +03:00
fd = ad_open_rsrc_adouble ( smb_fname , flags , mode ) ;
2016-12-08 21:12:32 +03:00
}
2014-06-23 18:59:45 +04:00
2016-12-08 21:12:32 +03:00
return fd ;
}
2014-06-23 18:59:45 +04:00
2017-05-23 18:31:47 +03:00
/*
* Here ' s the deal : for ADOUBLE_META we can do without an fd as we can issue
* path based xattr calls . For ADOUBLE_RSRC however we need a full - fledged fd
* for file IO on the . _ file .
*/
2016-12-08 21:12:32 +03:00
static int ad_open ( vfs_handle_struct * handle ,
struct adouble * ad ,
2017-05-23 18:31:47 +03:00
files_struct * fsp ,
2017-05-25 21:38:26 +03:00
const struct smb_filename * smb_fname ,
2016-12-08 21:12:32 +03:00
int flags ,
mode_t mode )
{
int fd ;
2017-05-23 18:31:47 +03:00
DBG_DEBUG ( " Path [%s] type [%s] \n " , smb_fname - > base_name ,
ad - > ad_type = = ADOUBLE_META ? " meta " : " rsrc " ) ;
2016-12-08 21:12:32 +03:00
2017-05-23 18:31:47 +03:00
if ( ad - > ad_type = = ADOUBLE_META ) {
return 0 ;
}
if ( ( fsp ! = NULL ) & & ( fsp - > fh ! = NULL ) & & ( fsp - > fh - > fd ! = - 1 ) ) {
ad - > ad_fd = fsp - > fh - > fd ;
ad - > ad_opened = false ;
return 0 ;
2014-06-23 18:59:45 +04:00
}
2017-05-23 18:31:47 +03:00
fd = ad_open_rsrc ( handle , smb_fname , flags , mode ) ;
if ( fd = = - 1 ) {
return - 1 ;
2016-12-08 21:12:32 +03:00
}
2017-05-23 18:31:47 +03:00
ad - > ad_opened = true ;
ad - > ad_fd = fd ;
2016-12-08 21:12:32 +03:00
DBG_DEBUG ( " Path [%s] type [%s] fd [%d] \n " ,
2017-05-25 21:38:26 +03:00
smb_fname - > base_name ,
2017-05-23 18:31:47 +03:00
ad - > ad_type = = ADOUBLE_META ? " meta " : " rsrc " , fd ) ;
2016-12-08 21:12:32 +03:00
2017-05-23 18:31:47 +03:00
return 0 ;
2016-12-08 21:12:32 +03:00
}
2017-05-25 21:38:26 +03:00
static ssize_t ad_read_rsrc_xattr ( struct adouble * ad )
2016-12-08 21:12:32 +03:00
{
int ret ;
SMB_STRUCT_STAT st ;
/* FIXME: direct sys_fstat(), don't have an fsp */
ret = sys_fstat ( ad - > ad_fd , & st ,
2014-06-23 18:59:45 +04:00
lp_fake_directory_create_times (
SNUM ( ad - > ad_handle - > conn ) ) ) ;
2016-12-08 21:12:32 +03:00
if ( ret ! = 0 ) {
return - 1 ;
}
2014-06-23 18:59:45 +04:00
2016-12-08 21:12:32 +03:00
ad_setentrylen ( ad , ADEID_RFORK , st . st_ex_size ) ;
return st . st_ex_size ;
}
2015-03-02 20:15:06 +03:00
2016-12-08 21:12:32 +03:00
static ssize_t ad_read_rsrc_adouble ( struct adouble * ad ,
2017-05-25 21:38:26 +03:00
const struct smb_filename * smb_fname )
2016-12-08 21:12:32 +03:00
{
SMB_STRUCT_STAT sbuf ;
char * p_ad = NULL ;
2017-10-10 17:04:29 +03:00
size_t size ;
2018-10-05 17:25:27 +03:00
ssize_t len ;
2016-12-08 21:12:32 +03:00
int ret ;
bool ok ;
2014-06-23 18:59:45 +04:00
2016-12-08 21:12:32 +03:00
ret = sys_fstat ( ad - > ad_fd , & sbuf , lp_fake_directory_create_times (
SNUM ( ad - > ad_handle - > conn ) ) ) ;
if ( ret ! = 0 ) {
return - 1 ;
}
2016-11-16 13:01:45 +03:00
2017-10-10 17:04:29 +03:00
/*
* AppleDouble file header content and size , two cases :
*
* - without xattrs it is exactly AD_DATASZ_DOT_UND ( 82 ) bytes large
* - with embedded xattrs it can be larger , up to AD_XATTR_MAX_HDR_SIZE
*
* Read as much as we can up to AD_XATTR_MAX_HDR_SIZE .
*/
size = sbuf . st_ex_size ;
if ( size > talloc_array_length ( ad - > ad_data ) ) {
if ( size > AD_XATTR_MAX_HDR_SIZE ) {
size = AD_XATTR_MAX_HDR_SIZE ;
}
p_ad = talloc_realloc ( ad , ad - > ad_data , char , size ) ;
if ( p_ad = = NULL ) {
return - 1 ;
}
ad - > ad_data = p_ad ;
}
len = sys_pread ( ad - > ad_fd , ad - > ad_data ,
talloc_array_length ( ad - > ad_data ) , 0 ) ;
if ( len ! = talloc_array_length ( ad - > ad_data ) ) {
DBG_NOTICE ( " %s %s: bad size: %zd \n " ,
smb_fname - > base_name , strerror ( errno ) , len ) ;
return - 1 ;
}
2016-12-08 21:12:32 +03:00
/* Now parse entries */
ok = ad_unpack ( ad , ADEID_NUM_DOT_UND , sbuf . st_ex_size ) ;
if ( ! ok ) {
2017-05-25 21:38:26 +03:00
DBG_ERR ( " invalid AppleDouble resource %s \n " ,
smb_fname - > base_name ) ;
2016-12-08 21:12:32 +03:00
errno = EINVAL ;
return - 1 ;
}
2014-06-23 18:59:45 +04:00
2016-12-08 21:12:32 +03:00
if ( ( ad_getentryoff ( ad , ADEID_FINDERI ) ! = ADEDOFF_FINDERI_DOT_UND )
| | ( ad_getentrylen ( ad , ADEID_FINDERI ) < ADEDLEN_FINDERI )
| | ( ad_getentryoff ( ad , ADEID_RFORK ) < ADEDOFF_RFORK_DOT_UND ) ) {
2017-05-25 21:38:26 +03:00
DBG_ERR ( " invalid AppleDouble resource %s \n " ,
smb_fname - > base_name ) ;
2016-12-08 21:12:32 +03:00
errno = EINVAL ;
return - 1 ;
}
2014-06-23 18:59:45 +04:00
2016-12-08 21:12:32 +03:00
/*
* Try to fixup AppleDouble files created by OS X with xattrs
2018-09-11 15:05:43 +03:00
* appended to the ADEID_FINDERI entry .
2016-12-08 21:12:32 +03:00
*/
2014-06-23 18:59:45 +04:00
2018-10-04 09:51:28 +03:00
ret = ad_convert ( ad , smb_fname ) ;
2016-12-08 21:12:32 +03:00
if ( ret ! = 0 ) {
2017-05-25 21:38:26 +03:00
DBG_WARNING ( " Failed to convert [%s] \n " , smb_fname - > base_name ) ;
2016-12-08 21:12:32 +03:00
return len ;
2014-06-23 18:59:45 +04:00
}
2016-12-08 21:12:32 +03:00
return len ;
}
/**
* Read and parse resource fork , either . _ AppleDouble file or xattr
* */
static ssize_t ad_read_rsrc ( struct adouble * ad ,
2017-05-25 21:38:26 +03:00
const struct smb_filename * smb_fname )
2016-12-08 21:12:32 +03:00
{
struct fruit_config_data * config = NULL ;
ssize_t len ;
SMB_VFS_HANDLE_GET_DATA ( ad - > ad_handle , config ,
struct fruit_config_data , return - 1 ) ;
if ( config - > rsrc = = FRUIT_RSRC_XATTR ) {
2017-05-25 21:38:26 +03:00
len = ad_read_rsrc_xattr ( ad ) ;
2016-12-08 21:12:32 +03:00
} else {
2017-05-25 21:38:26 +03:00
len = ad_read_rsrc_adouble ( ad , smb_fname ) ;
2016-12-08 21:12:32 +03:00
}
2014-06-23 18:59:45 +04:00
return len ;
}
/**
* Read and unpack an AppleDouble metadata xattr or resource
* */
2017-05-25 21:38:26 +03:00
static ssize_t ad_read ( struct adouble * ad , const struct smb_filename * smb_fname )
2014-06-23 18:59:45 +04:00
{
switch ( ad - > ad_type ) {
case ADOUBLE_META :
2017-05-25 21:38:26 +03:00
return ad_read_meta ( ad , smb_fname ) ;
2014-06-23 18:59:45 +04:00
case ADOUBLE_RSRC :
2017-05-25 21:38:26 +03:00
return ad_read_rsrc ( ad , smb_fname ) ;
2014-06-23 18:59:45 +04:00
default :
return - 1 ;
}
}
2016-12-08 21:12:32 +03:00
static int adouble_destructor ( struct adouble * ad )
{
if ( ( ad - > ad_fd ! = - 1 ) & & ad - > ad_opened ) {
close ( ad - > ad_fd ) ;
ad - > ad_fd = - 1 ;
}
return 0 ;
}
2014-06-23 18:59:45 +04:00
/**
* Allocate a struct adouble without initialiing it
*
* The struct is either hang of the fsp extension context or if fsp is
* NULL from ctx .
*
* @ param [ in ] ctx talloc context
* @ param [ in ] handle vfs handle
* @ param [ in ] type type of AppleDouble , ADOUBLE_META or ADOUBLE_RSRC
*
* @ return adouble handle
* */
static struct adouble * ad_alloc ( TALLOC_CTX * ctx , vfs_handle_struct * handle ,
2016-12-08 21:12:32 +03:00
adouble_type_t type )
2014-06-23 18:59:45 +04:00
{
int rc = 0 ;
size_t adsize = 0 ;
struct adouble * ad ;
struct fruit_config_data * config ;
SMB_VFS_HANDLE_GET_DATA ( handle , config ,
struct fruit_config_data , return NULL ) ;
switch ( type ) {
case ADOUBLE_META :
adsize = AD_DATASZ_XATTR ;
break ;
case ADOUBLE_RSRC :
if ( config - > rsrc = = FRUIT_RSRC_ADFILE ) {
adsize = AD_DATASZ_DOT_UND ;
}
break ;
default :
return NULL ;
}
2016-12-08 21:12:32 +03:00
ad = talloc_zero ( ctx , struct adouble ) ;
if ( ad = = NULL ) {
rc = - 1 ;
goto exit ;
}
if ( adsize ) {
ad - > ad_data = talloc_zero_array ( ad , char , adsize ) ;
if ( ad - > ad_data = = NULL ) {
2014-06-23 18:59:45 +04:00
rc = - 1 ;
goto exit ;
}
}
ad - > ad_handle = handle ;
ad - > ad_type = type ;
ad - > ad_magic = AD_MAGIC ;
ad - > ad_version = AD_VERSION ;
2016-12-08 21:12:32 +03:00
ad - > ad_fd = - 1 ;
talloc_set_destructor ( ad , adouble_destructor ) ;
2014-06-23 18:59:45 +04:00
exit :
if ( rc ! = 0 ) {
TALLOC_FREE ( ad ) ;
}
return ad ;
}
/**
* Allocate and initialize a new struct adouble
*
* @ param [ in ] ctx talloc context
* @ param [ in ] handle vfs handle
* @ param [ in ] type type of AppleDouble , ADOUBLE_META or ADOUBLE_RSRC
*
* @ return adouble handle , initialized
* */
static struct adouble * ad_init ( TALLOC_CTX * ctx , vfs_handle_struct * handle ,
2016-12-08 21:12:32 +03:00
adouble_type_t type )
2014-06-23 18:59:45 +04:00
{
int rc = 0 ;
const struct ad_entry_order * eid ;
struct adouble * ad = NULL ;
struct fruit_config_data * config ;
time_t t = time ( NULL ) ;
SMB_VFS_HANDLE_GET_DATA ( handle , config ,
struct fruit_config_data , return NULL ) ;
switch ( type ) {
case ADOUBLE_META :
eid = entry_order_meta_xattr ;
break ;
case ADOUBLE_RSRC :
if ( config - > rsrc = = FRUIT_RSRC_ADFILE ) {
eid = entry_order_dot_und ;
} else {
eid = entry_order_rsrc_xattr ;
}
break ;
default :
return NULL ;
}
2016-12-08 21:12:32 +03:00
ad = ad_alloc ( ctx , handle , type ) ;
2014-06-23 18:59:45 +04:00
if ( ad = = NULL ) {
return NULL ;
}
while ( eid - > id ) {
ad - > ad_eid [ eid - > id ] . ade_off = eid - > offset ;
ad - > ad_eid [ eid - > id ] . ade_len = eid - > len ;
eid + + ;
}
/* put something sane in the date fields */
ad_setdate ( ad , AD_DATE_CREATE | AD_DATE_UNIX , t ) ;
ad_setdate ( ad , AD_DATE_MODIFY | AD_DATE_UNIX , t ) ;
ad_setdate ( ad , AD_DATE_ACCESS | AD_DATE_UNIX , t ) ;
ad_setdate ( ad , AD_DATE_BACKUP , htonl ( AD_DATE_START ) ) ;
if ( rc ! = 0 ) {
TALLOC_FREE ( ad ) ;
}
return ad ;
}
2017-05-24 10:17:19 +03:00
static struct adouble * ad_get_internal ( TALLOC_CTX * ctx ,
vfs_handle_struct * handle ,
files_struct * fsp ,
const struct smb_filename * smb_fname ,
adouble_type_t type )
2014-06-23 18:59:45 +04:00
{
int rc = 0 ;
ssize_t len ;
struct adouble * ad = NULL ;
2016-12-08 21:12:32 +03:00
int mode ;
2014-06-23 18:59:45 +04:00
2017-05-24 10:17:19 +03:00
if ( fsp ! = NULL ) {
smb_fname = fsp - > base_fsp - > fsp_name ;
}
2014-06-23 18:59:45 +04:00
DEBUG ( 10 , ( " ad_get(%s) called for %s \n " ,
2017-05-25 21:38:26 +03:00
type = = ADOUBLE_META ? " meta " : " rsrc " ,
smb_fname - > base_name ) ) ;
2014-06-23 18:59:45 +04:00
2016-12-08 21:12:32 +03:00
ad = ad_alloc ( ctx , handle , type ) ;
2014-06-23 18:59:45 +04:00
if ( ad = = NULL ) {
rc = - 1 ;
goto exit ;
}
2017-05-23 18:31:47 +03:00
/* Try rw first so we can use the fd in ad_convert() */
mode = O_RDWR ;
2017-05-24 10:17:19 +03:00
rc = ad_open ( handle , ad , fsp , smb_fname , mode , 0 ) ;
2017-05-23 18:31:47 +03:00
if ( rc = = - 1 & & ( ( errno = = EROFS ) | | ( errno = = EACCES ) ) ) {
mode = O_RDONLY ;
2017-05-24 10:17:19 +03:00
rc = ad_open ( handle , ad , fsp , smb_fname , mode , 0 ) ;
2017-05-23 18:31:47 +03:00
}
if ( rc = = - 1 ) {
DBG_DEBUG ( " ad_open [%s] error [%s] \n " ,
smb_fname - > base_name , strerror ( errno ) ) ;
goto exit ;
2016-12-08 21:12:32 +03:00
}
2017-05-25 21:38:26 +03:00
len = ad_read ( ad , smb_fname ) ;
2014-06-23 18:59:45 +04:00
if ( len = = - 1 ) {
2017-05-25 21:38:26 +03:00
DEBUG ( 10 , ( " error reading AppleDouble for %s \n " ,
smb_fname - > base_name ) ) ;
2014-06-23 18:59:45 +04:00
rc = - 1 ;
goto exit ;
}
exit :
DEBUG ( 10 , ( " ad_get(%s) for %s returning %d \n " ,
2017-05-25 21:38:26 +03:00
type = = ADOUBLE_META ? " meta " : " rsrc " ,
smb_fname - > base_name , rc ) ) ;
2014-06-23 18:59:45 +04:00
if ( rc ! = 0 ) {
TALLOC_FREE ( ad ) ;
}
return ad ;
}
2017-05-24 10:17:19 +03:00
/**
* Return AppleDouble data for a file
*
* @ param [ in ] ctx talloc context
* @ param [ in ] handle vfs handle
* @ param [ in ] smb_fname pathname to file or directory
* @ param [ in ] type type of AppleDouble , ADOUBLE_META or ADOUBLE_RSRC
*
* @ return talloced struct adouble or NULL on error
* */
static struct adouble * ad_get ( TALLOC_CTX * ctx ,
vfs_handle_struct * handle ,
const struct smb_filename * smb_fname ,
adouble_type_t type )
{
return ad_get_internal ( ctx , handle , NULL , smb_fname , type ) ;
}
2016-12-08 21:12:32 +03:00
/**
* Return AppleDouble data for a file
*
* @ param [ in ] ctx talloc context
* @ param [ in ] handle vfs handle
* @ param [ in ] fsp fsp to use for IO
* @ param [ in ] type type of AppleDouble , ADOUBLE_META or ADOUBLE_RSRC
*
* @ return talloced struct adouble or NULL on error
* */
static struct adouble * ad_fget ( TALLOC_CTX * ctx , vfs_handle_struct * handle ,
files_struct * fsp , adouble_type_t type )
{
2017-05-24 10:17:19 +03:00
return ad_get_internal ( ctx , handle , fsp , NULL , type ) ;
2016-12-08 21:12:32 +03:00
}
2014-06-23 18:59:45 +04:00
/**
* Set AppleDouble metadata on a file or directory
*
* @ param [ in ] ad adouble handle
2016-12-08 21:12:32 +03:00
*
2017-05-25 21:38:26 +03:00
* @ param [ in ] smb_fname pathname to file or directory
2016-12-08 21:12:32 +03:00
*
* @ return status code , 0 means success
* */
2017-05-25 21:38:26 +03:00
static int ad_set ( struct adouble * ad , const struct smb_filename * smb_fname )
2016-12-08 21:12:32 +03:00
{
bool ok ;
int ret ;
2017-05-25 21:38:26 +03:00
DBG_DEBUG ( " Path [%s] \n " , smb_fname - > base_name ) ;
2016-12-08 21:12:32 +03:00
if ( ad - > ad_type ! = ADOUBLE_META ) {
2017-05-25 21:38:26 +03:00
DBG_ERR ( " ad_set on [%s] used with ADOUBLE_RSRC \n " ,
smb_fname - > base_name ) ;
2016-12-08 21:12:32 +03:00
return - 1 ;
}
ok = ad_pack ( ad ) ;
if ( ! ok ) {
return - 1 ;
}
2014-09-15 15:49:48 +04:00
2016-12-08 21:12:32 +03:00
ret = SMB_VFS_SETXATTR ( ad - > ad_handle - > conn ,
2017-05-25 22:41:31 +03:00
smb_fname ,
2016-12-08 21:12:32 +03:00
AFPINFO_EA_NETATALK ,
ad - > ad_data ,
AD_DATASZ_XATTR , 0 ) ;
2017-05-25 21:38:26 +03:00
DBG_DEBUG ( " Path [%s] ret [%d] \n " , smb_fname - > base_name , ret ) ;
2016-12-08 21:12:32 +03:00
return ret ;
}
/**
* Set AppleDouble metadata on a file or directory
*
* @ param [ in ] ad adouble handle
* @ param [ in ] fsp file handle
2014-06-23 18:59:45 +04:00
*
* @ return status code , 0 means success
* */
2016-12-08 21:12:32 +03:00
static int ad_fset ( struct adouble * ad , files_struct * fsp )
2014-06-23 18:59:45 +04:00
{
2016-12-08 21:12:32 +03:00
int rc = - 1 ;
2014-06-23 18:59:45 +04:00
ssize_t len ;
2015-09-27 13:11:31 +03:00
bool ok ;
2014-06-23 18:59:45 +04:00
2016-12-08 21:12:32 +03:00
DBG_DEBUG ( " Path [%s] \n " , fsp_str_dbg ( fsp ) ) ;
if ( ( fsp = = NULL )
| | ( fsp - > fh = = NULL )
| | ( fsp - > fh - > fd = = - 1 ) )
{
smb_panic ( " bad fsp " ) ;
}
2015-09-27 13:11:31 +03:00
ok = ad_pack ( ad ) ;
if ( ! ok ) {
return - 1 ;
2014-06-23 18:59:45 +04:00
}
switch ( ad - > ad_type ) {
case ADOUBLE_META :
2017-05-23 18:39:46 +03:00
rc = SMB_VFS_NEXT_SETXATTR ( ad - > ad_handle ,
fsp - > fsp_name ,
AFPINFO_EA_NETATALK ,
ad - > ad_data ,
AD_DATASZ_XATTR , 0 ) ;
2014-06-23 18:59:45 +04:00
break ;
2016-12-08 21:12:32 +03:00
2014-06-23 18:59:45 +04:00
case ADOUBLE_RSRC :
2016-12-08 21:12:32 +03:00
len = SMB_VFS_NEXT_PWRITE ( ad - > ad_handle ,
fsp ,
ad - > ad_data ,
2017-10-10 17:04:29 +03:00
AD_DATASZ_DOT_UND ,
2016-12-08 21:12:32 +03:00
0 ) ;
2017-10-10 17:04:29 +03:00
if ( len ! = AD_DATASZ_DOT_UND ) {
2016-12-08 21:12:32 +03:00
DBG_ERR ( " short write on %s: %zd " , fsp_str_dbg ( fsp ) , len ) ;
return - 1 ;
2014-06-23 18:59:45 +04:00
}
2016-12-08 21:12:32 +03:00
rc = 0 ;
2014-06-23 18:59:45 +04:00
break ;
2016-12-08 21:12:32 +03:00
2014-06-23 18:59:45 +04:00
default :
return - 1 ;
}
2016-12-08 21:12:32 +03:00
DBG_DEBUG ( " Path [%s] rc [%d] \n " , fsp_str_dbg ( fsp ) , rc ) ;
2014-06-23 18:59:45 +04:00
return rc ;
}
/*****************************************************************************
* Helper functions
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
static bool is_afpinfo_stream ( const struct smb_filename * smb_fname )
{
if ( strncasecmp_m ( smb_fname - > stream_name ,
AFPINFO_STREAM_NAME ,
strlen ( AFPINFO_STREAM_NAME ) ) = = 0 ) {
return true ;
}
return false ;
}
static bool is_afpresource_stream ( const struct smb_filename * smb_fname )
{
if ( strncasecmp_m ( smb_fname - > stream_name ,
AFPRESOURCE_STREAM_NAME ,
strlen ( AFPRESOURCE_STREAM_NAME ) ) = = 0 ) {
return true ;
}
return false ;
}
/**
* Test whether stream is an Apple stream , not used atm
* */
#if 0
static bool is_apple_stream ( const struct smb_filename * smb_fname )
{
if ( is_afpinfo_stream ( smb_fname ) ) {
return true ;
}
if ( is_afpresource_stream ( smb_fname ) ) {
return true ;
}
return false ;
}
# endif
/**
* Initialize config struct from our smb . conf config parameters
* */
static int init_fruit_config ( vfs_handle_struct * handle )
{
struct fruit_config_data * config ;
int enumval ;
2017-11-03 12:56:29 +03:00
const char * tm_size_str = NULL ;
2014-06-23 18:59:45 +04:00
config = talloc_zero ( handle - > conn , struct fruit_config_data ) ;
if ( ! config ) {
DEBUG ( 1 , ( " talloc_zero() failed \n " ) ) ;
errno = ENOMEM ;
return - 1 ;
}
2016-11-08 14:35:12 +03:00
/*
* Versions up to Samba 4.5 . x had a spelling bug in the
* fruit : resource option calling lp_parm_enum with
* " res*s*ource " ( ie two s ) .
*
* In Samba 4.6 we accept both the wrong and the correct
* spelling , in Samba 4.7 the bad spelling will be removed .
*/
2014-06-23 18:59:45 +04:00
enumval = lp_parm_enum ( SNUM ( handle - > conn ) , FRUIT_PARAM_TYPE_NAME ,
" ressource " , fruit_rsrc , FRUIT_RSRC_ADFILE ) ;
if ( enumval = = - 1 ) {
2016-11-08 14:35:12 +03:00
DEBUG ( 1 , ( " value for %s: resource type unknown \n " ,
FRUIT_PARAM_TYPE_NAME ) ) ;
return - 1 ;
}
config - > rsrc = ( enum fruit_rsrc ) enumval ;
enumval = lp_parm_enum ( SNUM ( handle - > conn ) , FRUIT_PARAM_TYPE_NAME ,
" resource " , fruit_rsrc , enumval ) ;
if ( enumval = = - 1 ) {
DEBUG ( 1 , ( " value for %s: resource type unknown \n " ,
2014-06-23 18:59:45 +04:00
FRUIT_PARAM_TYPE_NAME ) ) ;
return - 1 ;
}
config - > rsrc = ( enum fruit_rsrc ) enumval ;
enumval = lp_parm_enum ( SNUM ( handle - > conn ) , FRUIT_PARAM_TYPE_NAME ,
" metadata " , fruit_meta , FRUIT_META_NETATALK ) ;
if ( enumval = = - 1 ) {
DEBUG ( 1 , ( " value for %s: metadata type unknown \n " ,
FRUIT_PARAM_TYPE_NAME ) ) ;
return - 1 ;
}
config - > meta = ( enum fruit_meta ) enumval ;
enumval = lp_parm_enum ( SNUM ( handle - > conn ) , FRUIT_PARAM_TYPE_NAME ,
" locking " , fruit_locking , FRUIT_LOCKING_NONE ) ;
if ( enumval = = - 1 ) {
DEBUG ( 1 , ( " value for %s: locking type unknown \n " ,
FRUIT_PARAM_TYPE_NAME ) ) ;
return - 1 ;
}
config - > locking = ( enum fruit_locking ) enumval ;
enumval = lp_parm_enum ( SNUM ( handle - > conn ) , FRUIT_PARAM_TYPE_NAME ,
" encoding " , fruit_encoding , FRUIT_ENC_PRIVATE ) ;
if ( enumval = = - 1 ) {
DEBUG ( 1 , ( " value for %s: encoding type unknown \n " ,
FRUIT_PARAM_TYPE_NAME ) ) ;
return - 1 ;
}
config - > encoding = ( enum fruit_encoding ) enumval ;
2017-01-19 11:30:45 +03:00
if ( config - > rsrc = = FRUIT_RSRC_ADFILE ) {
config - > veto_appledouble = lp_parm_bool ( SNUM ( handle - > conn ) ,
FRUIT_PARAM_TYPE_NAME ,
" veto_appledouble " ,
true ) ;
}
2015-05-09 09:31:24 +03:00
2015-06-15 19:31:23 +03:00
config - > use_aapl = lp_parm_bool (
- 1 , FRUIT_PARAM_TYPE_NAME , " aapl " , true ) ;
2014-11-26 20:11:17 +03:00
2016-11-14 21:14:44 +03:00
config - > time_machine = lp_parm_bool (
SNUM ( handle - > conn ) , FRUIT_PARAM_TYPE_NAME , " time machine " , false ) ;
2015-06-15 19:31:23 +03:00
config - > unix_info_enabled = lp_parm_bool (
- 1 , FRUIT_PARAM_TYPE_NAME , " nfs_aces " , true ) ;
2015-03-25 17:09:02 +03:00
2015-04-22 23:29:16 +03:00
config - > use_copyfile = lp_parm_bool ( - 1 , FRUIT_PARAM_TYPE_NAME ,
" copyfile " , false ) ;
2016-04-11 13:17:22 +03:00
config - > posix_rename = lp_parm_bool (
SNUM ( handle - > conn ) , FRUIT_PARAM_TYPE_NAME , " posix_rename " , true ) ;
2017-03-23 15:08:45 +03:00
config - > aapl_zero_file_id =
lp_parm_bool ( - 1 , FRUIT_PARAM_TYPE_NAME , " zero_file_id " , true ) ;
2015-06-15 19:31:23 +03:00
config - > readdir_attr_rsize = lp_parm_bool (
SNUM ( handle - > conn ) , " readdir_attr " , " aapl_rsize " , true ) ;
2014-11-26 20:11:17 +03:00
2015-06-15 19:31:23 +03:00
config - > readdir_attr_finder_info = lp_parm_bool (
SNUM ( handle - > conn ) , " readdir_attr " , " aapl_finder_info " , true ) ;
2014-11-26 20:11:17 +03:00
2015-06-15 19:31:23 +03:00
config - > readdir_attr_max_access = lp_parm_bool (
SNUM ( handle - > conn ) , " readdir_attr " , " aapl_max_access " , true ) ;
2014-11-26 20:11:17 +03:00
2017-06-28 19:10:28 +03:00
config - > model = lp_parm_const_string (
- 1 , FRUIT_PARAM_TYPE_NAME , " model " , " MacSamba " ) ;
2017-11-03 12:56:29 +03:00
tm_size_str = lp_parm_const_string (
SNUM ( handle - > conn ) , FRUIT_PARAM_TYPE_NAME ,
" time machine max size " , NULL ) ;
if ( tm_size_str ! = NULL ) {
2018-02-22 17:52:46 +03:00
config - > time_machine_max_size = conv_str_size ( tm_size_str ) ;
2017-11-03 12:56:29 +03:00
}
2018-10-03 13:01:00 +03:00
config - > wipe_intentionally_left_blank_rfork = lp_parm_bool (
SNUM ( handle - > conn ) , FRUIT_PARAM_TYPE_NAME ,
" wipe_intentionally_left_blank_rfork " , false ) ;
2018-10-03 13:01:00 +03:00
config - > delete_empty_adfiles = lp_parm_bool (
SNUM ( handle - > conn ) , FRUIT_PARAM_TYPE_NAME ,
" delete_empty_adfiles " , false ) ;
2014-06-23 18:59:45 +04:00
SMB_VFS_HANDLE_SET_DATA ( handle , config ,
NULL , struct fruit_config_data ,
return - 1 ) ;
return 0 ;
}
/**
* Prepend " ._ " to a basename
2017-05-25 21:38:26 +03:00
* Return a new struct smb_filename with stream_name = = NULL .
2014-06-23 18:59:45 +04:00
* */
2017-05-25 21:38:26 +03:00
static int adouble_path ( TALLOC_CTX * ctx ,
const struct smb_filename * smb_fname_in ,
struct smb_filename * * pp_smb_fname_out )
2014-06-23 18:59:45 +04:00
{
char * parent ;
2014-11-13 11:12:56 +03:00
const char * base ;
2017-05-25 21:38:26 +03:00
struct smb_filename * smb_fname = cp_smb_filename ( ctx ,
smb_fname_in ) ;
if ( smb_fname = = NULL ) {
return - 1 ;
}
/* We need streamname to be NULL */
TALLOC_FREE ( smb_fname - > stream_name ) ;
2014-06-23 18:59:45 +04:00
2017-05-25 21:38:26 +03:00
/* And we're replacing base_name. */
TALLOC_FREE ( smb_fname - > base_name ) ;
if ( ! parent_dirname ( smb_fname , smb_fname_in - > base_name ,
& parent , & base ) ) {
TALLOC_FREE ( smb_fname ) ;
2014-06-23 18:59:45 +04:00
return - 1 ;
}
2017-05-25 21:38:26 +03:00
smb_fname - > base_name = talloc_asprintf ( smb_fname ,
" %s/._%s " , parent , base ) ;
if ( smb_fname - > base_name = = NULL ) {
TALLOC_FREE ( smb_fname ) ;
2014-06-23 18:59:45 +04:00
return - 1 ;
}
2017-05-25 21:38:26 +03:00
* pp_smb_fname_out = smb_fname ;
2014-06-23 18:59:45 +04:00
return 0 ;
}
/**
* Allocate and initialize an AfpInfo struct
* */
static AfpInfo * afpinfo_new ( TALLOC_CTX * ctx )
{
AfpInfo * ai = talloc_zero ( ctx , AfpInfo ) ;
if ( ai = = NULL ) {
return NULL ;
}
ai - > afpi_Signature = AFP_Signature ;
ai - > afpi_Version = AFP_Version ;
ai - > afpi_BackupTime = AD_DATE_START ;
return ai ;
}
/**
* Pack an AfpInfo struct into a buffer
*
* Buffer size must be at least AFP_INFO_SIZE
* Returns size of packed buffer
* */
static ssize_t afpinfo_pack ( const AfpInfo * ai , char * buf )
{
memset ( buf , 0 , AFP_INFO_SIZE ) ;
RSIVAL ( buf , 0 , ai - > afpi_Signature ) ;
RSIVAL ( buf , 4 , ai - > afpi_Version ) ;
RSIVAL ( buf , 12 , ai - > afpi_BackupTime ) ;
memcpy ( buf + 16 , ai - > afpi_FinderInfo , sizeof ( ai - > afpi_FinderInfo ) ) ;
return AFP_INFO_SIZE ;
}
/**
* Unpack a buffer into a AfpInfo structure
*
* Buffer size must be at least AFP_INFO_SIZE
* Returns allocated AfpInfo struct
* */
static AfpInfo * afpinfo_unpack ( TALLOC_CTX * ctx , const void * data )
{
AfpInfo * ai = talloc_zero ( ctx , AfpInfo ) ;
if ( ai = = NULL ) {
return NULL ;
}
ai - > afpi_Signature = RIVAL ( data , 0 ) ;
ai - > afpi_Version = RIVAL ( data , 4 ) ;
ai - > afpi_BackupTime = RIVAL ( data , 12 ) ;
memcpy ( ai - > afpi_FinderInfo , ( const char * ) data + 16 ,
sizeof ( ai - > afpi_FinderInfo ) ) ;
if ( ai - > afpi_Signature ! = AFP_Signature
| | ai - > afpi_Version ! = AFP_Version ) {
DEBUG ( 1 , ( " Bad AfpInfo signature or version \n " ) ) ;
TALLOC_FREE ( ai ) ;
}
return ai ;
}
/**
* Fake an inode number from the md5 hash of the ( xattr ) name
* */
static SMB_INO_T fruit_inode ( const SMB_STRUCT_STAT * sbuf , const char * sname )
{
MD5_CTX ctx ;
unsigned char hash [ 16 ] ;
SMB_INO_T result ;
char * upper_sname ;
2018-10-17 20:07:11 +03:00
DBG_DEBUG ( " fruit_inode called for %ju/%ju [%s] \n " ,
( uintmax_t ) sbuf - > st_ex_dev ,
( uintmax_t ) sbuf - > st_ex_ino , sname ) ;
2014-06-23 18:59:45 +04:00
upper_sname = talloc_strdup_upper ( talloc_tos ( ) , sname ) ;
SMB_ASSERT ( upper_sname ! = NULL ) ;
MD5Init ( & ctx ) ;
MD5Update ( & ctx , ( const unsigned char * ) & ( sbuf - > st_ex_dev ) ,
sizeof ( sbuf - > st_ex_dev ) ) ;
MD5Update ( & ctx , ( const unsigned char * ) & ( sbuf - > st_ex_ino ) ,
sizeof ( sbuf - > st_ex_ino ) ) ;
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 ) ) ;
2018-10-17 20:07:11 +03:00
DBG_DEBUG ( " fruit_inode \" %s \" : ino=%ju \n " ,
sname , ( uintmax_t ) result ) ;
2014-06-23 18:59:45 +04:00
return result ;
}
static bool add_fruit_stream ( TALLOC_CTX * mem_ctx , unsigned int * num_streams ,
struct stream_struct * * streams ,
const char * name , off_t size ,
off_t alloc_size )
{
struct stream_struct * tmp ;
tmp = talloc_realloc ( mem_ctx , * streams , struct stream_struct ,
( * num_streams ) + 1 ) ;
if ( tmp = = NULL ) {
return false ;
}
tmp [ * num_streams ] . name = talloc_asprintf ( tmp , " %s:$DATA " , 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 ;
}
2016-12-02 17:49:03 +03:00
static bool filter_empty_rsrc_stream ( unsigned int * num_streams ,
struct stream_struct * * streams )
{
struct stream_struct * tmp = * streams ;
unsigned int i ;
if ( * num_streams = = 0 ) {
return true ;
}
for ( i = 0 ; i < * num_streams ; i + + ) {
if ( strequal_m ( tmp [ i ] . name , AFPRESOURCE_STREAM ) ) {
break ;
}
}
if ( i = = * num_streams ) {
return true ;
}
if ( tmp [ i ] . size > 0 ) {
return true ;
}
TALLOC_FREE ( tmp [ i ] . name ) ;
if ( * num_streams - 1 > i ) {
memmove ( & tmp [ i ] , & tmp [ i + 1 ] ,
( * num_streams - i - 1 ) * sizeof ( struct stream_struct ) ) ;
}
* num_streams - = 1 ;
return true ;
}
2015-08-24 18:43:40 +03:00
static bool del_fruit_stream ( TALLOC_CTX * mem_ctx , unsigned int * num_streams ,
struct stream_struct * * streams ,
const char * name )
{
struct stream_struct * tmp = * streams ;
2016-05-19 19:42:04 +03:00
unsigned int i ;
2015-08-24 18:43:40 +03:00
if ( * num_streams = = 0 ) {
return true ;
}
for ( i = 0 ; i < * num_streams ; i + + ) {
if ( strequal_m ( tmp [ i ] . name , name ) ) {
break ;
}
}
if ( i = = * num_streams ) {
return true ;
}
TALLOC_FREE ( tmp [ i ] . name ) ;
if ( * num_streams - 1 > i ) {
memmove ( & tmp [ i ] , & tmp [ i + 1 ] ,
( * num_streams - i - 1 ) * sizeof ( struct stream_struct ) ) ;
}
* num_streams - = 1 ;
return true ;
}
2016-12-09 18:25:38 +03:00
static bool ad_empty_finderinfo ( const struct adouble * ad )
2014-06-23 18:59:45 +04:00
{
2016-12-09 18:25:38 +03:00
int cmp ;
2014-06-23 18:59:45 +04:00
char emptybuf [ ADEDLEN_FINDERI ] = { 0 } ;
2016-12-09 18:25:38 +03:00
char * fi = NULL ;
2016-11-16 13:01:45 +03:00
fi = ad_get_entry ( ad , ADEID_FINDERI ) ;
2016-12-09 18:25:38 +03:00
if ( fi = = NULL ) {
DBG_ERR ( " Missing FinderInfo in struct adouble [%p] \n " , ad ) ;
return false ;
2014-06-23 18:59:45 +04:00
}
2016-12-09 18:25:38 +03:00
cmp = memcmp ( emptybuf , fi , ADEDLEN_FINDERI ) ;
return ( cmp = = 0 ) ;
2014-06-23 18:59:45 +04:00
}
2016-11-15 23:32:25 +03:00
static bool ai_empty_finderinfo ( const AfpInfo * ai )
{
int cmp ;
char emptybuf [ ADEDLEN_FINDERI ] = { 0 } ;
cmp = memcmp ( emptybuf , & ai - > afpi_FinderInfo [ 0 ] , ADEDLEN_FINDERI ) ;
return ( cmp = = 0 ) ;
}
2014-06-23 18:59:45 +04:00
/**
* Update btime with btime from Netatalk
* */
static void update_btime ( vfs_handle_struct * handle ,
struct smb_filename * smb_fname )
{
uint32_t t ;
struct timespec creation_time = { 0 } ;
struct adouble * ad ;
2016-12-02 19:25:47 +03:00
struct fruit_config_data * config = NULL ;
SMB_VFS_HANDLE_GET_DATA ( handle , config , struct fruit_config_data ,
return ) ;
switch ( config - > meta ) {
case FRUIT_META_STREAM :
return ;
case FRUIT_META_NETATALK :
/* Handled below */
break ;
default :
DBG_ERR ( " Unexpected meta config [%d] \n " , config - > meta ) ;
return ;
}
2014-06-23 18:59:45 +04:00
2017-05-25 21:38:26 +03:00
ad = ad_get ( talloc_tos ( ) , handle , smb_fname , ADOUBLE_META ) ;
2014-06-23 18:59:45 +04:00
if ( ad = = NULL ) {
return ;
}
if ( ad_getdate ( ad , AD_DATE_UNIX | AD_DATE_CREATE , & t ) ! = 0 ) {
TALLOC_FREE ( ad ) ;
return ;
}
TALLOC_FREE ( ad ) ;
creation_time . tv_sec = convert_uint32_t_to_time_t ( t ) ;
update_stat_ex_create_time ( & smb_fname - > st , creation_time ) ;
return ;
}
/**
* Map an access mask to a Netatalk single byte byte range lock
* */
2014-11-13 11:12:56 +03:00
static off_t access_to_netatalk_brl ( enum apple_fork fork_type ,
2014-06-23 18:59:45 +04:00
uint32_t access_mask )
{
off_t offset ;
switch ( access_mask ) {
case FILE_READ_DATA :
offset = AD_FILELOCK_OPEN_RD ;
break ;
case FILE_WRITE_DATA :
case FILE_APPEND_DATA :
offset = AD_FILELOCK_OPEN_WR ;
break ;
default :
offset = AD_FILELOCK_OPEN_NONE ;
break ;
}
2014-11-13 11:12:56 +03:00
if ( fork_type = = APPLE_FORK_RSRC ) {
2014-06-23 18:59:45 +04:00
if ( offset = = AD_FILELOCK_OPEN_NONE ) {
offset = AD_FILELOCK_RSRC_OPEN_NONE ;
} else {
offset + = 2 ;
}
}
return offset ;
}
/**
* Map a deny mode to a Netatalk brl
* */
2014-11-13 11:12:56 +03:00
static off_t denymode_to_netatalk_brl ( enum apple_fork fork_type ,
2014-06-23 18:59:45 +04:00
uint32_t deny_mode )
{
2018-09-05 12:31:10 +03:00
off_t offset = 0 ;
2014-06-23 18:59:45 +04:00
switch ( deny_mode ) {
case DENY_READ :
offset = AD_FILELOCK_DENY_RD ;
break ;
case DENY_WRITE :
offset = AD_FILELOCK_DENY_WR ;
break ;
default :
smb_panic ( " denymode_to_netatalk_brl: bad deny mode \n " ) ;
}
2014-11-13 11:12:56 +03:00
if ( fork_type = = APPLE_FORK_RSRC ) {
2014-06-23 18:59:45 +04:00
offset + = 2 ;
}
return offset ;
}
/**
* Call fcntl ( ) with an exclusive F_GETLK request in order to
* determine if there ' s an exisiting shared lock
*
2017-02-17 22:46:28 +03:00
* @ return true if the requested lock was found or any error occurred
2014-06-23 18:59:45 +04:00
* false if the lock was not found
* */
static bool test_netatalk_lock ( files_struct * fsp , off_t in_offset )
{
bool result ;
off_t offset = in_offset ;
off_t len = 1 ;
int type = F_WRLCK ;
pid_t pid ;
result = SMB_VFS_GETLOCK ( fsp , & offset , & len , & type , & pid ) ;
if ( result = = false ) {
return true ;
}
if ( type ! = F_UNLCK ) {
return true ;
}
return false ;
}
static NTSTATUS fruit_check_access ( vfs_handle_struct * handle ,
files_struct * fsp ,
uint32_t access_mask ,
uint32_t deny_mode )
{
NTSTATUS status = NT_STATUS_OK ;
bool open_for_reading , open_for_writing , deny_read , deny_write ;
off_t off ;
2016-12-08 13:08:53 +03:00
bool have_read = false ;
int flags ;
2014-06-23 18:59:45 +04:00
/* FIXME: hardcoded data fork, add resource fork */
2014-11-13 11:12:56 +03:00
enum apple_fork fork_type = APPLE_FORK_DATA ;
2014-06-23 18:59:45 +04:00
DEBUG ( 10 , ( " fruit_check_access: %s, am: %s/%s, dm: %s/%s \n " ,
fsp_str_dbg ( fsp ) ,
access_mask & FILE_READ_DATA ? " READ " : " - " ,
access_mask & FILE_WRITE_DATA ? " WRITE " : " - " ,
deny_mode & DENY_READ ? " DENY_READ " : " - " ,
deny_mode & DENY_WRITE ? " DENY_WRITE " : " - " ) ) ;
2016-12-08 13:08:53 +03:00
if ( fsp - > fh - > fd = = - 1 ) {
return NT_STATUS_OK ;
}
flags = fcntl ( fsp - > fh - > fd , F_GETFL ) ;
if ( flags = = - 1 ) {
DBG_ERR ( " fcntl get flags [%s] fd [%d] failed [%s] \n " ,
fsp_str_dbg ( fsp ) , fsp - > fh - > fd , strerror ( errno ) ) ;
return map_nt_error_from_unix ( errno ) ;
}
if ( flags & ( O_RDONLY | O_RDWR ) ) {
/*
* Applying fcntl read locks requires an fd opened for
* reading . This means we won ' t be applying locks for
* files openend write - only , but what can we do . . .
*/
have_read = true ;
}
2014-06-23 18:59:45 +04:00
/*
* Check read access and deny read mode
*/
if ( ( access_mask & FILE_READ_DATA ) | | ( deny_mode & DENY_READ ) ) {
/* Check access */
open_for_reading = test_netatalk_lock (
2014-11-13 11:12:56 +03:00
fsp , access_to_netatalk_brl ( fork_type , FILE_READ_DATA ) ) ;
2014-06-23 18:59:45 +04:00
deny_read = test_netatalk_lock (
2014-11-13 11:12:56 +03:00
fsp , denymode_to_netatalk_brl ( fork_type , DENY_READ ) ) ;
2014-06-23 18:59:45 +04:00
DEBUG ( 10 , ( " read: %s, deny_write: %s \n " ,
open_for_reading = = true ? " yes " : " no " ,
deny_read = = true ? " yes " : " no " ) ) ;
if ( ( ( access_mask & FILE_READ_DATA ) & & deny_read )
| | ( ( deny_mode & DENY_READ ) & & open_for_reading ) ) {
return NT_STATUS_SHARING_VIOLATION ;
}
/* Set locks */
2016-12-08 13:08:53 +03:00
if ( ( access_mask & FILE_READ_DATA ) & & have_read ) {
2018-08-06 15:33:34 +03:00
struct byte_range_lock * br_lck = NULL ;
2014-11-13 11:12:56 +03:00
off = access_to_netatalk_brl ( fork_type , FILE_READ_DATA ) ;
2014-06-23 18:59:45 +04:00
br_lck = do_lock (
handle - > conn - > sconn - > msg_ctx , fsp ,
fsp - > op - > global - > open_persistent_id , 1 , off ,
READ_LOCK , POSIX_LOCK , false ,
& status , NULL ) ;
2018-08-06 15:33:34 +03:00
TALLOC_FREE ( br_lck ) ;
2014-06-23 18:59:45 +04:00
if ( ! NT_STATUS_IS_OK ( status ) ) {
return status ;
}
}
2016-12-08 13:08:53 +03:00
if ( ( deny_mode & DENY_READ ) & & have_read ) {
2018-08-06 15:33:34 +03:00
struct byte_range_lock * br_lck = NULL ;
2014-11-13 11:12:56 +03:00
off = denymode_to_netatalk_brl ( fork_type , DENY_READ ) ;
2014-06-23 18:59:45 +04:00
br_lck = do_lock (
handle - > conn - > sconn - > msg_ctx , fsp ,
fsp - > op - > global - > open_persistent_id , 1 , off ,
READ_LOCK , POSIX_LOCK , false ,
& status , NULL ) ;
2018-08-06 15:33:34 +03:00
TALLOC_FREE ( br_lck ) ;
2014-06-23 18:59:45 +04:00
if ( ! NT_STATUS_IS_OK ( status ) ) {
return status ;
}
}
}
/*
* Check write access and deny write mode
*/
if ( ( access_mask & FILE_WRITE_DATA ) | | ( deny_mode & DENY_WRITE ) ) {
/* Check access */
open_for_writing = test_netatalk_lock (
2014-11-13 11:12:56 +03:00
fsp , access_to_netatalk_brl ( fork_type , FILE_WRITE_DATA ) ) ;
2014-06-23 18:59:45 +04:00
deny_write = test_netatalk_lock (
2014-11-13 11:12:56 +03:00
fsp , denymode_to_netatalk_brl ( fork_type , DENY_WRITE ) ) ;
2014-06-23 18:59:45 +04:00
DEBUG ( 10 , ( " write: %s, deny_write: %s \n " ,
open_for_writing = = true ? " yes " : " no " ,
deny_write = = true ? " yes " : " no " ) ) ;
if ( ( ( access_mask & FILE_WRITE_DATA ) & & deny_write )
| | ( ( deny_mode & DENY_WRITE ) & & open_for_writing ) ) {
return NT_STATUS_SHARING_VIOLATION ;
}
/* Set locks */
2016-12-08 13:08:53 +03:00
if ( ( access_mask & FILE_WRITE_DATA ) & & have_read ) {
2018-08-06 15:33:34 +03:00
struct byte_range_lock * br_lck = NULL ;
2014-11-13 11:12:56 +03:00
off = access_to_netatalk_brl ( fork_type , FILE_WRITE_DATA ) ;
2014-06-23 18:59:45 +04:00
br_lck = do_lock (
handle - > conn - > sconn - > msg_ctx , fsp ,
fsp - > op - > global - > open_persistent_id , 1 , off ,
READ_LOCK , POSIX_LOCK , false ,
& status , NULL ) ;
2018-08-06 15:33:34 +03:00
TALLOC_FREE ( br_lck ) ;
2014-06-23 18:59:45 +04:00
if ( ! NT_STATUS_IS_OK ( status ) ) {
return status ;
}
}
2016-12-08 13:08:53 +03:00
if ( ( deny_mode & DENY_WRITE ) & & have_read ) {
2018-08-06 15:33:34 +03:00
struct byte_range_lock * br_lck = NULL ;
2014-11-13 11:12:56 +03:00
off = denymode_to_netatalk_brl ( fork_type , DENY_WRITE ) ;
2014-06-23 18:59:45 +04:00
br_lck = do_lock (
handle - > conn - > sconn - > msg_ctx , fsp ,
fsp - > op - > global - > open_persistent_id , 1 , off ,
READ_LOCK , POSIX_LOCK , false ,
& status , NULL ) ;
2018-08-06 15:33:34 +03:00
TALLOC_FREE ( br_lck ) ;
2014-06-23 18:59:45 +04:00
if ( ! NT_STATUS_IS_OK ( status ) ) {
return status ;
}
}
}
return status ;
}
2014-11-26 20:11:17 +03:00
static NTSTATUS check_aapl ( vfs_handle_struct * handle ,
struct smb_request * req ,
const struct smb2_create_blobs * in_context_blobs ,
struct smb2_create_blobs * out_context_blobs )
{
struct fruit_config_data * config ;
NTSTATUS status ;
struct smb2_create_blob * aapl = NULL ;
uint32_t cmd ;
bool ok ;
uint8_t p [ 16 ] ;
DATA_BLOB blob = data_blob_talloc ( req , NULL , 0 ) ;
uint64_t req_bitmap , client_caps ;
uint64_t server_caps = SMB2_CRTCTX_AAPL_UNIX_BASED ;
smb_ucs2_t * model ;
size_t modellen ;
SMB_VFS_HANDLE_GET_DATA ( handle , config , struct fruit_config_data ,
return NT_STATUS_UNSUCCESSFUL ) ;
if ( ! config - > use_aapl
| | in_context_blobs = = NULL
| | out_context_blobs = = NULL ) {
return NT_STATUS_OK ;
}
aapl = smb2_create_blob_find ( in_context_blobs ,
SMB2_CREATE_TAG_AAPL ) ;
if ( aapl = = NULL ) {
return NT_STATUS_OK ;
}
if ( aapl - > data . length ! = 24 ) {
2015-11-08 12:16:04 +03:00
DEBUG ( 1 , ( " unexpected AAPL ctxt length: %ju \n " ,
2014-11-26 20:11:17 +03:00
( uintmax_t ) aapl - > data . length ) ) ;
return NT_STATUS_INVALID_PARAMETER ;
}
cmd = IVAL ( aapl - > data . data , 0 ) ;
if ( cmd ! = SMB2_CRTCTX_AAPL_SERVER_QUERY ) {
DEBUG ( 1 , ( " unsupported AAPL cmd: %d \n " , cmd ) ) ;
return NT_STATUS_INVALID_PARAMETER ;
}
req_bitmap = BVAL ( aapl - > data . data , 8 ) ;
client_caps = BVAL ( aapl - > data . data , 16 ) ;
SIVAL ( p , 0 , SMB2_CRTCTX_AAPL_SERVER_QUERY ) ;
SIVAL ( p , 4 , 0 ) ;
SBVAL ( p , 8 , req_bitmap ) ;
ok = data_blob_append ( req , & blob , p , 16 ) ;
if ( ! ok ) {
return NT_STATUS_UNSUCCESSFUL ;
}
if ( req_bitmap & SMB2_CRTCTX_AAPL_SERVER_CAPS ) {
if ( ( client_caps & SMB2_CRTCTX_AAPL_SUPPORTS_READ_DIR_ATTR ) & &
( handle - > conn - > tcon - > compat - > fs_capabilities & FILE_NAMED_STREAMS ) ) {
server_caps | = SMB2_CRTCTX_AAPL_SUPPORTS_READ_DIR_ATTR ;
config - > readdir_attr_enabled = true ;
}
2015-04-22 23:29:16 +03:00
if ( config - > use_copyfile ) {
server_caps | = SMB2_CRTCTX_AAPL_SUPPORTS_OSX_COPYFILE ;
config - > copyfile_enabled = true ;
}
2014-11-26 20:11:17 +03:00
/*
* The client doesn ' t set the flag , so we can ' t check
* for it and just set it unconditionally
*/
2015-03-25 17:09:02 +03:00
if ( config - > unix_info_enabled ) {
server_caps | = SMB2_CRTCTX_AAPL_SUPPORTS_NFS_ACE ;
}
2014-11-26 20:11:17 +03:00
SBVAL ( p , 0 , server_caps ) ;
ok = data_blob_append ( req , & blob , p , 8 ) ;
if ( ! ok ) {
return NT_STATUS_UNSUCCESSFUL ;
}
}
if ( req_bitmap & SMB2_CRTCTX_AAPL_VOLUME_CAPS ) {
2017-04-19 14:12:55 +03:00
int val = lp_case_sensitive ( SNUM ( handle - > conn - > tcon - > compat ) ) ;
uint64_t caps = 0 ;
switch ( val ) {
case Auto :
break ;
case True :
caps | = SMB2_CRTCTX_AAPL_CASE_SENSITIVE ;
break ;
default :
break ;
}
2016-11-14 21:14:44 +03:00
if ( config - > time_machine ) {
caps | = SMB2_CRTCTX_AAPL_FULL_SYNC ;
}
2017-04-19 14:12:55 +03:00
SBVAL ( p , 0 , caps ) ;
2014-11-26 20:11:17 +03:00
ok = data_blob_append ( req , & blob , p , 8 ) ;
if ( ! ok ) {
return NT_STATUS_UNSUCCESSFUL ;
}
}
if ( req_bitmap & SMB2_CRTCTX_AAPL_MODEL_INFO ) {
ok = convert_string_talloc ( req ,
CH_UNIX , CH_UTF16LE ,
2017-06-28 19:10:28 +03:00
config - > model , strlen ( config - > model ) ,
2014-11-26 20:11:17 +03:00
& model , & modellen ) ;
if ( ! ok ) {
return NT_STATUS_UNSUCCESSFUL ;
}
SIVAL ( p , 0 , 0 ) ;
SIVAL ( p + 4 , 0 , modellen ) ;
ok = data_blob_append ( req , & blob , p , 8 ) ;
if ( ! ok ) {
talloc_free ( model ) ;
return NT_STATUS_UNSUCCESSFUL ;
}
ok = data_blob_append ( req , & blob , model , modellen ) ;
talloc_free ( model ) ;
if ( ! ok ) {
return NT_STATUS_UNSUCCESSFUL ;
}
}
status = smb2_create_blob_add ( out_context_blobs ,
out_context_blobs ,
SMB2_CREATE_TAG_AAPL ,
blob ) ;
2015-11-25 11:12:55 +03:00
if ( NT_STATUS_IS_OK ( status ) ) {
2017-02-28 11:39:37 +03:00
global_fruit_config . nego_aapl = true ;
2017-03-23 15:08:45 +03:00
if ( config - > aapl_zero_file_id ) {
aapl_force_zero_file_id ( handle - > conn - > sconn ) ;
}
2015-11-25 11:12:55 +03:00
}
2014-11-26 20:11:17 +03:00
return status ;
}
2016-12-01 19:04:35 +03:00
static bool readdir_attr_meta_finderi_stream (
struct vfs_handle_struct * handle ,
const struct smb_filename * smb_fname ,
AfpInfo * ai )
{
2016-12-09 19:24:18 +03:00
struct smb_filename * stream_name = NULL ;
files_struct * fsp = NULL ;
ssize_t nread ;
NTSTATUS status ;
int ret ;
bool ok ;
uint8_t buf [ AFP_INFO_SIZE ] ;
stream_name = synthetic_smb_fname ( talloc_tos ( ) ,
smb_fname - > base_name ,
AFPINFO_STREAM_NAME ,
NULL , smb_fname - > flags ) ;
if ( stream_name = = NULL ) {
return false ;
}
ret = SMB_VFS_STAT ( handle - > conn , stream_name ) ;
if ( ret ! = 0 ) {
return false ;
}
status = SMB_VFS_CREATE_FILE (
handle - > conn , /* conn */
NULL , /* req */
0 , /* root_dir_fid */
stream_name , /* fname */
FILE_READ_DATA , /* access_mask */
( FILE_SHARE_READ | FILE_SHARE_WRITE | /* share_access */
FILE_SHARE_DELETE ) ,
FILE_OPEN , /* create_disposition*/
0 , /* create_options */
0 , /* file_attributes */
INTERNAL_OPEN_ONLY , /* oplock_request */
NULL , /* lease */
0 , /* allocation_size */
0 , /* private_flags */
NULL , /* sd */
NULL , /* ea_list */
& fsp , /* result */
NULL , /* pinfo */
NULL , NULL ) ; /* create context */
TALLOC_FREE ( stream_name ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
return false ;
}
nread = SMB_VFS_PREAD ( fsp , & buf [ 0 ] , AFP_INFO_SIZE , 0 ) ;
if ( nread ! = AFP_INFO_SIZE ) {
DBG_ERR ( " short read [%s] [%zd/%d] \n " ,
smb_fname_str_dbg ( stream_name ) , nread , AFP_INFO_SIZE ) ;
ok = false ;
goto fail ;
}
memcpy ( & ai - > afpi_FinderInfo [ 0 ] , & buf [ AFP_OFF_FinderInfo ] ,
AFP_FinderSize ) ;
ok = true ;
fail :
if ( fsp ! = NULL ) {
close_file ( NULL , fsp , NORMAL_CLOSE ) ;
}
return ok ;
2016-12-01 19:04:35 +03:00
}
static bool readdir_attr_meta_finderi_netatalk (
struct vfs_handle_struct * handle ,
const struct smb_filename * smb_fname ,
AfpInfo * ai )
{
struct adouble * ad = NULL ;
char * p = NULL ;
2017-05-25 21:38:26 +03:00
ad = ad_get ( talloc_tos ( ) , handle , smb_fname , ADOUBLE_META ) ;
2016-12-01 19:04:35 +03:00
if ( ad = = NULL ) {
return false ;
}
p = ad_get_entry ( ad , ADEID_FINDERI ) ;
if ( p = = NULL ) {
DBG_ERR ( " No ADEID_FINDERI for [%s] \n " , smb_fname - > base_name ) ;
TALLOC_FREE ( ad ) ;
return false ;
}
memcpy ( & ai - > afpi_FinderInfo [ 0 ] , p , AFP_FinderSize ) ;
TALLOC_FREE ( ad ) ;
return true ;
}
static bool readdir_attr_meta_finderi ( struct vfs_handle_struct * handle ,
const struct smb_filename * smb_fname ,
struct readdir_attr_data * attr_data )
{
struct fruit_config_data * config = NULL ;
uint32_t date_added ;
AfpInfo ai = { 0 } ;
bool ok ;
SMB_VFS_HANDLE_GET_DATA ( handle , config ,
struct fruit_config_data ,
return false ) ;
switch ( config - > meta ) {
case FRUIT_META_NETATALK :
ok = readdir_attr_meta_finderi_netatalk (
handle , smb_fname , & ai ) ;
break ;
case FRUIT_META_STREAM :
ok = readdir_attr_meta_finderi_stream (
handle , smb_fname , & ai ) ;
break ;
default :
DBG_ERR ( " Unexpected meta config [%d] \n " , config - > meta ) ;
return false ;
}
if ( ! ok ) {
/* Don't bother with errors, it's likely ENOENT */
return true ;
}
if ( S_ISREG ( smb_fname - > st . st_ex_mode ) ) {
/* finder_type */
memcpy ( & attr_data - > attr_data . aapl . finder_info [ 0 ] ,
& ai . afpi_FinderInfo [ 0 ] , 4 ) ;
/* finder_creator */
memcpy ( & attr_data - > attr_data . aapl . finder_info [ 0 ] + 4 ,
& ai . afpi_FinderInfo [ 4 ] , 4 ) ;
}
/* finder_flags */
memcpy ( & attr_data - > attr_data . aapl . finder_info [ 0 ] + 8 ,
& ai . afpi_FinderInfo [ 8 ] , 2 ) ;
/* finder_ext_flags */
memcpy ( & attr_data - > attr_data . aapl . finder_info [ 0 ] + 10 ,
& ai . afpi_FinderInfo [ 24 ] , 2 ) ;
/* creation date */
date_added = convert_time_t_to_uint32_t (
smb_fname - > st . st_ex_btime . tv_sec - AD_DATE_DELTA ) ;
RSIVAL ( & attr_data - > attr_data . aapl . finder_info [ 0 ] , 12 , date_added ) ;
return true ;
}
2016-12-02 19:00:03 +03:00
static uint64_t readdir_attr_rfork_size_adouble (
struct vfs_handle_struct * handle ,
const struct smb_filename * smb_fname )
{
struct adouble * ad = NULL ;
uint64_t rfork_size ;
2017-05-25 21:38:26 +03:00
ad = ad_get ( talloc_tos ( ) , handle , smb_fname ,
2016-12-02 19:00:03 +03:00
ADOUBLE_RSRC ) ;
if ( ad = = NULL ) {
return 0 ;
}
rfork_size = ad_getentrylen ( ad , ADEID_RFORK ) ;
TALLOC_FREE ( ad ) ;
return rfork_size ;
}
static uint64_t readdir_attr_rfork_size_stream (
struct vfs_handle_struct * handle ,
const struct smb_filename * smb_fname )
{
struct smb_filename * stream_name = NULL ;
int ret ;
uint64_t rfork_size ;
stream_name = synthetic_smb_fname ( talloc_tos ( ) ,
smb_fname - > base_name ,
AFPRESOURCE_STREAM_NAME ,
NULL , 0 ) ;
if ( stream_name = = NULL ) {
return 0 ;
}
ret = SMB_VFS_STAT ( handle - > conn , stream_name ) ;
if ( ret ! = 0 ) {
TALLOC_FREE ( stream_name ) ;
return 0 ;
}
rfork_size = stream_name - > st . st_ex_size ;
TALLOC_FREE ( stream_name ) ;
return rfork_size ;
}
static uint64_t readdir_attr_rfork_size ( struct vfs_handle_struct * handle ,
const struct smb_filename * smb_fname )
{
struct fruit_config_data * config = NULL ;
uint64_t rfork_size ;
SMB_VFS_HANDLE_GET_DATA ( handle , config ,
struct fruit_config_data ,
return 0 ) ;
switch ( config - > rsrc ) {
case FRUIT_RSRC_ADFILE :
case FRUIT_RSRC_XATTR :
rfork_size = readdir_attr_rfork_size_adouble ( handle ,
smb_fname ) ;
break ;
case FRUIT_META_STREAM :
rfork_size = readdir_attr_rfork_size_stream ( handle ,
smb_fname ) ;
break ;
default :
DBG_ERR ( " Unexpected rsrc config [%d] \n " , config - > rsrc ) ;
rfork_size = 0 ;
break ;
}
return rfork_size ;
}
2014-11-26 20:11:17 +03:00
static NTSTATUS readdir_attr_macmeta ( struct vfs_handle_struct * handle ,
const struct smb_filename * smb_fname ,
struct readdir_attr_data * attr_data )
{
NTSTATUS status = NT_STATUS_OK ;
struct fruit_config_data * config = NULL ;
2016-12-01 19:04:35 +03:00
bool ok ;
2014-11-26 20:11:17 +03:00
SMB_VFS_HANDLE_GET_DATA ( handle , config ,
struct fruit_config_data ,
return NT_STATUS_UNSUCCESSFUL ) ;
/* Ensure we return a default value in the creation_date field */
RSIVAL ( & attr_data - > attr_data . aapl . finder_info , 12 , AD_DATE_START ) ;
/*
* Resource fork length
*/
if ( config - > readdir_attr_rsize ) {
2016-12-02 19:00:03 +03:00
uint64_t rfork_size ;
rfork_size = readdir_attr_rfork_size ( handle , smb_fname ) ;
attr_data - > attr_data . aapl . rfork_size = rfork_size ;
2014-11-26 20:11:17 +03:00
}
/*
* FinderInfo
*/
if ( config - > readdir_attr_finder_info ) {
2016-12-01 19:04:35 +03:00
ok = readdir_attr_meta_finderi ( handle , smb_fname , attr_data ) ;
if ( ! ok ) {
status = NT_STATUS_INTERNAL_ERROR ;
2014-11-26 20:11:17 +03:00
}
}
return status ;
}
2018-03-15 19:52:30 +03:00
static NTSTATUS remove_virtual_nfs_aces ( struct security_descriptor * psd )
{
NTSTATUS status ;
uint32_t i ;
if ( psd - > dacl = = NULL ) {
return NT_STATUS_OK ;
}
for ( i = 0 ; i < psd - > dacl - > num_aces ; i + + ) {
/* MS NFS style mode/uid/gid */
2018-03-20 01:46:41 +03:00
int cmp = dom_sid_compare_domain (
2018-03-15 19:52:30 +03:00
& global_sid_Unix_NFS ,
2018-03-20 01:46:41 +03:00
& psd - > dacl - > aces [ i ] . trustee ) ;
if ( cmp ! = 0 ) {
2018-03-15 19:52:30 +03:00
/* Normal ACE entry. */
continue ;
}
/*
* security_descriptor_dacl_del ( )
* * must * return NT_STATUS_OK as we know
* we have something to remove .
*/
status = security_descriptor_dacl_del ( psd ,
& psd - > dacl - > aces [ i ] . trustee ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
DBG_WARNING ( " failed to remove MS NFS style ACE: %s \n " ,
nt_errstr ( status ) ) ;
return status ;
}
/*
* security_descriptor_dacl_del ( ) may delete more
* then one entry subsequent to this one if the
* SID matches , but we only need to ensure that
* we stay looking at the same element in the array .
*/
i - - ;
}
return NT_STATUS_OK ;
}
2014-11-26 20:11:17 +03:00
/* Search MS NFS style ACE with UNIX mode */
static NTSTATUS check_ms_nfs ( vfs_handle_struct * handle ,
files_struct * fsp ,
2018-03-03 00:53:55 +03:00
struct security_descriptor * psd ,
2014-11-26 20:11:17 +03:00
mode_t * pmode ,
bool * pdo_chmod )
{
2016-05-19 19:42:04 +03:00
uint32_t i ;
2014-11-26 20:11:17 +03:00
struct fruit_config_data * config = NULL ;
* pdo_chmod = false ;
SMB_VFS_HANDLE_GET_DATA ( handle , config ,
struct fruit_config_data ,
return NT_STATUS_UNSUCCESSFUL ) ;
2017-07-12 10:33:59 +03:00
if ( ! global_fruit_config . nego_aapl ) {
return NT_STATUS_OK ;
}
2014-11-26 20:11:17 +03:00
if ( psd - > dacl = = NULL | | ! config - > unix_info_enabled ) {
return NT_STATUS_OK ;
}
for ( i = 0 ; i < psd - > dacl - > num_aces ; i + + ) {
if ( dom_sid_compare_domain (
& global_sid_Unix_NFS_Mode ,
& psd - > dacl - > aces [ i ] . trustee ) = = 0 ) {
* pmode = ( mode_t ) psd - > dacl - > aces [ i ] . trustee . sub_auths [ 2 ] ;
* pmode & = ( S_IRWXU | S_IRWXG | S_IRWXO ) ;
* pdo_chmod = true ;
DEBUG ( 10 , ( " MS NFS chmod request %s, %04o \n " ,
2015-05-06 17:25:51 +03:00
fsp_str_dbg ( fsp ) , ( unsigned ) ( * pmode ) ) ) ;
2014-11-26 20:11:17 +03:00
break ;
}
}
2018-03-03 00:53:55 +03:00
/*
* Remove any incoming virtual ACE entries generated by
* fruit_fget_nt_acl ( ) .
*/
2018-03-15 19:54:41 +03:00
return remove_virtual_nfs_aces ( psd ) ;
2014-11-26 20:11:17 +03:00
}
2014-06-23 18:59:45 +04:00
/****************************************************************************
* VFS ops
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
static int fruit_connect ( vfs_handle_struct * handle ,
const char * service ,
const char * user )
{
int rc ;
char * list = NULL , * newlist = NULL ;
struct fruit_config_data * config ;
DEBUG ( 10 , ( " fruit_connect \n " ) ) ;
rc = SMB_VFS_NEXT_CONNECT ( handle , service , user ) ;
if ( rc < 0 ) {
return rc ;
}
rc = init_fruit_config ( handle ) ;
if ( rc ! = 0 ) {
return rc ;
}
SMB_VFS_HANDLE_GET_DATA ( handle , config ,
struct fruit_config_data , return - 1 ) ;
2015-05-09 09:31:24 +03:00
if ( config - > veto_appledouble ) {
list = lp_veto_files ( talloc_tos ( ) , SNUM ( handle - > conn ) ) ;
if ( list ) {
if ( strstr ( list , " / " ADOUBLE_NAME_PREFIX " */ " ) = = NULL ) {
newlist = talloc_asprintf (
list ,
" %s/ " ADOUBLE_NAME_PREFIX " */ " ,
list ) ;
lp_do_parameter ( SNUM ( handle - > conn ) ,
" veto files " ,
newlist ) ;
}
} else {
lp_do_parameter ( SNUM ( handle - > conn ) ,
" veto files " ,
" / " ADOUBLE_NAME_PREFIX " */ " ) ;
}
TALLOC_FREE ( list ) ;
}
2014-06-23 18:59:45 +04:00
if ( config - > encoding = = FRUIT_ENC_NATIVE ) {
2017-10-10 20:13:36 +03:00
lp_do_parameter ( SNUM ( handle - > conn ) ,
" catia:mappings " ,
fruit_catia_maps ) ;
2014-06-23 18:59:45 +04:00
}
2016-11-14 21:14:44 +03:00
if ( config - > time_machine ) {
DBG_NOTICE ( " Enabling durable handles for Time Machine "
" support on [%s] \n " , service ) ;
lp_do_parameter ( SNUM ( handle - > conn ) , " durable handles " , " yes " ) ;
lp_do_parameter ( SNUM ( handle - > conn ) , " kernel oplocks " , " no " ) ;
lp_do_parameter ( SNUM ( handle - > conn ) , " kernel share modes " , " no " ) ;
if ( ! lp_strict_sync ( SNUM ( handle - > conn ) ) ) {
DBG_WARNING ( " Time Machine without strict sync is not "
" recommended! \n " ) ;
}
lp_do_parameter ( SNUM ( handle - > conn ) , " posix locking " , " no " ) ;
}
2014-06-23 18:59:45 +04:00
return rc ;
}
2016-11-29 18:56:00 +03:00
static int fruit_open_meta_stream ( vfs_handle_struct * handle ,
struct smb_filename * smb_fname ,
files_struct * fsp ,
int flags ,
mode_t mode )
{
2016-12-09 19:01:37 +03:00
AfpInfo * ai = NULL ;
char afpinfo_buf [ AFP_INFO_SIZE ] ;
ssize_t len , written ;
int hostfd = - 1 ;
int rc = - 1 ;
hostfd = SMB_VFS_NEXT_OPEN ( handle , smb_fname , fsp , flags , mode ) ;
if ( hostfd = = - 1 ) {
return - 1 ;
}
if ( ! ( flags & ( O_CREAT | O_TRUNC ) ) ) {
return hostfd ;
}
ai = afpinfo_new ( talloc_tos ( ) ) ;
if ( ai = = NULL ) {
rc = - 1 ;
goto fail ;
}
len = afpinfo_pack ( ai , afpinfo_buf ) ;
if ( len ! = AFP_INFO_SIZE ) {
rc = - 1 ;
goto fail ;
}
/* Set fd, needed in SMB_VFS_NEXT_PWRITE() */
fsp - > fh - > fd = hostfd ;
written = SMB_VFS_NEXT_PWRITE ( handle , fsp , afpinfo_buf ,
AFP_INFO_SIZE , 0 ) ;
fsp - > fh - > fd = - 1 ;
if ( written ! = AFP_INFO_SIZE ) {
DBG_ERR ( " bad write [%zd/%d] \n " , written , AFP_INFO_SIZE ) ;
rc = - 1 ;
goto fail ;
}
rc = 0 ;
fail :
DBG_DEBUG ( " rc=%d, fd=%d \n " , rc , hostfd ) ;
if ( rc ! = 0 ) {
int saved_errno = errno ;
if ( hostfd > = 0 ) {
fsp - > fh - > fd = hostfd ;
SMB_VFS_NEXT_CLOSE ( handle , fsp ) ;
}
hostfd = - 1 ;
errno = saved_errno ;
}
return hostfd ;
2016-11-29 18:56:00 +03:00
}
static int fruit_open_meta_netatalk ( vfs_handle_struct * handle ,
struct smb_filename * smb_fname ,
files_struct * fsp ,
int flags ,
mode_t mode )
2014-06-23 18:59:45 +04:00
{
2017-05-23 18:44:16 +03:00
int rc ;
int fakefd = - 1 ;
2014-06-23 18:59:45 +04:00
struct adouble * ad = NULL ;
2017-05-23 18:44:16 +03:00
int fds [ 2 ] ;
2014-06-23 18:59:45 +04:00
2016-12-08 22:34:55 +03:00
DBG_DEBUG ( " Path [%s] \n " , smb_fname_str_dbg ( smb_fname ) ) ;
2014-06-23 18:59:45 +04:00
/*
2017-05-23 18:44:16 +03:00
* Return a valid fd , but ensure any attempt to use it returns an error
* ( EPIPE ) . All operations on the smb_fname or the fsp will use path
* based syscalls .
2014-06-23 18:59:45 +04:00
*/
2017-05-23 18:44:16 +03:00
rc = pipe ( fds ) ;
if ( rc ! = 0 ) {
2014-06-23 18:59:45 +04:00
goto exit ;
}
2017-05-23 18:44:16 +03:00
fakefd = fds [ 0 ] ;
close ( fds [ 1 ] ) ;
2014-06-23 18:59:45 +04:00
if ( flags & ( O_CREAT | O_TRUNC ) ) {
/*
* The attribute does not exist or needs to be truncated ,
* create an AppleDouble EA
*/
2016-12-08 21:12:32 +03:00
ad = ad_init ( fsp , handle , ADOUBLE_META ) ;
2014-06-23 18:59:45 +04:00
if ( ad = = NULL ) {
rc = - 1 ;
goto exit ;
}
2017-05-23 18:44:16 +03:00
rc = ad_set ( ad , fsp - > fsp_name ) ;
2014-06-23 18:59:45 +04:00
if ( rc ! = 0 ) {
rc = - 1 ;
goto exit ;
}
2016-12-08 21:12:32 +03:00
TALLOC_FREE ( ad ) ;
2014-06-23 18:59:45 +04:00
}
exit :
2017-05-23 18:44:16 +03:00
DEBUG ( 10 , ( " fruit_open meta rc=%d, fd=%d \n " , rc , fakefd ) ) ;
2014-06-23 18:59:45 +04:00
if ( rc ! = 0 ) {
int saved_errno = errno ;
2017-05-23 18:44:16 +03:00
if ( fakefd > = 0 ) {
close ( fakefd ) ;
2014-06-23 18:59:45 +04:00
}
2017-05-23 18:44:16 +03:00
fakefd = - 1 ;
2014-06-23 18:59:45 +04:00
errno = saved_errno ;
}
2017-05-23 18:44:16 +03:00
return fakefd ;
2014-06-23 18:59:45 +04:00
}
2016-11-29 18:56:00 +03:00
static int fruit_open_meta ( vfs_handle_struct * handle ,
struct smb_filename * smb_fname ,
files_struct * fsp , int flags , mode_t mode )
{
2016-12-08 22:34:55 +03:00
int fd ;
2016-11-29 18:56:00 +03:00
struct fruit_config_data * config = NULL ;
2016-12-08 22:34:55 +03:00
struct fio * fio = NULL ;
2016-11-29 18:56:00 +03:00
DBG_DEBUG ( " path [%s] \n " , smb_fname_str_dbg ( smb_fname ) ) ;
SMB_VFS_HANDLE_GET_DATA ( handle , config ,
struct fruit_config_data , return - 1 ) ;
switch ( config - > meta ) {
case FRUIT_META_STREAM :
2016-12-08 22:34:55 +03:00
fd = fruit_open_meta_stream ( handle , smb_fname ,
2016-11-29 18:56:00 +03:00
fsp , flags , mode ) ;
break ;
case FRUIT_META_NETATALK :
2016-12-08 22:34:55 +03:00
fd = fruit_open_meta_netatalk ( handle , smb_fname ,
2016-11-29 18:56:00 +03:00
fsp , flags , mode ) ;
break ;
default :
DBG_ERR ( " Unexpected meta config [%d] \n " , config - > meta ) ;
return - 1 ;
}
2016-12-08 22:34:55 +03:00
DBG_DEBUG ( " path [%s] fd [%d] \n " , smb_fname_str_dbg ( smb_fname ) , fd ) ;
if ( fd = = - 1 ) {
return - 1 ;
}
2017-12-05 15:21:04 +03:00
fio = VFS_ADD_FSP_EXTENSION ( handle , fsp , struct fio , NULL ) ;
2016-12-08 22:34:55 +03:00
fio - > type = ADOUBLE_META ;
fio - > config = config ;
return fd ;
2016-11-29 18:56:00 +03:00
}
2016-12-02 12:46:55 +03:00
static int fruit_open_rsrc_adouble ( vfs_handle_struct * handle ,
struct smb_filename * smb_fname ,
files_struct * fsp ,
int flags ,
mode_t mode )
2014-06-23 18:59:45 +04:00
{
int rc = 0 ;
struct adouble * ad = NULL ;
struct smb_filename * smb_fname_base = NULL ;
2016-12-08 22:34:55 +03:00
struct fruit_config_data * config = NULL ;
2014-06-23 18:59:45 +04:00
int hostfd = - 1 ;
2016-12-08 22:34:55 +03:00
SMB_VFS_HANDLE_GET_DATA ( handle , config ,
struct fruit_config_data , return - 1 ) ;
2017-02-07 17:01:53 +03:00
if ( ( ! ( flags & O_CREAT ) ) & &
S_ISDIR ( fsp - > base_fsp - > fsp_name - > st . st_ex_mode ) )
{
2014-06-23 18:59:45 +04:00
/* sorry, but directories don't habe a resource fork */
rc = - 1 ;
goto exit ;
}
2017-05-25 21:38:26 +03:00
rc = adouble_path ( talloc_tos ( ) , smb_fname , & smb_fname_base ) ;
2014-06-23 18:59:45 +04:00
if ( rc ! = 0 ) {
goto exit ;
}
2018-10-22 13:32:09 +03:00
/* We always need read/write access for the metadata header too */
flags & = ~ ( O_RDONLY | O_WRONLY ) ;
flags | = O_RDWR ;
2014-06-23 18:59:45 +04:00
2017-02-08 21:15:38 +03:00
hostfd = SMB_VFS_NEXT_OPEN ( handle , smb_fname_base , fsp ,
flags , mode ) ;
2014-06-23 18:59:45 +04:00
if ( hostfd = = - 1 ) {
rc = - 1 ;
goto exit ;
}
if ( flags & ( O_CREAT | O_TRUNC ) ) {
2016-12-08 21:12:32 +03:00
ad = ad_init ( fsp , handle , ADOUBLE_RSRC ) ;
2014-06-23 18:59:45 +04:00
if ( ad = = NULL ) {
rc = - 1 ;
goto exit ;
}
2016-12-08 21:12:32 +03:00
fsp - > fh - > fd = hostfd ;
rc = ad_fset ( ad , fsp ) ;
fsp - > fh - > fd = - 1 ;
2014-06-23 18:59:45 +04:00
if ( rc ! = 0 ) {
rc = - 1 ;
goto exit ;
}
2016-12-08 21:12:32 +03:00
TALLOC_FREE ( ad ) ;
2014-06-23 18:59:45 +04:00
}
exit :
TALLOC_FREE ( smb_fname_base ) ;
DEBUG ( 10 , ( " fruit_open resource fork: rc=%d, fd=%d \n " , rc , hostfd ) ) ;
if ( rc ! = 0 ) {
int saved_errno = errno ;
if ( hostfd > = 0 ) {
/*
* BUGBUGBUG - - we would need to call
* fd_close_posix here , but we don ' t have a
* full fsp yet
*/
fsp - > fh - > fd = hostfd ;
SMB_VFS_CLOSE ( fsp ) ;
}
hostfd = - 1 ;
errno = saved_errno ;
}
return hostfd ;
}
2016-12-02 12:46:55 +03:00
static int fruit_open_rsrc_xattr ( vfs_handle_struct * handle ,
struct smb_filename * smb_fname ,
files_struct * fsp ,
int flags ,
mode_t mode )
{
# ifdef HAVE_ATTROPEN
int fd = - 1 ;
fd = attropen ( smb_fname - > base_name ,
2016-12-08 22:34:55 +03:00
AFPRESOURCE_EA_NETATALK ,
flags ,
mode ) ;
2016-12-02 12:46:55 +03:00
if ( fd = = - 1 ) {
return - 1 ;
}
return fd ;
# else
errno = ENOSYS ;
return - 1 ;
# endif
}
static int fruit_open_rsrc ( vfs_handle_struct * handle ,
struct smb_filename * smb_fname ,
files_struct * fsp , int flags , mode_t mode )
{
int fd ;
struct fruit_config_data * config = NULL ;
2016-12-08 22:34:55 +03:00
struct fio * fio = NULL ;
2016-12-02 12:46:55 +03:00
DBG_DEBUG ( " Path [%s] \n " , smb_fname_str_dbg ( smb_fname ) ) ;
SMB_VFS_HANDLE_GET_DATA ( handle , config ,
struct fruit_config_data , return - 1 ) ;
switch ( config - > rsrc ) {
case FRUIT_RSRC_STREAM :
fd = SMB_VFS_NEXT_OPEN ( handle , smb_fname , fsp , flags , mode ) ;
break ;
case FRUIT_RSRC_ADFILE :
fd = fruit_open_rsrc_adouble ( handle , smb_fname ,
fsp , flags , mode ) ;
break ;
case FRUIT_RSRC_XATTR :
fd = fruit_open_rsrc_xattr ( handle , smb_fname ,
fsp , flags , mode ) ;
break ;
default :
DBG_ERR ( " Unexpected rsrc config [%d] \n " , config - > rsrc ) ;
return - 1 ;
}
DBG_DEBUG ( " Path [%s] fd [%d] \n " , smb_fname_str_dbg ( smb_fname ) , fd ) ;
2016-12-08 22:34:55 +03:00
if ( fd = = - 1 ) {
return - 1 ;
}
2017-12-05 15:21:04 +03:00
fio = VFS_ADD_FSP_EXTENSION ( handle , fsp , struct fio , NULL ) ;
2016-12-08 22:34:55 +03:00
fio - > type = ADOUBLE_RSRC ;
fio - > config = config ;
2016-12-02 12:46:55 +03:00
return fd ;
}
2014-06-23 18:59:45 +04:00
static int fruit_open ( vfs_handle_struct * handle ,
struct smb_filename * smb_fname ,
files_struct * fsp , int flags , mode_t mode )
{
2016-12-08 22:34:55 +03:00
int fd ;
DBG_DEBUG ( " Path [%s] \n " , smb_fname_str_dbg ( smb_fname ) ) ;
2014-06-23 18:59:45 +04:00
if ( ! is_ntfs_stream_smb_fname ( smb_fname ) ) {
return SMB_VFS_NEXT_OPEN ( handle , smb_fname , fsp , flags , mode ) ;
}
if ( is_afpinfo_stream ( smb_fname ) ) {
2016-12-08 22:34:55 +03:00
fd = fruit_open_meta ( handle , smb_fname , fsp , flags , mode ) ;
2014-06-23 18:59:45 +04:00
} else if ( is_afpresource_stream ( smb_fname ) ) {
2016-12-08 22:34:55 +03:00
fd = fruit_open_rsrc ( handle , smb_fname , fsp , flags , mode ) ;
} else {
fd = SMB_VFS_NEXT_OPEN ( handle , smb_fname , fsp , flags , mode ) ;
2014-06-23 18:59:45 +04:00
}
2016-12-08 22:34:55 +03:00
DBG_DEBUG ( " Path [%s] fd [%d] \n " , smb_fname_str_dbg ( smb_fname ) , fd ) ;
return fd ;
2014-06-23 18:59:45 +04:00
}
static int fruit_rename ( struct vfs_handle_struct * handle ,
const struct smb_filename * smb_fname_src ,
const struct smb_filename * smb_fname_dst )
{
int rc = - 1 ;
struct fruit_config_data * config = NULL ;
2016-12-02 10:47:36 +03:00
struct smb_filename * src_adp_smb_fname = NULL ;
struct smb_filename * dst_adp_smb_fname = NULL ;
2014-06-23 18:59:45 +04:00
2016-12-02 10:47:36 +03:00
SMB_VFS_HANDLE_GET_DATA ( handle , config ,
struct fruit_config_data , return - 1 ) ;
2014-06-23 18:59:45 +04:00
2016-12-02 10:47:36 +03:00
if ( ! VALID_STAT ( smb_fname_src - > st ) ) {
DBG_ERR ( " Need valid stat for [%s] \n " ,
smb_fname_str_dbg ( smb_fname_src ) ) ;
return - 1 ;
2014-06-23 18:59:45 +04:00
}
2016-12-02 10:47:36 +03:00
rc = SMB_VFS_NEXT_RENAME ( handle , smb_fname_src , smb_fname_dst ) ;
if ( rc ! = 0 ) {
return - 1 ;
}
2014-06-23 18:59:45 +04:00
2016-12-02 10:47:36 +03:00
if ( ( config - > rsrc ! = FRUIT_RSRC_ADFILE ) | |
( ! S_ISREG ( smb_fname_src - > st . st_ex_mode ) ) )
{
return 0 ;
2014-06-23 18:59:45 +04:00
}
2017-05-25 21:38:26 +03:00
rc = adouble_path ( talloc_tos ( ) , smb_fname_src , & src_adp_smb_fname ) ;
2014-06-23 18:59:45 +04:00
if ( rc ! = 0 ) {
goto done ;
}
2016-12-02 10:47:36 +03:00
2017-05-25 21:38:26 +03:00
rc = adouble_path ( talloc_tos ( ) , smb_fname_dst , & dst_adp_smb_fname ) ;
2014-06-23 18:59:45 +04:00
if ( rc ! = 0 ) {
goto done ;
}
2016-12-02 10:47:36 +03:00
DBG_DEBUG ( " %s -> %s \n " ,
smb_fname_str_dbg ( src_adp_smb_fname ) ,
smb_fname_str_dbg ( dst_adp_smb_fname ) ) ;
2014-06-23 18:59:45 +04:00
2016-12-02 10:47:36 +03:00
rc = SMB_VFS_NEXT_RENAME ( handle , src_adp_smb_fname , dst_adp_smb_fname ) ;
2014-06-23 18:59:45 +04:00
if ( errno = = ENOENT ) {
rc = 0 ;
}
done :
2016-12-02 10:47:36 +03:00
TALLOC_FREE ( src_adp_smb_fname ) ;
TALLOC_FREE ( dst_adp_smb_fname ) ;
2014-06-23 18:59:45 +04:00
return rc ;
}
2016-12-02 11:00:31 +03:00
static int fruit_unlink_meta_stream ( vfs_handle_struct * handle ,
const struct smb_filename * smb_fname )
{
return SMB_VFS_NEXT_UNLINK ( handle , smb_fname ) ;
}
static int fruit_unlink_meta_netatalk ( vfs_handle_struct * handle ,
const struct smb_filename * smb_fname )
{
return SMB_VFS_REMOVEXATTR ( handle - > conn ,
2017-05-24 21:35:50 +03:00
smb_fname ,
2016-12-02 11:00:31 +03:00
AFPINFO_EA_NETATALK ) ;
}
static int fruit_unlink_meta ( vfs_handle_struct * handle ,
const struct smb_filename * smb_fname )
2014-06-23 18:59:45 +04:00
{
struct fruit_config_data * config = NULL ;
2016-12-02 11:00:31 +03:00
int rc ;
2014-06-23 18:59:45 +04:00
SMB_VFS_HANDLE_GET_DATA ( handle , config ,
struct fruit_config_data , return - 1 ) ;
2016-12-02 11:00:31 +03:00
switch ( config - > meta ) {
case FRUIT_META_STREAM :
rc = fruit_unlink_meta_stream ( handle , smb_fname ) ;
break ;
2015-08-25 18:06:52 +03:00
2016-12-02 11:00:31 +03:00
case FRUIT_META_NETATALK :
rc = fruit_unlink_meta_netatalk ( handle , smb_fname ) ;
break ;
2015-08-25 18:06:52 +03:00
2016-12-02 11:00:31 +03:00
default :
DBG_ERR ( " Unsupported meta config [%d] \n " , config - > meta ) ;
return - 1 ;
}
return rc ;
}
static int fruit_unlink_rsrc_stream ( vfs_handle_struct * handle ,
const struct smb_filename * smb_fname ,
bool force_unlink )
{
int ret ;
if ( ! force_unlink ) {
struct smb_filename * smb_fname_cp = NULL ;
off_t size ;
smb_fname_cp = cp_smb_filename ( talloc_tos ( ) , smb_fname ) ;
if ( smb_fname_cp = = NULL ) {
return - 1 ;
2015-08-25 18:06:52 +03:00
}
/*
* 0 byte resource fork streams are not listed by
* vfs_streaminfo , as a result stream cleanup / deletion of file
* deletion doesn ' t remove the resourcefork stream .
*/
2016-12-02 11:00:31 +03:00
ret = SMB_VFS_NEXT_STAT ( handle , smb_fname_cp ) ;
if ( ret ! = 0 ) {
TALLOC_FREE ( smb_fname_cp ) ;
DBG_ERR ( " stat [%s] failed [%s] \n " ,
smb_fname_str_dbg ( smb_fname_cp ) , strerror ( errno ) ) ;
2015-08-25 18:06:52 +03:00
return - 1 ;
}
2016-12-02 11:00:31 +03:00
size = smb_fname_cp - > st . st_ex_size ;
TALLOC_FREE ( smb_fname_cp ) ;
if ( size > 0 ) {
/* OS X ignores resource fork stream delete requests */
return 0 ;
2015-08-25 18:06:52 +03:00
}
2016-12-02 11:00:31 +03:00
}
2015-08-25 18:06:52 +03:00
2016-12-02 11:00:31 +03:00
ret = SMB_VFS_NEXT_UNLINK ( handle , smb_fname ) ;
if ( ( ret ! = 0 ) & & ( errno = = ENOENT ) & & force_unlink ) {
ret = 0 ;
2015-08-25 18:06:52 +03:00
}
2016-12-02 11:00:31 +03:00
return ret ;
}
static int fruit_unlink_rsrc_adouble ( vfs_handle_struct * handle ,
const struct smb_filename * smb_fname ,
bool force_unlink )
{
int rc ;
struct adouble * ad = NULL ;
struct smb_filename * adp_smb_fname = NULL ;
if ( ! force_unlink ) {
2017-05-25 21:38:26 +03:00
ad = ad_get ( talloc_tos ( ) , handle , smb_fname ,
2016-12-02 11:00:31 +03:00
ADOUBLE_RSRC ) ;
if ( ad = = NULL ) {
errno = ENOENT ;
return - 1 ;
2014-06-23 18:59:45 +04:00
}
2015-08-25 18:06:52 +03:00
2016-12-02 11:00:31 +03:00
/*
* 0 byte resource fork streams are not listed by
* vfs_streaminfo , as a result stream cleanup / deletion of file
* deletion doesn ' t remove the resourcefork stream .
*/
if ( ad_getentrylen ( ad , ADEID_RFORK ) > 0 ) {
/* OS X ignores resource fork stream delete requests */
TALLOC_FREE ( ad ) ;
return 0 ;
}
TALLOC_FREE ( ad ) ;
2015-08-25 18:06:52 +03:00
}
2017-05-25 21:38:26 +03:00
rc = adouble_path ( talloc_tos ( ) , smb_fname , & adp_smb_fname ) ;
2016-12-02 11:00:31 +03:00
if ( rc ! = 0 ) {
return - 1 ;
2014-06-23 18:59:45 +04:00
}
2016-12-02 11:00:31 +03:00
rc = SMB_VFS_NEXT_UNLINK ( handle , adp_smb_fname ) ;
TALLOC_FREE ( adp_smb_fname ) ;
if ( ( rc ! = 0 ) & & ( errno = = ENOENT ) & & force_unlink ) {
rc = 0 ;
}
2015-08-25 18:06:52 +03:00
2016-12-02 11:00:31 +03:00
return rc ;
}
2015-08-25 18:06:52 +03:00
2016-12-02 11:00:31 +03:00
static int fruit_unlink_rsrc_xattr ( vfs_handle_struct * handle ,
const struct smb_filename * smb_fname ,
bool force_unlink )
{
/*
* OS X ignores resource fork stream delete requests , so nothing to do
* here . Removing the file will remove the xattr anyway , so we don ' t
* have to take care of removing 0 byte resource forks that could be
* left behind .
*/
2015-08-25 18:06:52 +03:00
return 0 ;
2014-06-23 18:59:45 +04:00
}
2016-12-02 11:00:31 +03:00
static int fruit_unlink_rsrc ( vfs_handle_struct * handle ,
const struct smb_filename * smb_fname ,
bool force_unlink )
{
struct fruit_config_data * config = NULL ;
int rc ;
SMB_VFS_HANDLE_GET_DATA ( handle , config ,
struct fruit_config_data , return - 1 ) ;
switch ( config - > rsrc ) {
case FRUIT_RSRC_STREAM :
rc = fruit_unlink_rsrc_stream ( handle , smb_fname , force_unlink ) ;
break ;
case FRUIT_RSRC_ADFILE :
rc = fruit_unlink_rsrc_adouble ( handle , smb_fname , force_unlink ) ;
break ;
case FRUIT_RSRC_XATTR :
rc = fruit_unlink_rsrc_xattr ( handle , smb_fname , force_unlink ) ;
break ;
default :
DBG_ERR ( " Unsupported rsrc config [%d] \n " , config - > rsrc ) ;
return - 1 ;
}
return rc ;
}
static int fruit_unlink ( vfs_handle_struct * handle ,
const struct smb_filename * smb_fname )
{
int rc ;
struct fruit_config_data * config = NULL ;
struct smb_filename * rsrc_smb_fname = NULL ;
SMB_VFS_HANDLE_GET_DATA ( handle , config ,
struct fruit_config_data , return - 1 ) ;
if ( is_afpinfo_stream ( smb_fname ) ) {
return fruit_unlink_meta ( handle , smb_fname ) ;
} else if ( is_afpresource_stream ( smb_fname ) ) {
return fruit_unlink_rsrc ( handle , smb_fname , false ) ;
} if ( is_ntfs_stream_smb_fname ( smb_fname ) ) {
return SMB_VFS_NEXT_UNLINK ( handle , smb_fname ) ;
}
/*
* A request to delete the base file . Because 0 byte resource
* fork streams are not listed by fruit_streaminfo ,
* delete_all_streams ( ) can ' t remove 0 byte resource fork
* streams , so we have to cleanup this here .
*/
rsrc_smb_fname = synthetic_smb_fname ( talloc_tos ( ) ,
smb_fname - > base_name ,
AFPRESOURCE_STREAM_NAME ,
NULL ,
smb_fname - > flags ) ;
if ( rsrc_smb_fname = = NULL ) {
return - 1 ;
}
rc = fruit_unlink_rsrc ( handle , rsrc_smb_fname , true ) ;
if ( ( rc ! = 0 ) & & ( errno ! = ENOENT ) ) {
DBG_ERR ( " Forced unlink of [%s] failed [%s] \n " ,
smb_fname_str_dbg ( rsrc_smb_fname ) , strerror ( errno ) ) ;
TALLOC_FREE ( rsrc_smb_fname ) ;
return - 1 ;
}
TALLOC_FREE ( rsrc_smb_fname ) ;
return SMB_VFS_NEXT_UNLINK ( handle , smb_fname ) ;
}
2014-06-23 18:59:45 +04:00
static int fruit_chmod ( vfs_handle_struct * handle ,
2016-03-02 03:20:25 +03:00
const struct smb_filename * smb_fname ,
2014-06-23 18:59:45 +04:00
mode_t mode )
{
int rc = - 1 ;
struct fruit_config_data * config = NULL ;
2016-03-02 03:20:25 +03:00
struct smb_filename * smb_fname_adp = NULL ;
2014-06-23 18:59:45 +04:00
2016-03-02 03:20:25 +03:00
rc = SMB_VFS_NEXT_CHMOD ( handle , smb_fname , mode ) ;
2014-06-23 18:59:45 +04:00
if ( rc ! = 0 ) {
return rc ;
}
SMB_VFS_HANDLE_GET_DATA ( handle , config ,
struct fruit_config_data , return - 1 ) ;
2016-12-02 11:02:27 +03:00
if ( config - > rsrc ! = FRUIT_RSRC_ADFILE ) {
2014-06-23 18:59:45 +04:00
return 0 ;
}
2016-12-02 11:02:27 +03:00
if ( ! VALID_STAT ( smb_fname - > st ) ) {
return 0 ;
}
if ( ! S_ISREG ( smb_fname - > st . st_ex_mode ) ) {
return 0 ;
2014-06-23 18:59:45 +04:00
}
2017-05-25 21:38:26 +03:00
rc = adouble_path ( talloc_tos ( ) , smb_fname , & smb_fname_adp ) ;
2014-06-23 18:59:45 +04:00
if ( rc ! = 0 ) {
return - 1 ;
}
2017-05-25 21:38:26 +03:00
DEBUG ( 10 , ( " fruit_chmod: %s \n " , smb_fname_adp - > base_name ) ) ;
2016-03-02 03:20:25 +03:00
rc = SMB_VFS_NEXT_CHMOD ( handle , smb_fname_adp , mode ) ;
2014-06-23 18:59:45 +04:00
if ( errno = = ENOENT ) {
rc = 0 ;
}
2016-03-02 03:20:25 +03:00
TALLOC_FREE ( smb_fname_adp ) ;
2014-06-23 18:59:45 +04:00
return rc ;
}
static int fruit_chown ( vfs_handle_struct * handle ,
2016-03-03 22:54:23 +03:00
const struct smb_filename * smb_fname ,
2014-06-23 18:59:45 +04:00
uid_t uid ,
gid_t gid )
{
int rc = - 1 ;
struct fruit_config_data * config = NULL ;
2016-03-03 22:54:23 +03:00
struct smb_filename * adp_smb_fname = NULL ;
2014-06-23 18:59:45 +04:00
2016-03-03 22:54:23 +03:00
rc = SMB_VFS_NEXT_CHOWN ( handle , smb_fname , uid , gid ) ;
2014-06-23 18:59:45 +04:00
if ( rc ! = 0 ) {
return rc ;
}
SMB_VFS_HANDLE_GET_DATA ( handle , config ,
struct fruit_config_data , return - 1 ) ;
2016-12-02 11:04:37 +03:00
if ( config - > rsrc ! = FRUIT_RSRC_ADFILE ) {
return 0 ;
2014-06-23 18:59:45 +04:00
}
2016-12-02 11:04:37 +03:00
if ( ! VALID_STAT ( smb_fname - > st ) ) {
return 0 ;
}
if ( ! S_ISREG ( smb_fname - > st . st_ex_mode ) ) {
return 0 ;
2014-06-23 18:59:45 +04:00
}
2017-05-25 21:38:26 +03:00
rc = adouble_path ( talloc_tos ( ) , smb_fname , & adp_smb_fname ) ;
2014-06-23 18:59:45 +04:00
if ( rc ! = 0 ) {
goto done ;
}
2017-05-25 21:38:26 +03:00
DEBUG ( 10 , ( " fruit_chown: %s \n " , adp_smb_fname - > base_name ) ) ;
2016-03-03 22:54:23 +03:00
rc = SMB_VFS_NEXT_CHOWN ( handle , adp_smb_fname , uid , gid ) ;
2014-06-23 18:59:45 +04:00
if ( errno = = ENOENT ) {
rc = 0 ;
}
done :
2016-03-03 22:54:23 +03:00
TALLOC_FREE ( adp_smb_fname ) ;
2014-06-23 18:59:45 +04:00
return rc ;
}
2016-02-25 01:02:45 +03:00
static int fruit_rmdir ( struct vfs_handle_struct * handle ,
const struct smb_filename * smb_fname )
2014-06-23 18:59:45 +04:00
{
DIR * dh = NULL ;
struct dirent * de ;
struct fruit_config_data * config ;
SMB_VFS_HANDLE_GET_DATA ( handle , config ,
struct fruit_config_data , return - 1 ) ;
2016-12-09 20:22:49 +03:00
if ( config - > rsrc ! = FRUIT_RSRC_ADFILE ) {
2014-06-23 18:59:45 +04:00
goto exit_rmdir ;
}
/*
* Due to there is no way to change bDeleteVetoFiles variable
* from this module , need to clean up ourselves
*/
2016-12-09 20:24:40 +03:00
dh = SMB_VFS_OPENDIR ( handle - > conn , smb_fname , NULL , 0 ) ;
2014-06-23 18:59:45 +04:00
if ( dh = = NULL ) {
goto exit_rmdir ;
}
2016-12-09 20:24:40 +03:00
while ( ( de = SMB_VFS_READDIR ( handle - > conn , dh , NULL ) ) ! = NULL ) {
int match ;
struct adouble * ad = NULL ;
char * p = NULL ;
struct smb_filename * ad_smb_fname = NULL ;
int ret ;
match = strncmp ( de - > d_name ,
ADOUBLE_NAME_PREFIX ,
strlen ( ADOUBLE_NAME_PREFIX ) ) ;
if ( match ! = 0 ) {
continue ;
}
p = talloc_asprintf ( talloc_tos ( ) , " %s/%s " ,
smb_fname - > base_name , de - > d_name ) ;
if ( p = = NULL ) {
DBG_ERR ( " talloc_asprintf failed \n " ) ;
return - 1 ;
}
2017-05-25 21:38:26 +03:00
ad_smb_fname = synthetic_smb_fname ( talloc_tos ( ) , p ,
NULL , NULL ,
smb_fname - > flags ) ;
TALLOC_FREE ( p ) ;
if ( ad_smb_fname = = NULL ) {
DBG_ERR ( " synthetic_smb_fname failed \n " ) ;
return - 1 ;
}
2016-12-09 20:24:40 +03:00
/*
* Check whether it ' s a valid AppleDouble file , if
* yes , delete it , ignore it otherwise .
*/
2017-05-25 21:38:26 +03:00
ad = ad_get ( talloc_tos ( ) , handle , ad_smb_fname , ADOUBLE_RSRC ) ;
2016-12-09 20:24:40 +03:00
if ( ad = = NULL ) {
2017-05-25 21:38:26 +03:00
TALLOC_FREE ( ad_smb_fname ) ;
2014-06-23 18:59:45 +04:00
TALLOC_FREE ( p ) ;
2016-12-09 20:24:40 +03:00
continue ;
}
TALLOC_FREE ( ad ) ;
ret = SMB_VFS_NEXT_UNLINK ( handle , ad_smb_fname ) ;
if ( ret ! = 0 ) {
DBG_ERR ( " Deleting [%s] failed \n " ,
smb_fname_str_dbg ( ad_smb_fname ) ) ;
2014-06-23 18:59:45 +04:00
}
2017-11-03 16:56:43 +03:00
TALLOC_FREE ( ad_smb_fname ) ;
2014-06-23 18:59:45 +04:00
}
exit_rmdir :
if ( dh ) {
2017-10-13 18:08:31 +03:00
SMB_VFS_CLOSEDIR ( handle - > conn , dh ) ;
2014-06-23 18:59:45 +04:00
}
2016-02-25 01:02:45 +03:00
return SMB_VFS_NEXT_RMDIR ( handle , smb_fname ) ;
2014-06-23 18:59:45 +04:00
}
2016-12-08 22:38:17 +03:00
static ssize_t fruit_pread_meta_stream ( vfs_handle_struct * handle ,
files_struct * fsp , void * data ,
size_t n , off_t offset )
{
2016-12-11 21:10:05 +03:00
ssize_t nread ;
int ret ;
nread = SMB_VFS_NEXT_PREAD ( handle , fsp , data , n , offset ) ;
2018-08-22 16:22:08 +03:00
if ( nread = = - 1 | | nread = = n ) {
2016-12-11 21:10:05 +03:00
return nread ;
}
DBG_ERR ( " Removing [%s] after short read [%zd] \n " ,
fsp_str_dbg ( fsp ) , nread ) ;
ret = SMB_VFS_NEXT_UNLINK ( handle , fsp - > fsp_name ) ;
if ( ret ! = 0 ) {
DBG_ERR ( " Removing [%s] failed \n " , fsp_str_dbg ( fsp ) ) ;
return - 1 ;
}
errno = EINVAL ;
return - 1 ;
2016-12-08 22:38:17 +03:00
}
static ssize_t fruit_pread_meta_adouble ( vfs_handle_struct * handle ,
files_struct * fsp , void * data ,
size_t n , off_t offset )
2014-06-23 18:59:45 +04:00
{
AfpInfo * ai = NULL ;
2016-12-08 22:38:17 +03:00
struct adouble * ad = NULL ;
char afpinfo_buf [ AFP_INFO_SIZE ] ;
char * p = NULL ;
ssize_t nread ;
2014-06-23 18:59:45 +04:00
2016-12-08 22:38:17 +03:00
ai = afpinfo_new ( talloc_tos ( ) ) ;
if ( ai = = NULL ) {
return - 1 ;
}
2014-06-23 18:59:45 +04:00
2016-12-08 22:38:17 +03:00
ad = ad_fget ( talloc_tos ( ) , handle , fsp , ADOUBLE_META ) ;
if ( ad = = NULL ) {
nread = - 1 ;
goto fail ;
2014-06-23 18:59:45 +04:00
}
2016-12-08 22:38:17 +03:00
p = ad_get_entry ( ad , ADEID_FINDERI ) ;
if ( p = = NULL ) {
DBG_ERR ( " No ADEID_FINDERI for [%s] \n " , fsp_str_dbg ( fsp ) ) ;
nread = - 1 ;
goto fail ;
}
2014-06-23 18:59:45 +04:00
2016-12-08 22:38:17 +03:00
memcpy ( & ai - > afpi_FinderInfo [ 0 ] , p , ADEDLEN_FINDERI ) ;
nread = afpinfo_pack ( ai , afpinfo_buf ) ;
if ( nread ! = AFP_INFO_SIZE ) {
nread = - 1 ;
goto fail ;
}
2016-11-16 11:34:13 +03:00
2016-12-08 22:38:17 +03:00
memcpy ( data , afpinfo_buf , n ) ;
nread = n ;
2016-11-16 11:34:13 +03:00
2016-12-08 22:38:17 +03:00
fail :
TALLOC_FREE ( ai ) ;
return nread ;
}
static ssize_t fruit_pread_meta ( vfs_handle_struct * handle ,
files_struct * fsp , void * data ,
size_t n , off_t offset )
{
struct fio * fio = ( struct fio * ) VFS_FETCH_FSP_EXTENSION ( handle , fsp ) ;
ssize_t nread ;
ssize_t to_return ;
/*
* OS X has a off - by - 1 error in the offset calculation , so we ' re
* bug compatible here . It won ' t hurt , as any relevant real
* world read requests from the AFP_AfpInfo stream will be
* offset = 0 n = 60. offset is ignored anyway , see below .
*/
if ( ( offset < 0 ) | | ( offset > = AFP_INFO_SIZE + 1 ) ) {
return 0 ;
2016-11-16 11:34:13 +03:00
}
2018-05-25 13:45:35 +03:00
if ( fio = = NULL ) {
DBG_ERR ( " Failed to fetch fsp extension " ) ;
return - 1 ;
}
2016-12-08 22:38:17 +03:00
/* Yes, macOS always reads from offset 0 */
offset = 0 ;
to_return = MIN ( n , AFP_INFO_SIZE ) ;
switch ( fio - > config - > meta ) {
case FRUIT_META_STREAM :
nread = fruit_pread_meta_stream ( handle , fsp , data ,
to_return , offset ) ;
break ;
case FRUIT_META_NETATALK :
nread = fruit_pread_meta_adouble ( handle , fsp , data ,
to_return , offset ) ;
break ;
default :
DBG_ERR ( " Unexpected meta config [%d] \n " , fio - > config - > meta ) ;
return - 1 ;
}
2018-08-22 16:22:08 +03:00
if ( nread = = - 1 & & fio - > created ) {
AfpInfo * ai = NULL ;
char afpinfo_buf [ AFP_INFO_SIZE ] ;
ai = afpinfo_new ( talloc_tos ( ) ) ;
if ( ai = = NULL ) {
return - 1 ;
}
nread = afpinfo_pack ( ai , afpinfo_buf ) ;
TALLOC_FREE ( ai ) ;
if ( nread ! = AFP_INFO_SIZE ) {
return - 1 ;
}
memcpy ( data , afpinfo_buf , to_return ) ;
return to_return ;
}
2016-12-08 22:38:17 +03:00
return nread ;
}
static ssize_t fruit_pread_rsrc_stream ( vfs_handle_struct * handle ,
files_struct * fsp , void * data ,
size_t n , off_t offset )
{
return SMB_VFS_NEXT_PREAD ( handle , fsp , data , n , offset ) ;
}
static ssize_t fruit_pread_rsrc_xattr ( vfs_handle_struct * handle ,
files_struct * fsp , void * data ,
size_t n , off_t offset )
{
return SMB_VFS_NEXT_PREAD ( handle , fsp , data , n , offset ) ;
}
static ssize_t fruit_pread_rsrc_adouble ( vfs_handle_struct * handle ,
files_struct * fsp , void * data ,
size_t n , off_t offset )
{
struct adouble * ad = NULL ;
ssize_t nread ;
ad = ad_fget ( talloc_tos ( ) , handle , fsp , ADOUBLE_RSRC ) ;
2014-06-23 18:59:45 +04:00
if ( ad = = NULL ) {
2016-12-08 22:38:17 +03:00
return - 1 ;
2014-06-23 18:59:45 +04:00
}
2016-12-08 22:38:17 +03:00
nread = SMB_VFS_NEXT_PREAD ( handle , fsp , data , n ,
offset + ad_getentryoff ( ad , ADEID_RFORK ) ) ;
2015-06-25 16:42:04 +03:00
2016-12-08 22:38:17 +03:00
TALLOC_FREE ( ad ) ;
return nread ;
}
2014-06-23 18:59:45 +04:00
2016-12-08 22:38:17 +03:00
static ssize_t fruit_pread_rsrc ( vfs_handle_struct * handle ,
files_struct * fsp , void * data ,
size_t n , off_t offset )
{
struct fio * fio = ( struct fio * ) VFS_FETCH_FSP_EXTENSION ( handle , fsp ) ;
ssize_t nread ;
2014-06-23 18:59:45 +04:00
2018-04-10 22:05:09 +03:00
if ( fio = = NULL ) {
errno = EINVAL ;
return - 1 ;
}
2016-12-08 22:38:17 +03:00
switch ( fio - > config - > rsrc ) {
case FRUIT_RSRC_STREAM :
nread = fruit_pread_rsrc_stream ( handle , fsp , data , n , offset ) ;
break ;
2016-11-16 13:01:45 +03:00
2016-12-08 22:38:17 +03:00
case FRUIT_RSRC_ADFILE :
nread = fruit_pread_rsrc_adouble ( handle , fsp , data , n , offset ) ;
break ;
2016-11-16 13:01:45 +03:00
2016-12-08 22:38:17 +03:00
case FRUIT_RSRC_XATTR :
nread = fruit_pread_rsrc_xattr ( handle , fsp , data , n , offset ) ;
break ;
2015-06-25 16:42:04 +03:00
2016-12-08 22:38:17 +03:00
default :
DBG_ERR ( " Unexpected rsrc config [%d] \n " , fio - > config - > rsrc ) ;
return - 1 ;
2014-06-23 18:59:45 +04:00
}
2016-12-08 22:38:17 +03:00
return nread ;
}
static ssize_t fruit_pread ( vfs_handle_struct * handle ,
files_struct * fsp , void * data ,
size_t n , off_t offset )
{
struct fio * fio = ( struct fio * ) VFS_FETCH_FSP_EXTENSION ( handle , fsp ) ;
ssize_t nread ;
2017-04-29 13:01:41 +03:00
DBG_DEBUG ( " Path [%s] offset=% " PRIdMAX " , size=%zd \n " ,
fsp_str_dbg ( fsp ) , ( intmax_t ) offset , n ) ;
2016-12-08 22:38:17 +03:00
if ( fio = = NULL ) {
return SMB_VFS_NEXT_PREAD ( handle , fsp , data , n , offset ) ;
2014-06-23 18:59:45 +04:00
}
2016-12-08 22:38:17 +03:00
if ( fio - > type = = ADOUBLE_META ) {
nread = fruit_pread_meta ( handle , fsp , data , n , offset ) ;
} else {
nread = fruit_pread_rsrc ( handle , fsp , data , n , offset ) ;
}
DBG_DEBUG ( " Path [%s] nread [%zd] \n " , fsp_str_dbg ( fsp ) , nread ) ;
return nread ;
2014-06-23 18:59:45 +04:00
}
2017-05-12 15:40:03 +03:00
static bool fruit_must_handle_aio_stream ( struct fio * fio )
{
if ( fio = = NULL ) {
return false ;
} ;
if ( ( fio - > type = = ADOUBLE_META ) & &
( fio - > config - > meta = = FRUIT_META_NETATALK ) )
{
return true ;
}
if ( ( fio - > type = = ADOUBLE_RSRC ) & &
( fio - > config - > rsrc = = FRUIT_RSRC_ADFILE ) )
{
return true ;
}
return false ;
}
struct fruit_pread_state {
ssize_t nread ;
struct vfs_aio_state vfs_aio_state ;
} ;
static void fruit_pread_done ( struct tevent_req * subreq ) ;
static struct tevent_req * fruit_pread_send (
struct vfs_handle_struct * handle ,
TALLOC_CTX * mem_ctx ,
struct tevent_context * ev ,
struct files_struct * fsp ,
void * data ,
size_t n , off_t offset )
{
struct tevent_req * req = NULL ;
struct tevent_req * subreq = NULL ;
struct fruit_pread_state * state = NULL ;
struct fio * fio = ( struct fio * ) VFS_FETCH_FSP_EXTENSION ( handle , fsp ) ;
req = tevent_req_create ( mem_ctx , & state ,
struct fruit_pread_state ) ;
if ( req = = NULL ) {
return NULL ;
}
if ( fruit_must_handle_aio_stream ( fio ) ) {
state - > nread = SMB_VFS_PREAD ( fsp , data , n , offset ) ;
if ( state - > nread ! = n ) {
if ( state - > nread ! = - 1 ) {
errno = EIO ;
}
tevent_req_error ( req , errno ) ;
return tevent_req_post ( req , ev ) ;
}
tevent_req_done ( req ) ;
return tevent_req_post ( req , ev ) ;
}
subreq = SMB_VFS_NEXT_PREAD_SEND ( state , ev , handle , fsp ,
data , n , offset ) ;
if ( tevent_req_nomem ( req , subreq ) ) {
return tevent_req_post ( req , ev ) ;
}
tevent_req_set_callback ( subreq , fruit_pread_done , req ) ;
return req ;
}
static void fruit_pread_done ( struct tevent_req * subreq )
{
struct tevent_req * req = tevent_req_callback_data (
subreq , struct tevent_req ) ;
struct fruit_pread_state * state = tevent_req_data (
req , struct fruit_pread_state ) ;
state - > nread = SMB_VFS_PREAD_RECV ( subreq , & state - > vfs_aio_state ) ;
TALLOC_FREE ( subreq ) ;
if ( tevent_req_error ( req , state - > vfs_aio_state . error ) ) {
return ;
}
tevent_req_done ( req ) ;
}
static ssize_t fruit_pread_recv ( struct tevent_req * req ,
struct vfs_aio_state * vfs_aio_state )
{
struct fruit_pread_state * state = tevent_req_data (
req , struct fruit_pread_state ) ;
if ( tevent_req_is_unix_error ( req , & vfs_aio_state - > error ) ) {
return - 1 ;
}
* vfs_aio_state = state - > vfs_aio_state ;
return state - > nread ;
}
2016-12-08 22:38:17 +03:00
static ssize_t fruit_pwrite_meta_stream ( vfs_handle_struct * handle ,
files_struct * fsp , const void * data ,
size_t n , off_t offset )
2014-06-23 18:59:45 +04:00
{
2018-08-22 16:21:08 +03:00
struct fio * fio = ( struct fio * ) VFS_FETCH_FSP_EXTENSION ( handle , fsp ) ;
2014-06-23 18:59:45 +04:00
AfpInfo * ai = NULL ;
2017-12-07 00:09:52 +03:00
size_t nwritten ;
2018-08-22 16:21:08 +03:00
int ret ;
2017-12-07 00:09:52 +03:00
bool ok ;
2014-06-23 18:59:45 +04:00
2018-08-22 16:21:08 +03:00
DBG_DEBUG ( " Path [%s] offset=% " PRIdMAX " , size=%zd \n " ,
fsp_str_dbg ( fsp ) , ( intmax_t ) offset , n ) ;
if ( fio = = NULL ) {
return - 1 ;
}
if ( fio - > fake_fd ) {
int fd ;
ret = SMB_VFS_NEXT_CLOSE ( handle , fsp ) ;
if ( ret ! = 0 ) {
DBG_ERR ( " Close [%s] failed: %s \n " ,
fsp_str_dbg ( fsp ) , strerror ( errno ) ) ;
fsp - > fh - > fd = - 1 ;
return - 1 ;
}
fd = SMB_VFS_NEXT_OPEN ( handle ,
fsp - > fsp_name ,
fsp ,
fio - > flags ,
fio - > mode ) ;
if ( fd = = - 1 ) {
DBG_ERR ( " On-demand create [%s] in write failed: %s \n " ,
fsp_str_dbg ( fsp ) , strerror ( errno ) ) ;
return - 1 ;
}
fsp - > fh - > fd = fd ;
fio - > fake_fd = false ;
}
2016-12-08 22:38:17 +03:00
ai = afpinfo_unpack ( talloc_tos ( ) , data ) ;
if ( ai = = NULL ) {
return - 1 ;
}
2014-06-23 18:59:45 +04:00
2017-12-07 00:09:52 +03:00
nwritten = SMB_VFS_NEXT_PWRITE ( handle , fsp , data , n , offset ) ;
if ( nwritten ! = n ) {
return - 1 ;
}
2016-12-08 22:38:17 +03:00
2017-12-07 00:09:52 +03:00
if ( ! ai_empty_finderinfo ( ai ) ) {
2016-12-08 22:38:17 +03:00
return n ;
2014-06-23 18:59:45 +04:00
}
2017-12-07 00:09:52 +03:00
ok = set_delete_on_close (
fsp ,
true ,
handle - > conn - > session_info - > security_token ,
handle - > conn - > session_info - > unix_token ) ;
if ( ! ok ) {
DBG_ERR ( " set_delete_on_close on [%s] failed \n " ,
fsp_str_dbg ( fsp ) ) ;
return - 1 ;
}
return n ;
2016-12-08 22:38:17 +03:00
}
2014-06-23 18:59:45 +04:00
2016-12-08 22:38:17 +03:00
static ssize_t fruit_pwrite_meta_netatalk ( vfs_handle_struct * handle ,
files_struct * fsp , const void * data ,
size_t n , off_t offset )
{
struct adouble * ad = NULL ;
AfpInfo * ai = NULL ;
char * p = NULL ;
int ret ;
2017-12-07 00:09:52 +03:00
bool ok ;
2016-11-15 23:32:25 +03:00
2016-12-08 22:38:17 +03:00
ai = afpinfo_unpack ( talloc_tos ( ) , data ) ;
if ( ai = = NULL ) {
return - 1 ;
}
2016-11-15 23:32:25 +03:00
2016-12-08 22:38:17 +03:00
ad = ad_fget ( talloc_tos ( ) , handle , fsp , ADOUBLE_META ) ;
2016-11-15 23:32:25 +03:00
if ( ad = = NULL ) {
2016-12-08 22:38:17 +03:00
ad = ad_init ( talloc_tos ( ) , handle , ADOUBLE_META ) ;
if ( ad = = NULL ) {
return - 1 ;
2016-11-15 23:32:25 +03:00
}
2016-12-08 22:38:17 +03:00
}
p = ad_get_entry ( ad , ADEID_FINDERI ) ;
if ( p = = NULL ) {
DBG_ERR ( " No ADEID_FINDERI for [%s] \n " , fsp_str_dbg ( fsp ) ) ;
TALLOC_FREE ( ad ) ;
return - 1 ;
2016-11-15 23:32:25 +03:00
}
2016-12-08 22:38:17 +03:00
memcpy ( p , & ai - > afpi_FinderInfo [ 0 ] , ADEDLEN_FINDERI ) ;
2016-11-15 23:32:25 +03:00
2016-12-08 22:38:17 +03:00
ret = ad_fset ( ad , fsp ) ;
if ( ret ! = 0 ) {
DBG_ERR ( " ad_pwrite [%s] failed \n " , fsp_str_dbg ( fsp ) ) ;
TALLOC_FREE ( ad ) ;
return - 1 ;
}
2016-11-16 13:01:45 +03:00
2016-12-08 22:38:17 +03:00
TALLOC_FREE ( ad ) ;
2017-12-07 00:09:52 +03:00
if ( ! ai_empty_finderinfo ( ai ) ) {
return n ;
}
ok = set_delete_on_close (
fsp ,
true ,
handle - > conn - > session_info - > security_token ,
handle - > conn - > session_info - > unix_token ) ;
if ( ! ok ) {
DBG_ERR ( " set_delete_on_close on [%s] failed \n " ,
fsp_str_dbg ( fsp ) ) ;
return - 1 ;
}
2016-12-08 22:38:17 +03:00
return n ;
}
2014-06-23 18:59:45 +04:00
2016-12-08 22:38:17 +03:00
static ssize_t fruit_pwrite_meta ( vfs_handle_struct * handle ,
files_struct * fsp , const void * data ,
size_t n , off_t offset )
{
struct fio * fio = ( struct fio * ) VFS_FETCH_FSP_EXTENSION ( handle , fsp ) ;
ssize_t nwritten ;
2014-06-23 18:59:45 +04:00
2016-12-08 22:38:17 +03:00
/*
* Writing an all 0 blob to the metadata stream
* results in the stream being removed on a macOS
* server . This ensures we behave the same and it
* verified by the " delete AFP_AfpInfo by writing all
* 0 " test.
*/
if ( n ! = AFP_INFO_SIZE | | offset ! = 0 ) {
DBG_ERR ( " unexpected offset=%jd or size=%jd \n " ,
( intmax_t ) offset , ( intmax_t ) n ) ;
return - 1 ;
2014-06-23 18:59:45 +04:00
}
2018-05-25 12:39:50 +03:00
if ( fio = = NULL ) {
DBG_ERR ( " Failed to fetch fsp extension " ) ;
return - 1 ;
}
2016-12-08 22:38:17 +03:00
switch ( fio - > config - > meta ) {
case FRUIT_META_STREAM :
nwritten = fruit_pwrite_meta_stream ( handle , fsp , data ,
n , offset ) ;
break ;
case FRUIT_META_NETATALK :
nwritten = fruit_pwrite_meta_netatalk ( handle , fsp , data ,
n , offset ) ;
break ;
default :
DBG_ERR ( " Unexpected meta config [%d] \n " , fio - > config - > meta ) ;
return - 1 ;
}
return nwritten ;
}
static ssize_t fruit_pwrite_rsrc_stream ( vfs_handle_struct * handle ,
files_struct * fsp , const void * data ,
size_t n , off_t offset )
{
return SMB_VFS_NEXT_PWRITE ( handle , fsp , data , n , offset ) ;
}
static ssize_t fruit_pwrite_rsrc_xattr ( vfs_handle_struct * handle ,
files_struct * fsp , const void * data ,
size_t n , off_t offset )
{
return SMB_VFS_NEXT_PWRITE ( handle , fsp , data , n , offset ) ;
}
static ssize_t fruit_pwrite_rsrc_adouble ( vfs_handle_struct * handle ,
files_struct * fsp , const void * data ,
size_t n , off_t offset )
{
struct adouble * ad = NULL ;
ssize_t nwritten ;
int ret ;
ad = ad_fget ( talloc_tos ( ) , handle , fsp , ADOUBLE_RSRC ) ;
if ( ad = = NULL ) {
DBG_ERR ( " ad_get [%s] failed \n " , fsp_str_dbg ( fsp ) ) ;
2014-06-23 18:59:45 +04:00
return - 1 ;
}
2016-12-08 22:38:17 +03:00
nwritten = SMB_VFS_NEXT_PWRITE ( handle , fsp , data , n ,
offset + ad_getentryoff ( ad , ADEID_RFORK ) ) ;
if ( nwritten ! = n ) {
DBG_ERR ( " Short write on [%s] [%zd/%zd] \n " ,
fsp_str_dbg ( fsp ) , nwritten , n ) ;
TALLOC_FREE ( ad ) ;
return - 1 ;
}
if ( ( n + offset ) > ad_getentrylen ( ad , ADEID_RFORK ) ) {
ad_setentrylen ( ad , ADEID_RFORK , n + offset ) ;
ret = ad_fset ( ad , fsp ) ;
if ( ret ! = 0 ) {
DBG_ERR ( " ad_pwrite [%s] failed \n " , fsp_str_dbg ( fsp ) ) ;
TALLOC_FREE ( ad ) ;
return - 1 ;
}
}
TALLOC_FREE ( ad ) ;
2014-06-23 18:59:45 +04:00
return n ;
}
2016-12-08 22:38:17 +03:00
static ssize_t fruit_pwrite_rsrc ( vfs_handle_struct * handle ,
files_struct * fsp , const void * data ,
size_t n , off_t offset )
{
struct fio * fio = ( struct fio * ) VFS_FETCH_FSP_EXTENSION ( handle , fsp ) ;
ssize_t nwritten ;
2018-05-25 12:32:38 +03:00
if ( fio = = NULL ) {
DBG_ERR ( " Failed to fetch fsp extension " ) ;
return - 1 ;
}
2016-12-08 22:38:17 +03:00
switch ( fio - > config - > rsrc ) {
case FRUIT_RSRC_STREAM :
nwritten = fruit_pwrite_rsrc_stream ( handle , fsp , data , n , offset ) ;
break ;
case FRUIT_RSRC_ADFILE :
nwritten = fruit_pwrite_rsrc_adouble ( handle , fsp , data , n , offset ) ;
break ;
case FRUIT_RSRC_XATTR :
nwritten = fruit_pwrite_rsrc_xattr ( handle , fsp , data , n , offset ) ;
break ;
default :
DBG_ERR ( " Unexpected rsrc config [%d] \n " , fio - > config - > rsrc ) ;
return - 1 ;
}
return nwritten ;
}
static ssize_t fruit_pwrite ( vfs_handle_struct * handle ,
files_struct * fsp , const void * data ,
size_t n , off_t offset )
{
struct fio * fio = ( struct fio * ) VFS_FETCH_FSP_EXTENSION ( handle , fsp ) ;
ssize_t nwritten ;
2017-04-29 13:01:41 +03:00
DBG_DEBUG ( " Path [%s] offset=% " PRIdMAX " , size=%zd \n " ,
fsp_str_dbg ( fsp ) , ( intmax_t ) offset , n ) ;
2016-12-08 22:38:17 +03:00
if ( fio = = NULL ) {
return SMB_VFS_NEXT_PWRITE ( handle , fsp , data , n , offset ) ;
}
if ( fio - > type = = ADOUBLE_META ) {
nwritten = fruit_pwrite_meta ( handle , fsp , data , n , offset ) ;
} else {
nwritten = fruit_pwrite_rsrc ( handle , fsp , data , n , offset ) ;
}
DBG_DEBUG ( " Path [%s] nwritten=%zd \n " , fsp_str_dbg ( fsp ) , nwritten ) ;
return nwritten ;
}
2017-05-12 15:40:03 +03:00
struct fruit_pwrite_state {
ssize_t nwritten ;
struct vfs_aio_state vfs_aio_state ;
} ;
static void fruit_pwrite_done ( struct tevent_req * subreq ) ;
static struct tevent_req * fruit_pwrite_send (
struct vfs_handle_struct * handle ,
TALLOC_CTX * mem_ctx ,
struct tevent_context * ev ,
struct files_struct * fsp ,
const void * data ,
size_t n , off_t offset )
{
struct tevent_req * req = NULL ;
struct tevent_req * subreq = NULL ;
struct fruit_pwrite_state * state = NULL ;
struct fio * fio = ( struct fio * ) VFS_FETCH_FSP_EXTENSION ( handle , fsp ) ;
req = tevent_req_create ( mem_ctx , & state ,
struct fruit_pwrite_state ) ;
if ( req = = NULL ) {
return NULL ;
}
if ( fruit_must_handle_aio_stream ( fio ) ) {
state - > nwritten = SMB_VFS_PWRITE ( fsp , data , n , offset ) ;
if ( state - > nwritten ! = n ) {
if ( state - > nwritten ! = - 1 ) {
errno = EIO ;
}
tevent_req_error ( req , errno ) ;
return tevent_req_post ( req , ev ) ;
}
tevent_req_done ( req ) ;
return tevent_req_post ( req , ev ) ;
}
subreq = SMB_VFS_NEXT_PWRITE_SEND ( state , ev , handle , fsp ,
data , n , offset ) ;
if ( tevent_req_nomem ( req , subreq ) ) {
return tevent_req_post ( req , ev ) ;
}
tevent_req_set_callback ( subreq , fruit_pwrite_done , req ) ;
return req ;
}
static void fruit_pwrite_done ( struct tevent_req * subreq )
{
struct tevent_req * req = tevent_req_callback_data (
subreq , struct tevent_req ) ;
struct fruit_pwrite_state * state = tevent_req_data (
req , struct fruit_pwrite_state ) ;
state - > nwritten = SMB_VFS_PWRITE_RECV ( subreq , & state - > vfs_aio_state ) ;
TALLOC_FREE ( subreq ) ;
if ( tevent_req_error ( req , state - > vfs_aio_state . error ) ) {
return ;
}
tevent_req_done ( req ) ;
}
static ssize_t fruit_pwrite_recv ( struct tevent_req * req ,
struct vfs_aio_state * vfs_aio_state )
{
struct fruit_pwrite_state * state = tevent_req_data (
req , struct fruit_pwrite_state ) ;
if ( tevent_req_is_unix_error ( req , & vfs_aio_state - > error ) ) {
return - 1 ;
}
* vfs_aio_state = state - > vfs_aio_state ;
return state - > nwritten ;
}
2014-06-23 18:59:45 +04:00
/**
* Helper to stat / lstat the base file of an smb_fname .
*/
static int fruit_stat_base ( vfs_handle_struct * handle ,
struct smb_filename * smb_fname ,
bool follow_links )
{
char * tmp_stream_name ;
int rc ;
tmp_stream_name = smb_fname - > stream_name ;
smb_fname - > stream_name = NULL ;
if ( follow_links ) {
rc = SMB_VFS_NEXT_STAT ( handle , smb_fname ) ;
} else {
rc = SMB_VFS_NEXT_LSTAT ( handle , smb_fname ) ;
}
smb_fname - > stream_name = tmp_stream_name ;
2018-10-17 20:07:11 +03:00
DBG_DEBUG ( " fruit_stat_base [%s] dev [%ju] ino [%ju] \n " ,
smb_fname - > base_name ,
( uintmax_t ) smb_fname - > st . st_ex_dev ,
( uintmax_t ) smb_fname - > st . st_ex_ino ) ;
2014-06-23 18:59:45 +04:00
return rc ;
}
2016-12-02 13:12:18 +03:00
static int fruit_stat_meta_stream ( vfs_handle_struct * handle ,
struct smb_filename * smb_fname ,
bool follow_links )
{
int ret ;
2018-08-22 17:49:23 +03:00
ino_t ino ;
ret = fruit_stat_base ( handle , smb_fname , false ) ;
if ( ret ! = 0 ) {
return - 1 ;
}
ino = fruit_inode ( & smb_fname - > st , smb_fname - > stream_name ) ;
2016-12-02 13:12:18 +03:00
if ( follow_links ) {
ret = SMB_VFS_NEXT_STAT ( handle , smb_fname ) ;
} else {
ret = SMB_VFS_NEXT_LSTAT ( handle , smb_fname ) ;
}
2018-08-22 17:49:23 +03:00
smb_fname - > st . st_ex_ino = ino ;
2016-12-02 13:12:18 +03:00
return ret ;
}
2016-12-02 13:05:50 +03:00
static int fruit_stat_meta_netatalk ( vfs_handle_struct * handle ,
struct smb_filename * smb_fname ,
bool follow_links )
2014-06-23 18:59:45 +04:00
{
2015-12-17 22:05:04 +03:00
struct adouble * ad = NULL ;
2017-05-25 21:38:26 +03:00
ad = ad_get ( talloc_tos ( ) , handle , smb_fname , ADOUBLE_META ) ;
2015-12-17 22:05:04 +03:00
if ( ad = = NULL ) {
DBG_INFO ( " fruit_stat_meta %s: %s \n " ,
smb_fname_str_dbg ( smb_fname ) , strerror ( errno ) ) ;
errno = ENOENT ;
return - 1 ;
}
TALLOC_FREE ( ad ) ;
2014-06-23 18:59:45 +04:00
/* Populate the stat struct with info from the base file. */
if ( fruit_stat_base ( handle , smb_fname , follow_links ) = = - 1 ) {
return - 1 ;
}
smb_fname - > st . st_ex_size = AFP_INFO_SIZE ;
smb_fname - > st . st_ex_ino = fruit_inode ( & smb_fname - > st ,
smb_fname - > stream_name ) ;
return 0 ;
}
2016-12-02 13:05:50 +03:00
static int fruit_stat_meta ( vfs_handle_struct * handle ,
struct smb_filename * smb_fname ,
bool follow_links )
{
struct fruit_config_data * config = NULL ;
int ret ;
SMB_VFS_HANDLE_GET_DATA ( handle , config ,
struct fruit_config_data , return - 1 ) ;
switch ( config - > meta ) {
case FRUIT_META_STREAM :
2016-12-02 13:12:18 +03:00
ret = fruit_stat_meta_stream ( handle , smb_fname , follow_links ) ;
break ;
2016-12-02 13:05:50 +03:00
case FRUIT_META_NETATALK :
ret = fruit_stat_meta_netatalk ( handle , smb_fname , follow_links ) ;
break ;
default :
DBG_ERR ( " Unexpected meta config [%d] \n " , config - > meta ) ;
return - 1 ;
}
return ret ;
}
2016-12-02 13:26:22 +03:00
static int fruit_stat_rsrc_netatalk ( vfs_handle_struct * handle ,
struct smb_filename * smb_fname ,
bool follow_links )
2014-06-23 18:59:45 +04:00
{
struct adouble * ad = NULL ;
2016-12-02 13:26:22 +03:00
int ret ;
2014-06-23 18:59:45 +04:00
2017-05-25 21:38:26 +03:00
ad = ad_get ( talloc_tos ( ) , handle , smb_fname , ADOUBLE_RSRC ) ;
2014-06-23 18:59:45 +04:00
if ( ad = = NULL ) {
errno = ENOENT ;
return - 1 ;
}
/* Populate the stat struct with info from the base file. */
2016-12-02 13:26:22 +03:00
ret = fruit_stat_base ( handle , smb_fname , follow_links ) ;
if ( ret ! = 0 ) {
2014-06-23 18:59:45 +04:00
TALLOC_FREE ( ad ) ;
return - 1 ;
}
smb_fname - > st . st_ex_size = ad_getentrylen ( ad , ADEID_RFORK ) ;
smb_fname - > st . st_ex_ino = fruit_inode ( & smb_fname - > st ,
smb_fname - > stream_name ) ;
TALLOC_FREE ( ad ) ;
return 0 ;
}
2016-12-02 13:30:06 +03:00
static int fruit_stat_rsrc_stream ( vfs_handle_struct * handle ,
struct smb_filename * smb_fname ,
bool follow_links )
{
int ret ;
if ( follow_links ) {
ret = SMB_VFS_NEXT_STAT ( handle , smb_fname ) ;
} else {
ret = SMB_VFS_NEXT_LSTAT ( handle , smb_fname ) ;
}
return ret ;
}
2016-12-02 13:44:53 +03:00
static int fruit_stat_rsrc_xattr ( vfs_handle_struct * handle ,
struct smb_filename * smb_fname ,
bool follow_links )
{
# ifdef HAVE_ATTROPEN
int ret ;
int fd = - 1 ;
/* Populate the stat struct with info from the base file. */
ret = fruit_stat_base ( handle , smb_fname , follow_links ) ;
if ( ret ! = 0 ) {
return - 1 ;
}
fd = attropen ( smb_fname - > base_name ,
AFPRESOURCE_EA_NETATALK ,
O_RDONLY ) ;
if ( fd = = - 1 ) {
return 0 ;
}
ret = sys_fstat ( fd , & smb_fname - > st , false ) ;
if ( ret ! = 0 ) {
close ( fd ) ;
DBG_ERR ( " fstat [%s:%s] failed \n " , smb_fname - > base_name ,
AFPRESOURCE_EA_NETATALK ) ;
return - 1 ;
}
close ( fd ) ;
fd = - 1 ;
smb_fname - > st . st_ex_ino = fruit_inode ( & smb_fname - > st ,
smb_fname - > stream_name ) ;
return ret ;
# else
errno = ENOSYS ;
return - 1 ;
# endif
}
2016-12-02 13:26:22 +03:00
static int fruit_stat_rsrc ( vfs_handle_struct * handle ,
struct smb_filename * smb_fname ,
bool follow_links )
{
struct fruit_config_data * config = NULL ;
int ret ;
DBG_DEBUG ( " Path [%s] \n " , smb_fname_str_dbg ( smb_fname ) ) ;
SMB_VFS_HANDLE_GET_DATA ( handle , config ,
struct fruit_config_data , return - 1 ) ;
switch ( config - > rsrc ) {
case FRUIT_RSRC_STREAM :
2016-12-02 13:30:06 +03:00
ret = fruit_stat_rsrc_stream ( handle , smb_fname , follow_links ) ;
break ;
2016-12-02 13:26:22 +03:00
case FRUIT_RSRC_XATTR :
2016-12-02 13:44:53 +03:00
ret = fruit_stat_rsrc_xattr ( handle , smb_fname , follow_links ) ;
break ;
2016-12-02 13:26:22 +03:00
case FRUIT_RSRC_ADFILE :
ret = fruit_stat_rsrc_netatalk ( handle , smb_fname , follow_links ) ;
break ;
default :
DBG_ERR ( " Unexpected rsrc config [%d] \n " , config - > rsrc ) ;
return - 1 ;
}
return ret ;
}
2014-06-23 18:59:45 +04:00
static int fruit_stat ( vfs_handle_struct * handle ,
struct smb_filename * smb_fname )
{
int rc = - 1 ;
DEBUG ( 10 , ( " fruit_stat called for %s \n " ,
smb_fname_str_dbg ( smb_fname ) ) ) ;
if ( ! is_ntfs_stream_smb_fname ( smb_fname )
| | is_ntfs_default_stream_smb_fname ( smb_fname ) ) {
rc = SMB_VFS_NEXT_STAT ( handle , smb_fname ) ;
if ( rc = = 0 ) {
update_btime ( handle , smb_fname ) ;
}
return rc ;
}
/*
* 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 .
*/
if ( is_afpinfo_stream ( smb_fname ) ) {
rc = fruit_stat_meta ( handle , smb_fname , true ) ;
} else if ( is_afpresource_stream ( smb_fname ) ) {
rc = fruit_stat_rsrc ( handle , smb_fname , true ) ;
} else {
return SMB_VFS_NEXT_STAT ( handle , smb_fname ) ;
}
if ( rc = = 0 ) {
update_btime ( handle , smb_fname ) ;
smb_fname - > st . st_ex_mode & = ~ S_IFMT ;
smb_fname - > st . st_ex_mode | = S_IFREG ;
smb_fname - > st . st_ex_blocks =
smb_fname - > st . st_ex_size / STAT_ST_BLOCKSIZE + 1 ;
}
return rc ;
}
static int fruit_lstat ( vfs_handle_struct * handle ,
struct smb_filename * smb_fname )
{
int rc = - 1 ;
DEBUG ( 10 , ( " fruit_lstat called for %s \n " ,
smb_fname_str_dbg ( smb_fname ) ) ) ;
if ( ! is_ntfs_stream_smb_fname ( smb_fname )
| | is_ntfs_default_stream_smb_fname ( smb_fname ) ) {
rc = SMB_VFS_NEXT_LSTAT ( handle , smb_fname ) ;
if ( rc = = 0 ) {
update_btime ( handle , smb_fname ) ;
}
return rc ;
}
if ( is_afpinfo_stream ( smb_fname ) ) {
rc = fruit_stat_meta ( handle , smb_fname , false ) ;
} else if ( is_afpresource_stream ( smb_fname ) ) {
rc = fruit_stat_rsrc ( handle , smb_fname , false ) ;
} else {
return SMB_VFS_NEXT_LSTAT ( handle , smb_fname ) ;
}
if ( rc = = 0 ) {
update_btime ( handle , smb_fname ) ;
smb_fname - > st . st_ex_mode & = ~ S_IFMT ;
smb_fname - > st . st_ex_mode | = S_IFREG ;
smb_fname - > st . st_ex_blocks =
smb_fname - > st . st_ex_size / STAT_ST_BLOCKSIZE + 1 ;
}
return rc ;
}
2016-12-08 22:39:38 +03:00
static int fruit_fstat_meta_stream ( vfs_handle_struct * handle ,
files_struct * fsp ,
SMB_STRUCT_STAT * sbuf )
2014-06-23 18:59:45 +04:00
{
2018-08-22 17:49:23 +03:00
struct fio * fio = ( struct fio * ) VFS_FETCH_FSP_EXTENSION ( handle , fsp ) ;
ino_t ino ;
int ret ;
if ( fio = = NULL ) {
return - 1 ;
}
if ( fio - > fake_fd ) {
ret = fruit_stat_base ( handle , fsp - > base_fsp - > fsp_name , false ) ;
if ( ret ! = 0 ) {
return - 1 ;
}
* sbuf = fsp - > base_fsp - > fsp_name - > st ;
sbuf - > st_ex_size = AFP_INFO_SIZE ;
sbuf - > st_ex_ino = fruit_inode ( sbuf , fsp - > fsp_name - > stream_name ) ;
return 0 ;
}
ret = fruit_stat_base ( handle , fsp - > base_fsp - > fsp_name , false ) ;
if ( ret ! = 0 ) {
return - 1 ;
}
* sbuf = fsp - > base_fsp - > fsp_name - > st ;
ino = fruit_inode ( sbuf , fsp - > fsp_name - > stream_name ) ;
ret = SMB_VFS_NEXT_FSTAT ( handle , fsp , sbuf ) ;
if ( ret ! = 0 ) {
return - 1 ;
}
sbuf - > st_ex_ino = ino ;
return 0 ;
2016-12-08 22:39:38 +03:00
}
2014-06-23 18:59:45 +04:00
2016-12-08 22:39:38 +03:00
static int fruit_fstat_meta_netatalk ( vfs_handle_struct * handle ,
files_struct * fsp ,
SMB_STRUCT_STAT * sbuf )
{
int ret ;
ret = fruit_stat_base ( handle , fsp - > base_fsp - > fsp_name , false ) ;
if ( ret ! = 0 ) {
2014-06-23 18:59:45 +04:00
return - 1 ;
}
2016-12-08 22:39:38 +03:00
2014-06-23 18:59:45 +04:00
* sbuf = fsp - > base_fsp - > fsp_name - > st ;
sbuf - > st_ex_size = AFP_INFO_SIZE ;
sbuf - > st_ex_ino = fruit_inode ( sbuf , fsp - > fsp_name - > stream_name ) ;
return 0 ;
}
2016-12-08 22:39:38 +03:00
static int fruit_fstat_meta ( vfs_handle_struct * handle ,
files_struct * fsp ,
SMB_STRUCT_STAT * sbuf ,
struct fio * fio )
2014-06-23 18:59:45 +04:00
{
2016-12-08 22:39:38 +03:00
int ret ;
2014-06-23 18:59:45 +04:00
2016-12-08 22:39:38 +03:00
DBG_DEBUG ( " Path [%s] \n " , fsp_str_dbg ( fsp ) ) ;
2014-06-23 18:59:45 +04:00
2016-12-08 22:39:38 +03:00
switch ( fio - > config - > meta ) {
case FRUIT_META_STREAM :
ret = fruit_fstat_meta_stream ( handle , fsp , sbuf ) ;
break ;
2014-06-23 18:59:45 +04:00
2016-12-08 22:39:38 +03:00
case FRUIT_META_NETATALK :
ret = fruit_fstat_meta_netatalk ( handle , fsp , sbuf ) ;
break ;
default :
DBG_ERR ( " Unexpected meta config [%d] \n " , fio - > config - > meta ) ;
return - 1 ;
2014-06-23 18:59:45 +04:00
}
2016-12-08 22:39:38 +03:00
DBG_DEBUG ( " Path [%s] ret [%d] \n " , fsp_str_dbg ( fsp ) , ret ) ;
return ret ;
}
static int fruit_fstat_rsrc_xattr ( vfs_handle_struct * handle ,
files_struct * fsp ,
SMB_STRUCT_STAT * sbuf )
{
return SMB_VFS_NEXT_FSTAT ( handle , fsp , sbuf ) ;
}
static int fruit_fstat_rsrc_stream ( vfs_handle_struct * handle ,
files_struct * fsp ,
SMB_STRUCT_STAT * sbuf )
{
return SMB_VFS_NEXT_FSTAT ( handle , fsp , sbuf ) ;
}
static int fruit_fstat_rsrc_adouble ( vfs_handle_struct * handle ,
files_struct * fsp ,
SMB_STRUCT_STAT * sbuf )
{
struct adouble * ad = NULL ;
int ret ;
2014-06-23 18:59:45 +04:00
/* Populate the stat struct with info from the base file. */
2016-12-08 22:39:38 +03:00
ret = fruit_stat_base ( handle , fsp - > base_fsp - > fsp_name , false ) ;
if ( ret = = - 1 ) {
return - 1 ;
}
ad = ad_get ( talloc_tos ( ) , handle ,
2017-05-25 21:38:26 +03:00
fsp - > base_fsp - > fsp_name ,
2016-12-08 22:39:38 +03:00
ADOUBLE_RSRC ) ;
if ( ad = = NULL ) {
DBG_ERR ( " ad_get [%s] failed [%s] \n " ,
fsp_str_dbg ( fsp ) , strerror ( errno ) ) ;
2014-06-23 18:59:45 +04:00
return - 1 ;
}
2016-12-08 22:39:38 +03:00
2014-06-23 18:59:45 +04:00
* sbuf = fsp - > base_fsp - > fsp_name - > st ;
sbuf - > st_ex_size = ad_getentrylen ( ad , ADEID_RFORK ) ;
sbuf - > st_ex_ino = fruit_inode ( sbuf , fsp - > fsp_name - > stream_name ) ;
2016-12-08 22:39:38 +03:00
TALLOC_FREE ( ad ) ;
2014-06-23 18:59:45 +04:00
return 0 ;
}
2016-12-08 22:39:38 +03:00
static int fruit_fstat_rsrc ( vfs_handle_struct * handle , files_struct * fsp ,
SMB_STRUCT_STAT * sbuf , struct fio * fio )
{
int ret ;
switch ( fio - > config - > rsrc ) {
case FRUIT_RSRC_STREAM :
ret = fruit_fstat_rsrc_stream ( handle , fsp , sbuf ) ;
break ;
case FRUIT_RSRC_ADFILE :
ret = fruit_fstat_rsrc_adouble ( handle , fsp , sbuf ) ;
break ;
case FRUIT_RSRC_XATTR :
ret = fruit_fstat_rsrc_xattr ( handle , fsp , sbuf ) ;
break ;
default :
DBG_ERR ( " Unexpected rsrc config [%d] \n " , fio - > config - > rsrc ) ;
return - 1 ;
}
return ret ;
}
2014-06-23 18:59:45 +04:00
static int fruit_fstat ( vfs_handle_struct * handle , files_struct * fsp ,
SMB_STRUCT_STAT * sbuf )
{
2016-12-08 22:39:38 +03:00
struct fio * fio = ( struct fio * ) VFS_FETCH_FSP_EXTENSION ( handle , fsp ) ;
2014-06-23 18:59:45 +04:00
int rc ;
2016-12-08 22:39:38 +03:00
if ( fio = = NULL ) {
return SMB_VFS_NEXT_FSTAT ( handle , fsp , sbuf ) ;
2014-06-23 18:59:45 +04:00
}
2016-12-08 22:39:38 +03:00
DBG_DEBUG ( " Path [%s] \n " , fsp_str_dbg ( fsp ) ) ;
if ( fio - > type = = ADOUBLE_META ) {
rc = fruit_fstat_meta ( handle , fsp , sbuf , fio ) ;
} else {
rc = fruit_fstat_rsrc ( handle , fsp , sbuf , fio ) ;
2014-06-23 18:59:45 +04:00
}
if ( rc = = 0 ) {
sbuf - > st_ex_mode & = ~ S_IFMT ;
sbuf - > st_ex_mode | = S_IFREG ;
sbuf - > st_ex_blocks = sbuf - > st_ex_size / STAT_ST_BLOCKSIZE + 1 ;
}
2017-04-29 13:01:41 +03:00
DBG_DEBUG ( " Path [%s] rc [%d] size [% " PRIdMAX " ] \n " ,
fsp_str_dbg ( fsp ) , rc , ( intmax_t ) sbuf - > st_ex_size ) ;
2014-06-23 18:59:45 +04:00
return rc ;
}
2017-12-07 16:56:36 +03:00
static NTSTATUS delete_invalid_meta_stream (
2016-12-02 17:49:03 +03:00
vfs_handle_struct * handle ,
const struct smb_filename * smb_fname ,
TALLOC_CTX * mem_ctx ,
unsigned int * pnum_streams ,
struct stream_struct * * pstreams )
{
2016-12-11 21:10:05 +03:00
struct smb_filename * sname = NULL ;
int ret ;
bool ok ;
ok = del_fruit_stream ( mem_ctx , pnum_streams , pstreams , AFPINFO_STREAM ) ;
if ( ! ok ) {
return NT_STATUS_INTERNAL_ERROR ;
}
sname = synthetic_smb_fname ( talloc_tos ( ) ,
smb_fname - > base_name ,
AFPINFO_STREAM_NAME ,
NULL , 0 ) ;
if ( sname = = NULL ) {
return NT_STATUS_NO_MEMORY ;
}
ret = SMB_VFS_NEXT_UNLINK ( handle , sname ) ;
TALLOC_FREE ( sname ) ;
if ( ret ! = 0 ) {
DBG_ERR ( " Removing [%s] failed \n " , smb_fname_str_dbg ( sname ) ) ;
return map_nt_error_from_unix ( errno ) ;
}
2016-12-02 17:49:03 +03:00
return NT_STATUS_OK ;
}
2017-12-07 16:56:36 +03:00
static NTSTATUS fruit_streaminfo_meta_stream (
vfs_handle_struct * handle ,
struct files_struct * fsp ,
const struct smb_filename * smb_fname ,
TALLOC_CTX * mem_ctx ,
unsigned int * pnum_streams ,
struct stream_struct * * pstreams )
{
struct stream_struct * stream = * pstreams ;
unsigned int num_streams = * pnum_streams ;
2017-12-07 19:32:35 +03:00
struct smb_filename * sname = NULL ;
char * full_name = NULL ;
uint32_t name_hash ;
struct share_mode_lock * lck = NULL ;
struct file_id id = { 0 } ;
bool delete_on_close_set ;
2017-12-07 16:56:36 +03:00
int i ;
2017-12-07 19:32:35 +03:00
int ret ;
NTSTATUS status ;
bool ok ;
2017-12-07 16:56:36 +03:00
for ( i = 0 ; i < num_streams ; i + + ) {
if ( strequal_m ( stream [ i ] . name , AFPINFO_STREAM ) ) {
break ;
}
}
if ( i = = num_streams ) {
return NT_STATUS_OK ;
}
if ( stream [ i ] . size ! = AFP_INFO_SIZE ) {
DBG_ERR ( " Removing invalid AFPINFO_STREAM size [%jd] from [%s] \n " ,
( intmax_t ) stream [ i ] . size , smb_fname_str_dbg ( smb_fname ) ) ;
return delete_invalid_meta_stream ( handle , smb_fname , mem_ctx ,
pnum_streams , pstreams ) ;
}
2017-12-07 19:32:35 +03:00
/*
* Now check if there ' s a delete - on - close pending on the stream . If so ,
* hide the stream . This behaviour was verified against a macOS 10.12
* SMB server .
*/
sname = synthetic_smb_fname ( talloc_tos ( ) ,
smb_fname - > base_name ,
AFPINFO_STREAM_NAME ,
NULL , 0 ) ;
if ( sname = = NULL ) {
status = NT_STATUS_NO_MEMORY ;
goto out ;
}
2018-08-22 17:49:23 +03:00
ret = fruit_stat_base ( handle , sname , false ) ;
2017-12-07 19:32:35 +03:00
if ( ret ! = 0 ) {
status = map_nt_error_from_unix ( errno ) ;
goto out ;
}
2018-08-22 17:49:23 +03:00
sname - > st . st_ex_ino = fruit_inode ( & sname - > st , AFPINFO_STREAM ) ;
2017-12-07 19:32:35 +03:00
id = SMB_VFS_NEXT_FILE_ID_CREATE ( handle , & sname - > st ) ;
lck = get_existing_share_mode_lock ( talloc_tos ( ) , id ) ;
if ( lck = = NULL ) {
status = NT_STATUS_OK ;
goto out ;
}
full_name = talloc_asprintf ( talloc_tos ( ) ,
" %s%s " ,
sname - > base_name ,
AFPINFO_STREAM ) ;
if ( full_name = = NULL ) {
status = NT_STATUS_NO_MEMORY ;
goto out ;
}
status = file_name_hash ( handle - > conn , full_name , & name_hash ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
goto out ;
}
delete_on_close_set = is_delete_on_close_set ( lck , name_hash ) ;
if ( delete_on_close_set ) {
ok = del_fruit_stream ( mem_ctx ,
pnum_streams ,
pstreams ,
AFPINFO_STREAM ) ;
if ( ! ok ) {
status = NT_STATUS_INTERNAL_ERROR ;
goto out ;
}
}
status = NT_STATUS_OK ;
out :
TALLOC_FREE ( sname ) ;
TALLOC_FREE ( lck ) ;
TALLOC_FREE ( full_name ) ;
return status ;
2017-12-07 16:56:36 +03:00
}
2016-12-02 17:49:03 +03:00
static NTSTATUS fruit_streaminfo_meta_netatalk (
vfs_handle_struct * handle ,
struct files_struct * fsp ,
const struct smb_filename * smb_fname ,
TALLOC_CTX * mem_ctx ,
unsigned int * pnum_streams ,
struct stream_struct * * pstreams )
{
2016-12-11 21:10:05 +03:00
struct stream_struct * stream = * pstreams ;
unsigned int num_streams = * pnum_streams ;
2016-12-02 17:49:03 +03:00
struct adouble * ad = NULL ;
bool is_fi_empty ;
2016-12-11 21:10:05 +03:00
int i ;
2016-12-02 17:49:03 +03:00
bool ok ;
/* Remove the Netatalk xattr from the list */
ok = del_fruit_stream ( mem_ctx , pnum_streams , pstreams ,
" : " NETATALK_META_XATTR " :$DATA " ) ;
if ( ! ok ) {
return NT_STATUS_NO_MEMORY ;
}
2016-12-11 21:10:05 +03:00
/*
* Check if there ' s a AFPINFO_STREAM from the VFS streams
* backend and if yes , remove it from the list
*/
for ( i = 0 ; i < num_streams ; i + + ) {
if ( strequal_m ( stream [ i ] . name , AFPINFO_STREAM ) ) {
break ;
}
}
if ( i < num_streams ) {
DBG_WARNING ( " Unexpected AFPINFO_STREAM on [%s] \n " ,
smb_fname_str_dbg ( smb_fname ) ) ;
ok = del_fruit_stream ( mem_ctx , pnum_streams , pstreams ,
AFPINFO_STREAM ) ;
if ( ! ok ) {
return NT_STATUS_INTERNAL_ERROR ;
}
}
2017-05-25 21:38:26 +03:00
ad = ad_get ( talloc_tos ( ) , handle , smb_fname , ADOUBLE_META ) ;
2016-12-02 17:49:03 +03:00
if ( ad = = NULL ) {
return NT_STATUS_OK ;
}
is_fi_empty = ad_empty_finderinfo ( ad ) ;
TALLOC_FREE ( ad ) ;
if ( is_fi_empty ) {
return NT_STATUS_OK ;
}
ok = add_fruit_stream ( mem_ctx , pnum_streams , pstreams ,
AFPINFO_STREAM_NAME , AFP_INFO_SIZE ,
smb_roundup ( handle - > conn , AFP_INFO_SIZE ) ) ;
if ( ! ok ) {
return NT_STATUS_NO_MEMORY ;
}
return NT_STATUS_OK ;
}
static NTSTATUS fruit_streaminfo_meta ( vfs_handle_struct * handle ,
struct files_struct * fsp ,
const struct smb_filename * smb_fname ,
TALLOC_CTX * mem_ctx ,
unsigned int * pnum_streams ,
struct stream_struct * * pstreams )
{
struct fruit_config_data * config = NULL ;
NTSTATUS status ;
SMB_VFS_HANDLE_GET_DATA ( handle , config , struct fruit_config_data ,
return NT_STATUS_INTERNAL_ERROR ) ;
switch ( config - > meta ) {
case FRUIT_META_NETATALK :
status = fruit_streaminfo_meta_netatalk ( handle , fsp , smb_fname ,
mem_ctx , pnum_streams ,
pstreams ) ;
break ;
case FRUIT_META_STREAM :
status = fruit_streaminfo_meta_stream ( handle , fsp , smb_fname ,
mem_ctx , pnum_streams ,
pstreams ) ;
break ;
default :
return NT_STATUS_INTERNAL_ERROR ;
}
return status ;
}
static NTSTATUS fruit_streaminfo_rsrc_stream (
vfs_handle_struct * handle ,
struct files_struct * fsp ,
const struct smb_filename * smb_fname ,
TALLOC_CTX * mem_ctx ,
unsigned int * pnum_streams ,
struct stream_struct * * pstreams )
{
bool ok ;
ok = filter_empty_rsrc_stream ( pnum_streams , pstreams ) ;
if ( ! ok ) {
DBG_ERR ( " Filtering resource stream failed \n " ) ;
return NT_STATUS_INTERNAL_ERROR ;
}
return NT_STATUS_OK ;
}
static NTSTATUS fruit_streaminfo_rsrc_xattr (
vfs_handle_struct * handle ,
struct files_struct * fsp ,
const struct smb_filename * smb_fname ,
TALLOC_CTX * mem_ctx ,
unsigned int * pnum_streams ,
struct stream_struct * * pstreams )
{
bool ok ;
ok = filter_empty_rsrc_stream ( pnum_streams , pstreams ) ;
if ( ! ok ) {
DBG_ERR ( " Filtering resource stream failed \n " ) ;
return NT_STATUS_INTERNAL_ERROR ;
}
return NT_STATUS_OK ;
}
static NTSTATUS fruit_streaminfo_rsrc_adouble (
vfs_handle_struct * handle ,
struct files_struct * fsp ,
const struct smb_filename * smb_fname ,
TALLOC_CTX * mem_ctx ,
unsigned int * pnum_streams ,
struct stream_struct * * pstreams )
{
struct stream_struct * stream = * pstreams ;
unsigned int num_streams = * pnum_streams ;
struct adouble * ad = NULL ;
bool ok ;
size_t rlen ;
int i ;
/*
* Check if there ' s a AFPRESOURCE_STREAM from the VFS streams backend
* and if yes , remove it from the list
*/
for ( i = 0 ; i < num_streams ; i + + ) {
if ( strequal_m ( stream [ i ] . name , AFPRESOURCE_STREAM ) ) {
break ;
}
}
if ( i < num_streams ) {
DBG_WARNING ( " Unexpected AFPRESOURCE_STREAM on [%s] \n " ,
smb_fname_str_dbg ( smb_fname ) ) ;
ok = del_fruit_stream ( mem_ctx , pnum_streams , pstreams ,
AFPRESOURCE_STREAM ) ;
if ( ! ok ) {
return NT_STATUS_INTERNAL_ERROR ;
}
}
2017-05-25 21:38:26 +03:00
ad = ad_get ( talloc_tos ( ) , handle , smb_fname , ADOUBLE_RSRC ) ;
2016-12-02 17:49:03 +03:00
if ( ad = = NULL ) {
return NT_STATUS_OK ;
}
rlen = ad_getentrylen ( ad , ADEID_RFORK ) ;
TALLOC_FREE ( ad ) ;
if ( rlen = = 0 ) {
return NT_STATUS_OK ;
}
ok = add_fruit_stream ( mem_ctx , pnum_streams , pstreams ,
AFPRESOURCE_STREAM_NAME , rlen ,
smb_roundup ( handle - > conn , rlen ) ) ;
if ( ! ok ) {
return NT_STATUS_NO_MEMORY ;
}
return NT_STATUS_OK ;
}
static NTSTATUS fruit_streaminfo_rsrc ( vfs_handle_struct * handle ,
struct files_struct * fsp ,
const struct smb_filename * smb_fname ,
TALLOC_CTX * mem_ctx ,
unsigned int * pnum_streams ,
struct stream_struct * * pstreams )
{
struct fruit_config_data * config = NULL ;
NTSTATUS status ;
SMB_VFS_HANDLE_GET_DATA ( handle , config , struct fruit_config_data ,
return NT_STATUS_INTERNAL_ERROR ) ;
switch ( config - > rsrc ) {
case FRUIT_RSRC_STREAM :
status = fruit_streaminfo_rsrc_stream ( handle , fsp , smb_fname ,
mem_ctx , pnum_streams ,
pstreams ) ;
break ;
case FRUIT_RSRC_XATTR :
status = fruit_streaminfo_rsrc_xattr ( handle , fsp , smb_fname ,
mem_ctx , pnum_streams ,
pstreams ) ;
break ;
case FRUIT_RSRC_ADFILE :
status = fruit_streaminfo_rsrc_adouble ( handle , fsp , smb_fname ,
mem_ctx , pnum_streams ,
pstreams ) ;
break ;
default :
return NT_STATUS_INTERNAL_ERROR ;
}
return status ;
}
2018-10-20 15:53:50 +03:00
static void fruit_filter_empty_streams ( unsigned int * pnum_streams ,
struct stream_struct * * pstreams )
{
unsigned num_streams = * pnum_streams ;
struct stream_struct * streams = * pstreams ;
unsigned i = 0 ;
if ( ! global_fruit_config . nego_aapl ) {
return ;
}
while ( i < num_streams ) {
struct smb_filename smb_fname = ( struct smb_filename ) {
. stream_name = streams [ i ] . name ,
} ;
if ( is_ntfs_default_stream_smb_fname ( & smb_fname )
| | streams [ i ] . size > 0 )
{
i + + ;
continue ;
}
streams [ i ] = streams [ num_streams - 1 ] ;
num_streams - - ;
}
* pnum_streams = num_streams ;
}
2014-06-23 18:59:45 +04:00
static NTSTATUS fruit_streaminfo ( vfs_handle_struct * handle ,
struct files_struct * fsp ,
2016-03-05 01:16:13 +03:00
const struct smb_filename * smb_fname ,
2014-06-23 18:59:45 +04:00
TALLOC_CTX * mem_ctx ,
unsigned int * pnum_streams ,
struct stream_struct * * pstreams )
{
struct fruit_config_data * config = NULL ;
2015-08-24 18:43:40 +03:00
NTSTATUS status ;
2014-06-23 18:59:45 +04:00
SMB_VFS_HANDLE_GET_DATA ( handle , config , struct fruit_config_data ,
return NT_STATUS_UNSUCCESSFUL ) ;
2016-12-02 17:49:03 +03:00
DBG_DEBUG ( " Path [%s] \n " , smb_fname_str_dbg ( smb_fname ) ) ;
2014-06-23 18:59:45 +04:00
2016-03-05 01:16:13 +03:00
status = SMB_VFS_NEXT_STREAMINFO ( handle , fsp , smb_fname , mem_ctx ,
2015-08-24 18:43:40 +03:00
pnum_streams , pstreams ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
return status ;
}
2018-10-20 15:53:50 +03:00
fruit_filter_empty_streams ( pnum_streams , pstreams ) ;
2016-12-02 17:49:03 +03:00
status = fruit_streaminfo_meta ( handle , fsp , smb_fname ,
mem_ctx , pnum_streams , pstreams ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
return status ;
}
status = fruit_streaminfo_rsrc ( handle , fsp , smb_fname ,
mem_ctx , pnum_streams , pstreams ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
return status ;
2015-08-24 18:43:40 +03:00
}
return NT_STATUS_OK ;
2014-06-23 18:59:45 +04:00
}
static int fruit_ntimes ( vfs_handle_struct * handle ,
const struct smb_filename * smb_fname ,
struct smb_file_time * ft )
{
int rc = 0 ;
struct adouble * ad = NULL ;
2016-12-02 17:57:22 +03:00
struct fruit_config_data * config = NULL ;
2014-06-23 18:59:45 +04:00
2016-12-02 17:57:22 +03:00
SMB_VFS_HANDLE_GET_DATA ( handle , config , struct fruit_config_data ,
return - 1 ) ;
if ( ( config - > meta ! = FRUIT_META_NETATALK ) | |
null_timespec ( ft - > create_time ) )
{
return SMB_VFS_NEXT_NTIMES ( handle , smb_fname , ft ) ;
2014-06-23 18:59:45 +04:00
}
DEBUG ( 10 , ( " set btime for %s to %s \n " , smb_fname_str_dbg ( smb_fname ) ,
time_to_asc ( convert_timespec_to_time_t ( ft - > create_time ) ) ) ) ;
2017-05-25 21:38:26 +03:00
ad = ad_get ( talloc_tos ( ) , handle , smb_fname , ADOUBLE_META ) ;
2014-06-23 18:59:45 +04:00
if ( ad = = NULL ) {
goto exit ;
}
ad_setdate ( ad , AD_DATE_CREATE | AD_DATE_UNIX ,
convert_time_t_to_uint32_t ( ft - > create_time . tv_sec ) ) ;
2017-05-25 21:38:26 +03:00
rc = ad_set ( ad , smb_fname ) ;
2014-06-23 18:59:45 +04:00
exit :
TALLOC_FREE ( ad ) ;
if ( rc ! = 0 ) {
DEBUG ( 1 , ( " fruit_ntimes: %s \n " , smb_fname_str_dbg ( smb_fname ) ) ) ;
return - 1 ;
}
return SMB_VFS_NEXT_NTIMES ( handle , smb_fname , ft ) ;
}
static int fruit_fallocate ( struct vfs_handle_struct * handle ,
struct files_struct * fsp ,
2015-02-09 20:21:59 +03:00
uint32_t mode ,
2014-06-23 18:59:45 +04:00
off_t offset ,
off_t len )
{
2016-12-08 22:41:55 +03:00
struct fio * fio = ( struct fio * ) VFS_FETCH_FSP_EXTENSION ( handle , fsp ) ;
2014-06-23 18:59:45 +04:00
2016-12-08 22:41:55 +03:00
if ( fio = = NULL ) {
2014-06-23 18:59:45 +04:00
return SMB_VFS_NEXT_FALLOCATE ( handle , fsp , mode , offset , len ) ;
}
/* Let the pwrite code path handle it. */
2014-12-06 02:37:11 +03:00
errno = ENOSYS ;
return - 1 ;
2014-06-23 18:59:45 +04:00
}
2016-12-02 18:20:46 +03:00
static int fruit_ftruncate_rsrc_xattr ( struct vfs_handle_struct * handle ,
struct files_struct * fsp ,
off_t offset )
{
# ifdef HAVE_ATTROPEN
return SMB_VFS_NEXT_FTRUNCATE ( handle , fsp , offset ) ;
# endif
return 0 ;
}
static int fruit_ftruncate_rsrc_adouble ( struct vfs_handle_struct * handle ,
struct files_struct * fsp ,
off_t offset )
{
int rc ;
2016-12-08 21:12:32 +03:00
struct adouble * ad = NULL ;
off_t ad_off ;
2016-12-02 18:20:46 +03:00
2016-12-08 21:12:32 +03:00
ad = ad_fget ( talloc_tos ( ) , handle , fsp , ADOUBLE_RSRC ) ;
if ( ad = = NULL ) {
2016-12-08 22:42:54 +03:00
DBG_DEBUG ( " ad_get [%s] failed [%s] \n " ,
fsp_str_dbg ( fsp ) , strerror ( errno ) ) ;
2016-12-02 18:20:46 +03:00
return - 1 ;
}
2016-12-08 21:12:32 +03:00
ad_off = ad_getentryoff ( ad , ADEID_RFORK ) ;
2017-10-11 19:11:12 +03:00
rc = ftruncate ( fsp - > fh - > fd , offset + ad_off ) ;
2016-12-02 18:20:46 +03:00
if ( rc ! = 0 ) {
2016-12-08 22:42:54 +03:00
TALLOC_FREE ( ad ) ;
2016-12-02 18:20:46 +03:00
return - 1 ;
}
ad_setentrylen ( ad , ADEID_RFORK , offset ) ;
2016-12-08 21:12:32 +03:00
rc = ad_fset ( ad , fsp ) ;
2016-12-02 18:20:46 +03:00
if ( rc ! = 0 ) {
2016-12-08 22:42:54 +03:00
DBG_ERR ( " ad_fset [%s] failed [%s] \n " ,
2016-12-02 18:20:46 +03:00
fsp_str_dbg ( fsp ) , strerror ( errno ) ) ;
2016-12-08 22:42:54 +03:00
TALLOC_FREE ( ad ) ;
2016-12-02 18:20:46 +03:00
return - 1 ;
}
2016-12-08 22:42:54 +03:00
TALLOC_FREE ( ad ) ;
2016-12-02 18:20:46 +03:00
return 0 ;
}
static int fruit_ftruncate_rsrc_stream ( struct vfs_handle_struct * handle ,
struct files_struct * fsp ,
off_t offset )
{
return SMB_VFS_NEXT_FTRUNCATE ( handle , fsp , offset ) ;
}
2015-08-12 08:34:53 +03:00
static int fruit_ftruncate_rsrc ( struct vfs_handle_struct * handle ,
struct files_struct * fsp ,
2016-12-02 18:20:46 +03:00
off_t offset )
2015-08-12 08:34:53 +03:00
{
2016-12-08 22:42:54 +03:00
struct fio * fio = ( struct fio * ) VFS_FETCH_FSP_EXTENSION ( handle , fsp ) ;
2016-12-02 18:20:46 +03:00
int ret ;
2015-08-12 08:34:53 +03:00
2018-05-25 13:43:42 +03:00
if ( fio = = NULL ) {
DBG_ERR ( " Failed to fetch fsp extension " ) ;
return - 1 ;
}
2016-12-08 22:42:54 +03:00
switch ( fio - > config - > rsrc ) {
2016-12-02 18:20:46 +03:00
case FRUIT_RSRC_XATTR :
ret = fruit_ftruncate_rsrc_xattr ( handle , fsp , offset ) ;
break ;
2015-08-12 08:34:53 +03:00
2016-12-02 18:20:46 +03:00
case FRUIT_RSRC_ADFILE :
ret = fruit_ftruncate_rsrc_adouble ( handle , fsp , offset ) ;
break ;
case FRUIT_RSRC_STREAM :
ret = fruit_ftruncate_rsrc_stream ( handle , fsp , offset ) ;
break ;
default :
2016-12-08 22:42:54 +03:00
DBG_ERR ( " Unexpected rsrc config [%d] \n " , fio - > config - > rsrc ) ;
2015-08-12 08:34:53 +03:00
return - 1 ;
}
2016-12-02 18:20:46 +03:00
return ret ;
2015-08-12 08:34:53 +03:00
}
2016-12-08 22:42:54 +03:00
static int fruit_ftruncate_meta ( struct vfs_handle_struct * handle ,
struct files_struct * fsp ,
off_t offset )
{
if ( offset > 60 ) {
DBG_WARNING ( " ftruncate %s to %jd " ,
fsp_str_dbg ( fsp ) , ( intmax_t ) offset ) ;
/* OS X returns NT_STATUS_ALLOTTED_SPACE_EXCEEDED */
errno = EOVERFLOW ;
return - 1 ;
}
/* OS X returns success but does nothing */
DBG_INFO ( " ignoring ftruncate %s to %jd \n " ,
fsp_str_dbg ( fsp ) , ( intmax_t ) offset ) ;
return 0 ;
}
2014-06-23 18:59:45 +04:00
static int fruit_ftruncate ( struct vfs_handle_struct * handle ,
struct files_struct * fsp ,
off_t offset )
{
2016-12-08 22:42:54 +03:00
struct fio * fio = ( struct fio * ) VFS_FETCH_FSP_EXTENSION ( handle , fsp ) ;
int ret ;
2016-11-15 22:32:05 +03:00
2017-04-29 13:01:41 +03:00
DBG_DEBUG ( " Path [%s] offset [% " PRIdMAX " ] \n " , fsp_str_dbg ( fsp ) ,
( intmax_t ) offset ) ;
2016-12-08 22:42:54 +03:00
if ( fio = = NULL ) {
return SMB_VFS_NEXT_FTRUNCATE ( handle , fsp , offset ) ;
2016-11-15 22:32:05 +03:00
}
2016-12-08 22:42:54 +03:00
if ( fio - > type = = ADOUBLE_META ) {
ret = fruit_ftruncate_meta ( handle , fsp , offset ) ;
} else {
ret = fruit_ftruncate_rsrc ( handle , fsp , offset ) ;
2014-06-23 18:59:45 +04:00
}
2016-12-08 22:42:54 +03:00
DBG_DEBUG ( " Path [%s] result [%d] \n " , fsp_str_dbg ( fsp ) , ret ) ;
return ret ;
2014-06-23 18:59:45 +04:00
}
static NTSTATUS fruit_create_file ( vfs_handle_struct * handle ,
struct smb_request * req ,
uint16_t root_dir_fid ,
struct smb_filename * smb_fname ,
uint32_t access_mask ,
uint32_t share_access ,
uint32_t create_disposition ,
uint32_t create_options ,
uint32_t file_attributes ,
uint32_t oplock_request ,
struct smb2_lease * lease ,
uint64_t allocation_size ,
uint32_t private_flags ,
struct security_descriptor * sd ,
struct ea_list * ea_list ,
files_struct * * result ,
2014-11-26 16:12:51 +03:00
int * pinfo ,
const struct smb2_create_blobs * in_context_blobs ,
struct smb2_create_blobs * out_context_blobs )
2014-06-23 18:59:45 +04:00
{
NTSTATUS status ;
struct fruit_config_data * config = NULL ;
2015-08-08 21:21:39 +03:00
files_struct * fsp = NULL ;
2018-10-22 17:56:46 +03:00
struct fio * fio = NULL ;
2014-06-23 18:59:45 +04:00
2014-11-26 20:11:17 +03:00
status = check_aapl ( handle , req , in_context_blobs , out_context_blobs ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
2015-09-08 09:18:02 +03:00
goto fail ;
2014-11-26 20:11:17 +03:00
}
SMB_VFS_HANDLE_GET_DATA ( handle , config , struct fruit_config_data ,
return NT_STATUS_UNSUCCESSFUL ) ;
2014-06-23 18:59:45 +04:00
status = SMB_VFS_NEXT_CREATE_FILE (
handle , req , root_dir_fid , smb_fname ,
access_mask , share_access ,
create_disposition , create_options ,
file_attributes , oplock_request ,
lease ,
allocation_size , private_flags ,
sd , ea_list , result ,
2014-11-26 20:11:17 +03:00
pinfo , in_context_blobs , out_context_blobs ) ;
2014-06-23 18:59:45 +04:00
if ( ! NT_STATUS_IS_OK ( status ) ) {
return status ;
}
2015-11-25 11:12:55 +03:00
2015-08-08 21:21:39 +03:00
fsp = * result ;
2014-06-23 18:59:45 +04:00
2017-02-28 11:39:37 +03:00
if ( global_fruit_config . nego_aapl ) {
2016-04-11 13:17:22 +03:00
if ( config - > posix_rename & & fsp - > is_directory ) {
2015-01-22 12:07:56 +03:00
/*
* Enable POSIX directory rename behaviour
*/
fsp - > posix_flags | = FSP_POSIX_FLAGS_RENAME ;
}
2015-08-08 21:21:39 +03:00
}
/*
* If this is a plain open for existing files , opening an 0
* byte size resource fork MUST fail with
* NT_STATUS_OBJECT_NAME_NOT_FOUND .
*
* Cf the vfs_fruit torture tests in test_rfork_create ( ) .
*/
2018-10-20 16:28:06 +03:00
if ( global_fruit_config . nego_aapl & &
2018-10-20 15:53:50 +03:00
create_disposition = = FILE_OPEN & &
smb_fname - > st . st_ex_size = = 0 & &
is_ntfs_stream_smb_fname ( smb_fname ) & &
! ( is_ntfs_default_stream_smb_fname ( smb_fname ) ) )
2015-08-08 21:21:39 +03:00
{
2018-10-20 15:53:50 +03:00
status = NT_STATUS_OBJECT_NAME_NOT_FOUND ;
goto fail ;
2015-04-22 23:29:16 +03:00
}
2015-08-08 21:21:39 +03:00
2018-10-22 17:56:46 +03:00
fio = ( struct fio * ) VFS_FETCH_FSP_EXTENSION ( handle , fsp ) ;
if ( fio ! = NULL & & pinfo ! = NULL & & * pinfo = = FILE_WAS_CREATED ) {
fio - > created = true ;
}
2014-06-23 18:59:45 +04:00
if ( is_ntfs_stream_smb_fname ( smb_fname )
2015-08-08 21:21:39 +03:00
| | fsp - > is_directory ) {
2014-06-23 18:59:45 +04:00
return status ;
}
if ( config - > locking = = FRUIT_LOCKING_NETATALK ) {
status = fruit_check_access (
handle , * result ,
access_mask ,
map_share_mode_to_deny_mode ( share_access , 0 ) ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
goto fail ;
}
}
return status ;
fail :
2015-08-08 21:21:39 +03:00
DEBUG ( 10 , ( " fruit_create_file: %s \n " , nt_errstr ( status ) ) ) ;
2014-06-23 18:59:45 +04:00
2015-08-08 21:21:39 +03:00
if ( fsp ) {
close_file ( req , fsp , ERROR_CLOSE ) ;
* result = fsp = NULL ;
2014-06-23 18:59:45 +04:00
}
return status ;
}
2014-11-26 20:11:17 +03:00
static NTSTATUS fruit_readdir_attr ( struct vfs_handle_struct * handle ,
const struct smb_filename * fname ,
TALLOC_CTX * mem_ctx ,
struct readdir_attr_data * * pattr_data )
{
struct fruit_config_data * config = NULL ;
struct readdir_attr_data * attr_data ;
NTSTATUS status ;
SMB_VFS_HANDLE_GET_DATA ( handle , config ,
struct fruit_config_data ,
return NT_STATUS_UNSUCCESSFUL ) ;
2017-02-28 11:39:37 +03:00
if ( ! global_fruit_config . nego_aapl ) {
2014-11-26 20:11:17 +03:00
return SMB_VFS_NEXT_READDIR_ATTR ( handle , fname , mem_ctx , pattr_data ) ;
}
DEBUG ( 10 , ( " fruit_readdir_attr %s \n " , fname - > base_name ) ) ;
* pattr_data = talloc_zero ( mem_ctx , struct readdir_attr_data ) ;
if ( * pattr_data = = NULL ) {
return NT_STATUS_UNSUCCESSFUL ;
}
attr_data = * pattr_data ;
attr_data - > type = RDATTR_AAPL ;
/*
* Mac metadata : compressed FinderInfo , resource fork length
* and creation date
*/
status = readdir_attr_macmeta ( handle , fname , attr_data ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
/*
* Error handling is tricky : if we return failure from
* this function , the corresponding directory entry
* will to be passed to the client , so we really just
* want to error out on fatal errors .
*/
if ( ! NT_STATUS_EQUAL ( status , NT_STATUS_ACCESS_DENIED ) ) {
goto fail ;
}
}
/*
* UNIX mode
*/
if ( config - > unix_info_enabled ) {
attr_data - > attr_data . aapl . unix_mode = fname - > st . st_ex_mode ;
}
/*
* max_access
*/
if ( ! config - > readdir_attr_max_access ) {
attr_data - > attr_data . aapl . max_access = FILE_GENERIC_ALL ;
} else {
status = smbd_calculate_access_mask (
handle - > conn ,
fname ,
false ,
SEC_FLAG_MAXIMUM_ALLOWED ,
& attr_data - > attr_data . aapl . max_access ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
goto fail ;
}
}
return NT_STATUS_OK ;
fail :
DEBUG ( 1 , ( " fruit_readdir_attr %s, error: %s \n " ,
fname - > base_name , nt_errstr ( status ) ) ) ;
TALLOC_FREE ( * pattr_data ) ;
return status ;
}
static NTSTATUS fruit_fget_nt_acl ( vfs_handle_struct * handle ,
files_struct * fsp ,
2015-05-03 06:11:02 +03:00
uint32_t security_info ,
2014-11-26 20:11:17 +03:00
TALLOC_CTX * mem_ctx ,
struct security_descriptor * * ppdesc )
{
NTSTATUS status ;
struct security_ace ace ;
struct dom_sid sid ;
struct fruit_config_data * config ;
SMB_VFS_HANDLE_GET_DATA ( handle , config ,
struct fruit_config_data ,
return NT_STATUS_UNSUCCESSFUL ) ;
status = SMB_VFS_NEXT_FGET_NT_ACL ( handle , fsp , security_info ,
mem_ctx , ppdesc ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
return status ;
}
/*
* Add MS NFS style ACEs with uid , gid and mode
*/
2017-07-12 10:33:59 +03:00
if ( ! global_fruit_config . nego_aapl ) {
return NT_STATUS_OK ;
}
2014-11-26 20:11:17 +03:00
if ( ! config - > unix_info_enabled ) {
return NT_STATUS_OK ;
}
2018-03-15 19:57:09 +03:00
/* First remove any existing ACE's with NFS style mode/uid/gid SIDs. */
status = remove_virtual_nfs_aces ( * ppdesc ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
DBG_WARNING ( " failed to remove MS NFS style ACEs \n " ) ;
return status ;
}
2014-11-26 20:11:17 +03:00
/* MS NFS style mode */
sid_compose ( & sid , & global_sid_Unix_NFS_Mode , fsp - > fsp_name - > st . st_ex_mode ) ;
init_sec_ace ( & ace , & sid , SEC_ACE_TYPE_ACCESS_DENIED , 0 , 0 ) ;
status = security_descriptor_dacl_add ( * ppdesc , & ace ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
DEBUG ( 1 , ( " failed to add MS NFS style ACE \n " ) ) ;
return status ;
}
/* MS NFS style uid */
sid_compose ( & sid , & global_sid_Unix_NFS_Users , fsp - > fsp_name - > st . st_ex_uid ) ;
init_sec_ace ( & ace , & sid , SEC_ACE_TYPE_ACCESS_DENIED , 0 , 0 ) ;
status = security_descriptor_dacl_add ( * ppdesc , & ace ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
DEBUG ( 1 , ( " failed to add MS NFS style ACE \n " ) ) ;
return status ;
}
/* MS NFS style gid */
sid_compose ( & sid , & global_sid_Unix_NFS_Groups , fsp - > fsp_name - > st . st_ex_gid ) ;
init_sec_ace ( & ace , & sid , SEC_ACE_TYPE_ACCESS_DENIED , 0 , 0 ) ;
status = security_descriptor_dacl_add ( * ppdesc , & ace ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
DEBUG ( 1 , ( " failed to add MS NFS style ACE \n " ) ) ;
return status ;
}
return NT_STATUS_OK ;
}
static NTSTATUS fruit_fset_nt_acl ( vfs_handle_struct * handle ,
files_struct * fsp ,
2015-05-03 06:11:02 +03:00
uint32_t security_info_sent ,
2018-03-03 00:21:37 +03:00
const struct security_descriptor * orig_psd )
2014-11-26 20:11:17 +03:00
{
NTSTATUS status ;
bool do_chmod ;
2016-06-16 04:01:23 +03:00
mode_t ms_nfs_mode = 0 ;
2014-11-26 20:11:17 +03:00
int result ;
2018-03-03 00:21:37 +03:00
struct security_descriptor * psd = NULL ;
2018-03-03 00:51:54 +03:00
uint32_t orig_num_aces = 0 ;
if ( orig_psd - > dacl ! = NULL ) {
orig_num_aces = orig_psd - > dacl - > num_aces ;
}
2018-03-03 00:21:37 +03:00
psd = security_descriptor_copy ( talloc_tos ( ) , orig_psd ) ;
if ( psd = = NULL ) {
return NT_STATUS_NO_MEMORY ;
}
2014-11-26 20:11:17 +03:00
2015-12-11 19:27:50 +03:00
DBG_DEBUG ( " fruit_fset_nt_acl: %s \n " , fsp_str_dbg ( fsp ) ) ;
2014-11-26 20:11:17 +03:00
status = check_ms_nfs ( handle , fsp , psd , & ms_nfs_mode , & do_chmod ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
DEBUG ( 1 , ( " fruit_fset_nt_acl: check_ms_nfs failed%s \n " , fsp_str_dbg ( fsp ) ) ) ;
2018-03-03 00:21:37 +03:00
TALLOC_FREE ( psd ) ;
2014-11-26 20:11:17 +03:00
return status ;
}
2018-03-03 00:51:54 +03:00
/*
* If only ms_nfs ACE entries were sent , ensure we set the DACL
* sent / present flags correctly now we ' ve removed them .
*/
if ( orig_num_aces ! = 0 ) {
/*
* Are there any ACE ' s left ?
*/
if ( psd - > dacl - > num_aces = = 0 ) {
/* No - clear the DACL sent/present flags. */
security_info_sent & = ~ SECINFO_DACL ;
psd - > type & = ~ SEC_DESC_DACL_PRESENT ;
}
}
2014-11-26 20:11:17 +03:00
status = SMB_VFS_NEXT_FSET_NT_ACL ( handle , fsp , security_info_sent , psd ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
DEBUG ( 1 , ( " fruit_fset_nt_acl: SMB_VFS_NEXT_FSET_NT_ACL failed%s \n " , fsp_str_dbg ( fsp ) ) ) ;
2018-03-03 00:21:37 +03:00
TALLOC_FREE ( psd ) ;
2014-11-26 20:11:17 +03:00
return status ;
}
if ( do_chmod ) {
if ( fsp - > fh - > fd ! = - 1 ) {
result = SMB_VFS_FCHMOD ( fsp , ms_nfs_mode ) ;
} else {
result = SMB_VFS_CHMOD ( fsp - > conn ,
2016-03-02 03:20:25 +03:00
fsp - > fsp_name ,
2014-11-26 20:11:17 +03:00
ms_nfs_mode ) ;
}
if ( result ! = 0 ) {
DEBUG ( 1 , ( " chmod: %s, result: %d, %04o error %s \n " , fsp_str_dbg ( fsp ) ,
2015-05-06 17:25:51 +03:00
result , ( unsigned ) ms_nfs_mode ,
strerror ( errno ) ) ) ;
2014-11-26 20:11:17 +03:00
status = map_nt_error_from_unix ( errno ) ;
2018-03-03 00:21:37 +03:00
TALLOC_FREE ( psd ) ;
2014-11-26 20:11:17 +03:00
return status ;
}
}
2018-03-03 00:21:37 +03:00
TALLOC_FREE ( psd ) ;
2014-11-26 20:11:17 +03:00
return NT_STATUS_OK ;
}
2017-06-03 13:57:59 +03:00
static struct vfs_offload_ctx * fruit_offload_ctx ;
struct fruit_offload_read_state {
struct vfs_handle_struct * handle ;
struct tevent_context * ev ;
files_struct * fsp ;
uint32_t fsctl ;
DATA_BLOB token ;
} ;
static void fruit_offload_read_done ( struct tevent_req * subreq ) ;
static struct tevent_req * fruit_offload_read_send (
TALLOC_CTX * mem_ctx ,
struct tevent_context * ev ,
struct vfs_handle_struct * handle ,
files_struct * fsp ,
uint32_t fsctl ,
uint32_t ttl ,
off_t offset ,
size_t to_copy )
{
struct tevent_req * req = NULL ;
struct tevent_req * subreq = NULL ;
struct fruit_offload_read_state * state = NULL ;
req = tevent_req_create ( mem_ctx , & state ,
struct fruit_offload_read_state ) ;
if ( req = = NULL ) {
return NULL ;
}
* state = ( struct fruit_offload_read_state ) {
. handle = handle ,
. ev = ev ,
. fsp = fsp ,
. fsctl = fsctl ,
} ;
subreq = SMB_VFS_NEXT_OFFLOAD_READ_SEND ( mem_ctx , ev , handle , fsp ,
fsctl , ttl , offset , to_copy ) ;
if ( tevent_req_nomem ( subreq , req ) ) {
return tevent_req_post ( req , ev ) ;
}
tevent_req_set_callback ( subreq , fruit_offload_read_done , req ) ;
return req ;
}
static void fruit_offload_read_done ( struct tevent_req * subreq )
{
struct tevent_req * req = tevent_req_callback_data (
subreq , struct tevent_req ) ;
struct fruit_offload_read_state * state = tevent_req_data (
req , struct fruit_offload_read_state ) ;
NTSTATUS status ;
status = SMB_VFS_NEXT_OFFLOAD_READ_RECV ( subreq ,
state - > handle ,
state ,
& state - > token ) ;
TALLOC_FREE ( subreq ) ;
if ( tevent_req_nterror ( req , status ) ) {
return ;
}
if ( state - > fsctl ! = FSCTL_SRV_REQUEST_RESUME_KEY ) {
tevent_req_done ( req ) ;
return ;
}
status = vfs_offload_token_ctx_init ( state - > fsp - > conn - > sconn - > client ,
& fruit_offload_ctx ) ;
if ( tevent_req_nterror ( req , status ) ) {
return ;
}
status = vfs_offload_token_db_store_fsp ( fruit_offload_ctx ,
state - > fsp ,
& state - > token ) ;
if ( tevent_req_nterror ( req , status ) ) {
return ;
}
tevent_req_done ( req ) ;
return ;
}
static NTSTATUS fruit_offload_read_recv ( struct tevent_req * req ,
struct vfs_handle_struct * handle ,
TALLOC_CTX * mem_ctx ,
DATA_BLOB * token )
{
struct fruit_offload_read_state * state = tevent_req_data (
req , struct fruit_offload_read_state ) ;
NTSTATUS status ;
if ( tevent_req_is_nterror ( req , & status ) ) {
tevent_req_received ( req ) ;
return status ;
}
token - > length = state - > token . length ;
token - > data = talloc_move ( mem_ctx , & state - > token . data ) ;
tevent_req_received ( req ) ;
return NT_STATUS_OK ;
}
2017-06-04 14:50:33 +03:00
struct fruit_offload_write_state {
2015-04-22 23:29:16 +03:00
struct vfs_handle_struct * handle ;
off_t copied ;
struct files_struct * src_fsp ;
struct files_struct * dst_fsp ;
bool is_copyfile ;
} ;
2017-06-04 14:50:33 +03:00
static void fruit_offload_write_done ( struct tevent_req * subreq ) ;
static struct tevent_req * fruit_offload_write_send ( struct vfs_handle_struct * handle ,
2015-04-22 23:29:16 +03:00
TALLOC_CTX * mem_ctx ,
struct tevent_context * ev ,
2017-06-09 14:02:49 +03:00
uint32_t fsctl ,
DATA_BLOB * token ,
off_t transfer_offset ,
2015-04-22 23:29:16 +03:00
struct files_struct * dest_fsp ,
off_t dest_off ,
2017-06-10 10:05:55 +03:00
off_t num )
2015-04-22 23:29:16 +03:00
{
struct tevent_req * req , * subreq ;
2017-06-04 14:50:33 +03:00
struct fruit_offload_write_state * state ;
2015-04-22 23:29:16 +03:00
NTSTATUS status ;
struct fruit_config_data * config ;
2017-06-09 14:02:49 +03:00
off_t src_off = transfer_offset ;
files_struct * src_fsp = NULL ;
2015-04-22 23:29:16 +03:00
off_t to_copy = num ;
2017-06-09 18:27:17 +03:00
bool copyfile_enabled = false ;
2015-04-22 23:29:16 +03:00
2015-07-11 14:44:05 +03:00
DEBUG ( 10 , ( " soff: %ju, doff: %ju, len: %ju \n " ,
( uintmax_t ) src_off , ( uintmax_t ) dest_off , ( uintmax_t ) num ) ) ;
2015-04-22 23:29:16 +03:00
SMB_VFS_HANDLE_GET_DATA ( handle , config ,
struct fruit_config_data ,
return NULL ) ;
2017-06-04 14:50:33 +03:00
req = tevent_req_create ( mem_ctx , & state ,
struct fruit_offload_write_state ) ;
2015-04-22 23:29:16 +03:00
if ( req = = NULL ) {
return NULL ;
}
2017-06-04 14:50:33 +03:00
state - > handle = handle ;
state - > dst_fsp = dest_fsp ;
2015-04-22 23:29:16 +03:00
2017-06-09 14:02:49 +03:00
switch ( fsctl ) {
case FSCTL_SRV_COPYCHUNK :
case FSCTL_SRV_COPYCHUNK_WRITE :
2017-06-09 18:27:17 +03:00
copyfile_enabled = config - > copyfile_enabled ;
2017-06-09 14:02:49 +03:00
break ;
default :
break ;
}
2015-04-22 23:29:16 +03:00
/*
* Check if this a OS X copyfile style copychunk request with
* a requested chunk count of 0 that was translated to a
2017-06-04 14:50:33 +03:00
* offload_write_send VFS call overloading the parameters src_off
2015-04-22 23:29:16 +03:00
* = dest_off = num = 0.
*/
2017-06-09 18:27:17 +03:00
if ( copyfile_enabled & & num = = 0 & & src_off = = 0 & & dest_off = = 0 ) {
status = vfs_offload_token_db_fetch_fsp (
fruit_offload_ctx , token , & src_fsp ) ;
if ( tevent_req_nterror ( req , status ) ) {
return tevent_req_post ( req , ev ) ;
}
state - > src_fsp = src_fsp ;
2015-04-22 23:29:16 +03:00
status = vfs_stat_fsp ( src_fsp ) ;
if ( tevent_req_nterror ( req , status ) ) {
return tevent_req_post ( req , ev ) ;
}
to_copy = src_fsp - > fsp_name - > st . st_ex_size ;
2017-06-04 14:50:33 +03:00
state - > is_copyfile = true ;
2015-04-22 23:29:16 +03:00
}
2017-06-04 14:50:33 +03:00
subreq = SMB_VFS_NEXT_OFFLOAD_WRITE_SEND ( handle ,
2015-04-22 23:29:16 +03:00
mem_ctx ,
ev ,
2017-06-09 14:02:49 +03:00
fsctl ,
token ,
transfer_offset ,
2015-04-22 23:29:16 +03:00
dest_fsp ,
dest_off ,
2017-06-10 10:05:55 +03:00
to_copy ) ;
2015-04-22 23:29:16 +03:00
if ( tevent_req_nomem ( subreq , req ) ) {
return tevent_req_post ( req , ev ) ;
}
2017-06-04 14:50:33 +03:00
tevent_req_set_callback ( subreq , fruit_offload_write_done , req ) ;
2015-04-22 23:29:16 +03:00
return req ;
}
2017-06-04 14:50:33 +03:00
static void fruit_offload_write_done ( struct tevent_req * subreq )
2015-04-22 23:29:16 +03:00
{
struct tevent_req * req = tevent_req_callback_data (
subreq , struct tevent_req ) ;
2017-06-04 14:50:33 +03:00
struct fruit_offload_write_state * state = tevent_req_data (
req , struct fruit_offload_write_state ) ;
2015-04-22 23:29:16 +03:00
NTSTATUS status ;
unsigned int num_streams = 0 ;
struct stream_struct * streams = NULL ;
2016-05-19 19:42:04 +03:00
unsigned int i ;
2015-04-22 23:29:16 +03:00
struct smb_filename * src_fname_tmp = NULL ;
struct smb_filename * dst_fname_tmp = NULL ;
2017-06-04 14:50:33 +03:00
status = SMB_VFS_NEXT_OFFLOAD_WRITE_RECV ( state - > handle ,
2015-04-22 23:29:16 +03:00
subreq ,
& state - > copied ) ;
TALLOC_FREE ( subreq ) ;
if ( tevent_req_nterror ( req , status ) ) {
return ;
}
if ( ! state - > is_copyfile ) {
tevent_req_done ( req ) ;
return ;
}
/*
2017-01-20 01:49:54 +03:00
* Now copy all remaining streams . We know the share supports
2015-04-22 23:29:16 +03:00
* streams , because we ' re in vfs_fruit . We don ' t do this async
* because streams are few and small .
*/
2016-03-05 01:01:47 +03:00
status = vfs_streaminfo ( state - > handle - > conn , state - > src_fsp ,
2016-03-05 01:16:13 +03:00
state - > src_fsp - > fsp_name ,
2015-04-22 23:29:16 +03:00
req , & num_streams , & streams ) ;
if ( tevent_req_nterror ( req , status ) ) {
return ;
}
if ( num_streams = = 1 ) {
/* There is always one stream, ::$DATA. */
tevent_req_done ( req ) ;
return ;
}
for ( i = 0 ; i < num_streams ; i + + ) {
2015-07-01 02:43:09 +03:00
DEBUG ( 10 , ( " %s: stream: '%s'/%zu \n " ,
__func__ , streams [ i ] . name , ( size_t ) streams [ i ] . size ) ) ;
2015-04-22 23:29:16 +03:00
src_fname_tmp = synthetic_smb_fname (
req ,
state - > src_fsp - > fsp_name - > base_name ,
streams [ i ] . name ,
2016-03-19 07:19:38 +03:00
NULL ,
state - > src_fsp - > fsp_name - > flags ) ;
2015-04-22 23:29:16 +03:00
if ( tevent_req_nomem ( src_fname_tmp , req ) ) {
return ;
}
if ( is_ntfs_default_stream_smb_fname ( src_fname_tmp ) ) {
TALLOC_FREE ( src_fname_tmp ) ;
continue ;
}
dst_fname_tmp = synthetic_smb_fname (
req ,
state - > dst_fsp - > fsp_name - > base_name ,
streams [ i ] . name ,
2016-03-19 07:19:38 +03:00
NULL ,
state - > dst_fsp - > fsp_name - > flags ) ;
2015-04-22 23:29:16 +03:00
if ( tevent_req_nomem ( dst_fname_tmp , req ) ) {
TALLOC_FREE ( src_fname_tmp ) ;
return ;
}
status = copy_file ( req ,
state - > handle - > conn ,
src_fname_tmp ,
dst_fname_tmp ,
OPENX_FILE_CREATE_IF_NOT_EXIST ,
0 , false ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
DEBUG ( 1 , ( " %s: copy %s to %s failed: %s \n " , __func__ ,
smb_fname_str_dbg ( src_fname_tmp ) ,
smb_fname_str_dbg ( dst_fname_tmp ) ,
nt_errstr ( status ) ) ) ;
TALLOC_FREE ( src_fname_tmp ) ;
TALLOC_FREE ( dst_fname_tmp ) ;
tevent_req_nterror ( req , status ) ;
return ;
}
TALLOC_FREE ( src_fname_tmp ) ;
TALLOC_FREE ( dst_fname_tmp ) ;
}
TALLOC_FREE ( streams ) ;
TALLOC_FREE ( src_fname_tmp ) ;
TALLOC_FREE ( dst_fname_tmp ) ;
tevent_req_done ( req ) ;
}
2017-06-04 14:50:33 +03:00
static NTSTATUS fruit_offload_write_recv ( struct vfs_handle_struct * handle ,
2015-04-22 23:29:16 +03:00
struct tevent_req * req ,
off_t * copied )
{
2017-06-04 14:50:33 +03:00
struct fruit_offload_write_state * state = tevent_req_data (
req , struct fruit_offload_write_state ) ;
2015-04-22 23:29:16 +03:00
NTSTATUS status ;
if ( tevent_req_is_nterror ( req , & status ) ) {
DEBUG ( 1 , ( " server side copy chunk failed: %s \n " ,
nt_errstr ( status ) ) ) ;
* copied = 0 ;
tevent_req_received ( req ) ;
return status ;
}
2017-06-04 14:50:33 +03:00
* copied = state - > copied ;
2015-04-22 23:29:16 +03:00
tevent_req_received ( req ) ;
return NT_STATUS_OK ;
}
2017-11-03 12:56:29 +03:00
static char * fruit_get_bandsize_line ( char * * lines , int numlines )
{
static regex_t re ;
static bool re_initialized = false ;
int i ;
int ret ;
if ( ! re_initialized ) {
ret = regcomp ( & re , " ^[[:blank:]]*<key>band-size</key>$ " , 0 ) ;
if ( ret ! = 0 ) {
return NULL ;
}
re_initialized = true ;
}
for ( i = 0 ; i < numlines ; i + + ) {
regmatch_t matches [ 1 ] ;
ret = regexec ( & re , lines [ i ] , 1 , matches , 0 ) ;
if ( ret = = 0 ) {
/*
* Check if the match was on the last line , sa we want
* the subsequent line .
*/
if ( i + 1 = = numlines ) {
return NULL ;
}
return lines [ i + 1 ] ;
}
if ( ret ! = REG_NOMATCH ) {
return NULL ;
}
}
return NULL ;
}
static bool fruit_get_bandsize_from_line ( char * line , size_t * _band_size )
{
static regex_t re ;
static bool re_initialized = false ;
regmatch_t matches [ 2 ] ;
uint64_t band_size ;
int ret ;
bool ok ;
if ( ! re_initialized ) {
ret = regcomp ( & re ,
" ^[[:blank:]]* "
" <integer> \\ ([[:digit:]]* \\ )</integer>$ " ,
0 ) ;
if ( ret ! = 0 ) {
return false ;
}
re_initialized = true ;
}
ret = regexec ( & re , line , 2 , matches , 0 ) ;
if ( ret ! = 0 ) {
DBG_ERR ( " regex failed [%s] \n " , line ) ;
return false ;
}
line [ matches [ 1 ] . rm_eo ] = ' \0 ' ;
ok = conv_str_u64 ( & line [ matches [ 1 ] . rm_so ] , & band_size ) ;
if ( ! ok ) {
return false ;
}
* _band_size = ( size_t ) band_size ;
return true ;
}
/*
* This reads and parses an Info . plist from a TM sparsebundle looking for the
* " band-size " key and value .
*/
static bool fruit_get_bandsize ( vfs_handle_struct * handle ,
const char * dir ,
size_t * band_size )
{
# define INFO_PLIST_MAX_SIZE 64*1024
char * plist = NULL ;
struct smb_filename * smb_fname = NULL ;
files_struct * fsp = NULL ;
uint8_t * file_data = NULL ;
char * * lines = NULL ;
char * band_size_line = NULL ;
size_t plist_file_size ;
ssize_t nread ;
int numlines ;
int ret ;
bool ok = false ;
NTSTATUS status ;
plist = talloc_asprintf ( talloc_tos ( ) ,
" %s/%s/Info.plist " ,
handle - > conn - > connectpath ,
dir ) ;
if ( plist = = NULL ) {
ok = false ;
goto out ;
}
smb_fname = synthetic_smb_fname ( talloc_tos ( ) , plist , NULL , NULL , 0 ) ;
if ( smb_fname = = NULL ) {
ok = false ;
goto out ;
}
ret = SMB_VFS_NEXT_LSTAT ( handle , smb_fname ) ;
if ( ret ! = 0 ) {
DBG_INFO ( " Ignoring Sparsebundle without Info.plist [%s] \n " , dir ) ;
ok = true ;
goto out ;
}
plist_file_size = smb_fname - > st . st_ex_size ;
if ( plist_file_size > INFO_PLIST_MAX_SIZE ) {
DBG_INFO ( " %s is too large, ignoring \n " , plist ) ;
ok = true ;
goto out ;
}
status = SMB_VFS_NEXT_CREATE_FILE (
handle , /* conn */
NULL , /* req */
0 , /* root_dir_fid */
smb_fname , /* fname */
FILE_GENERIC_READ , /* access_mask */
FILE_SHARE_READ | FILE_SHARE_WRITE , /* share_access */
FILE_OPEN , /* create_disposition */
0 , /* create_options */
0 , /* file_attributes */
INTERNAL_OPEN_ONLY , /* oplock_request */
NULL , /* lease */
0 , /* allocation_size */
0 , /* private_flags */
NULL , /* sd */
NULL , /* ea_list */
& fsp , /* result */
NULL , /* psbuf */
NULL , NULL ) ; /* create context */
if ( ! NT_STATUS_IS_OK ( status ) ) {
DBG_INFO ( " Opening [%s] failed [%s] \n " ,
smb_fname_str_dbg ( smb_fname ) , nt_errstr ( status ) ) ;
ok = false ;
goto out ;
}
file_data = talloc_array ( talloc_tos ( ) , uint8_t , plist_file_size ) ;
if ( file_data = = NULL ) {
ok = false ;
goto out ;
}
nread = SMB_VFS_NEXT_PREAD ( handle , fsp , file_data , plist_file_size , 0 ) ;
if ( nread ! = plist_file_size ) {
DBG_ERR ( " Short read on [%s]: %zu/%zd \n " ,
fsp_str_dbg ( fsp ) , nread , plist_file_size ) ;
ok = false ;
goto out ;
}
status = close_file ( NULL , fsp , NORMAL_CLOSE ) ;
fsp = NULL ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
DBG_ERR ( " close_file failed: %s \n " , nt_errstr ( status ) ) ;
ok = false ;
goto out ;
}
lines = file_lines_parse ( ( char * ) file_data ,
plist_file_size ,
& numlines ,
talloc_tos ( ) ) ;
if ( lines = = NULL ) {
ok = false ;
goto out ;
}
band_size_line = fruit_get_bandsize_line ( lines , numlines ) ;
if ( band_size_line = = NULL ) {
DBG_ERR ( " Didn't find band-size key in [%s] \n " ,
smb_fname_str_dbg ( smb_fname ) ) ;
ok = false ;
goto out ;
}
ok = fruit_get_bandsize_from_line ( band_size_line , band_size ) ;
if ( ! ok ) {
DBG_ERR ( " fruit_get_bandsize_from_line failed \n " ) ;
goto out ;
}
DBG_DEBUG ( " Parsed band-size [%zu] for [%s] \n " , * band_size , plist ) ;
out :
if ( fsp ! = NULL ) {
status = close_file ( NULL , fsp , NORMAL_CLOSE ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
DBG_ERR ( " close_file failed: %s \n " , nt_errstr ( status ) ) ;
}
fsp = NULL ;
}
TALLOC_FREE ( plist ) ;
TALLOC_FREE ( smb_fname ) ;
TALLOC_FREE ( file_data ) ;
TALLOC_FREE ( lines ) ;
return ok ;
}
struct fruit_disk_free_state {
2018-02-22 17:52:46 +03:00
off_t total_size ;
2017-11-03 12:56:29 +03:00
} ;
static bool fruit_get_num_bands ( vfs_handle_struct * handle ,
char * bundle ,
size_t * _nbands )
{
char * path = NULL ;
struct smb_filename * bands_dir = NULL ;
DIR * d = NULL ;
struct dirent * e = NULL ;
size_t nbands ;
int ret ;
path = talloc_asprintf ( talloc_tos ( ) ,
" %s/%s/bands " ,
handle - > conn - > connectpath ,
bundle ) ;
if ( path = = NULL ) {
return false ;
}
bands_dir = synthetic_smb_fname ( talloc_tos ( ) ,
path ,
NULL ,
NULL ,
0 ) ;
TALLOC_FREE ( path ) ;
if ( bands_dir = = NULL ) {
return false ;
}
d = SMB_VFS_NEXT_OPENDIR ( handle , bands_dir , NULL , 0 ) ;
if ( d = = NULL ) {
TALLOC_FREE ( bands_dir ) ;
return false ;
}
nbands = 0 ;
for ( e = SMB_VFS_NEXT_READDIR ( handle , d , NULL ) ;
e ! = NULL ;
e = SMB_VFS_NEXT_READDIR ( handle , d , NULL ) )
{
if ( ISDOT ( e - > d_name ) | | ISDOTDOT ( e - > d_name ) ) {
continue ;
}
nbands + + ;
}
ret = SMB_VFS_NEXT_CLOSEDIR ( handle , d ) ;
if ( ret ! = 0 ) {
TALLOC_FREE ( bands_dir ) ;
return false ;
}
DBG_DEBUG ( " %zu bands in [%s] \n " , nbands , smb_fname_str_dbg ( bands_dir ) ) ;
TALLOC_FREE ( bands_dir ) ;
* _nbands = nbands ;
return true ;
}
static bool fruit_tmsize_do_dirent ( vfs_handle_struct * handle ,
struct fruit_disk_free_state * state ,
struct dirent * e )
{
bool ok ;
char * p = NULL ;
size_t sparsebundle_strlen = strlen ( " sparsebundle " ) ;
2018-01-09 14:08:01 +03:00
size_t bandsize = 0 ;
2017-11-03 12:56:29 +03:00
size_t nbands ;
2018-02-22 17:52:46 +03:00
off_t tm_size ;
2017-11-03 12:56:29 +03:00
p = strstr ( e - > d_name , " sparsebundle " ) ;
if ( p = = NULL ) {
return true ;
}
if ( p [ sparsebundle_strlen ] ! = ' \0 ' ) {
return true ;
}
DBG_DEBUG ( " Processing sparsebundle [%s] \n " , e - > d_name ) ;
ok = fruit_get_bandsize ( handle , e - > d_name , & bandsize ) ;
if ( ! ok ) {
/*
* Beware of race conditions : this may be an uninitialized
* Info . plist that a client is just creating . We don ' t want let
* this to trigger complete failure .
*/
DBG_ERR ( " Processing sparsebundle [%s] failed \n " , e - > d_name ) ;
return true ;
}
ok = fruit_get_num_bands ( handle , e - > d_name , & nbands ) ;
if ( ! ok ) {
/*
* Beware of race conditions : this may be a backup sparsebundle
* in an early stage lacking a bands subdirectory . We don ' t want
* let this to trigger complete failure .
*/
DBG_ERR ( " Processing sparsebundle [%s] failed \n " , e - > d_name ) ;
return true ;
}
2018-03-28 15:17:59 +03:00
if ( bandsize > SIZE_MAX / nbands ) {
2017-11-03 12:56:29 +03:00
DBG_ERR ( " tmsize overflow: bandsize [%zu] nbands [%zu] \n " ,
bandsize , nbands ) ;
return false ;
}
2018-03-28 15:17:59 +03:00
tm_size = bandsize * nbands ;
2017-11-03 12:56:29 +03:00
if ( state - > total_size + tm_size < state - > total_size ) {
DBG_ERR ( " tmsize overflow: bandsize [%zu] nbands [%zu] \n " ,
bandsize , nbands ) ;
return false ;
}
state - > total_size + = tm_size ;
2018-02-22 17:52:46 +03:00
DBG_DEBUG ( " [%s] tm_size [%jd] total_size [%jd] \n " ,
e - > d_name , ( intmax_t ) tm_size , ( intmax_t ) state - > total_size ) ;
2017-11-03 12:56:29 +03:00
return true ;
}
/**
* Calculate used size of a TimeMachine volume
*
* This assumes that the volume is used only for TimeMachine .
*
* - readdir ( basedir of share ) , then
* - for every element that matches regex " ^ \ (.* \ ) \ .sparsebundle$ " :
* - parse " \1 .sparsebundle/Info.plist " and read the band - size XML key
* - count band files in " \1 .sparsebundle/bands/ "
* - calculate used size of all bands : band_count * band_size
* */
static uint64_t fruit_disk_free ( vfs_handle_struct * handle ,
const struct smb_filename * smb_fname ,
uint64_t * _bsize ,
uint64_t * _dfree ,
uint64_t * _dsize )
{
struct fruit_config_data * config = NULL ;
struct fruit_disk_free_state state = { 0 } ;
DIR * d = NULL ;
struct dirent * e = NULL ;
uint64_t dfree ;
uint64_t dsize ;
int ret ;
bool ok ;
SMB_VFS_HANDLE_GET_DATA ( handle , config ,
struct fruit_config_data ,
return UINT64_MAX ) ;
if ( ! config - > time_machine | |
config - > time_machine_max_size = = 0 )
{
return SMB_VFS_NEXT_DISK_FREE ( handle ,
smb_fname ,
_bsize ,
_dfree ,
_dsize ) ;
}
d = SMB_VFS_NEXT_OPENDIR ( handle , smb_fname , NULL , 0 ) ;
if ( d = = NULL ) {
return UINT64_MAX ;
}
for ( e = SMB_VFS_NEXT_READDIR ( handle , d , NULL ) ;
e ! = NULL ;
e = SMB_VFS_NEXT_READDIR ( handle , d , NULL ) )
{
ok = fruit_tmsize_do_dirent ( handle , & state , e ) ;
if ( ! ok ) {
SMB_VFS_NEXT_CLOSEDIR ( handle , d ) ;
return UINT64_MAX ;
}
}
ret = SMB_VFS_NEXT_CLOSEDIR ( handle , d ) ;
if ( ret ! = 0 ) {
return UINT64_MAX ;
}
dsize = config - > time_machine_max_size / 512 ;
dfree = dsize - ( state . total_size / 512 ) ;
if ( dfree > dsize ) {
dfree = 0 ;
}
* _bsize = 512 ;
* _dsize = dsize ;
* _dfree = dfree ;
return dfree / 2 ;
}
2014-06-23 18:59:45 +04:00
static struct vfs_fn_pointers vfs_fruit_fns = {
. connect_fn = fruit_connect ,
2017-11-03 12:56:29 +03:00
. disk_free_fn = fruit_disk_free ,
2014-06-23 18:59:45 +04:00
/* File operations */
. chmod_fn = fruit_chmod ,
. chown_fn = fruit_chown ,
. unlink_fn = fruit_unlink ,
. rename_fn = fruit_rename ,
. rmdir_fn = fruit_rmdir ,
. open_fn = fruit_open ,
. pread_fn = fruit_pread ,
. pwrite_fn = fruit_pwrite ,
2017-05-12 15:40:03 +03:00
. pread_send_fn = fruit_pread_send ,
. pread_recv_fn = fruit_pread_recv ,
. pwrite_send_fn = fruit_pwrite_send ,
. pwrite_recv_fn = fruit_pwrite_recv ,
2014-06-23 18:59:45 +04:00
. stat_fn = fruit_stat ,
. lstat_fn = fruit_lstat ,
. fstat_fn = fruit_fstat ,
. streaminfo_fn = fruit_streaminfo ,
. ntimes_fn = fruit_ntimes ,
. ftruncate_fn = fruit_ftruncate ,
. fallocate_fn = fruit_fallocate ,
. create_file_fn = fruit_create_file ,
2014-11-26 20:11:17 +03:00
. readdir_attr_fn = fruit_readdir_attr ,
2017-06-03 13:57:59 +03:00
. offload_read_send_fn = fruit_offload_read_send ,
. offload_read_recv_fn = fruit_offload_read_recv ,
2017-06-04 14:50:33 +03:00
. offload_write_send_fn = fruit_offload_write_send ,
. offload_write_recv_fn = fruit_offload_write_recv ,
2014-11-26 20:11:17 +03:00
/* NT ACL operations */
. fget_nt_acl_fn = fruit_fget_nt_acl ,
. fset_nt_acl_fn = fruit_fset_nt_acl ,
2014-06-23 18:59:45 +04:00
} ;
2017-12-16 01:32:12 +03:00
static_decl_vfs ;
2017-04-20 22:24:43 +03:00
NTSTATUS vfs_fruit_init ( TALLOC_CTX * ctx )
2014-06-23 18:59:45 +04:00
{
NTSTATUS ret = smb_register_vfs ( SMB_VFS_INTERFACE_VERSION , " fruit " ,
& vfs_fruit_fns ) ;
if ( ! NT_STATUS_IS_OK ( ret ) ) {
return ret ;
}
vfs_fruit_debug_level = debug_add_class ( " fruit " ) ;
if ( vfs_fruit_debug_level = = - 1 ) {
vfs_fruit_debug_level = DBGC_VFS ;
DEBUG ( 0 , ( " %s: Couldn't register custom debugging class! \n " ,
" vfs_fruit_init " ) ) ;
} else {
DEBUG ( 10 , ( " %s: Debug class number of '%s': %d \n " ,
" vfs_fruit_init " , " fruit " , vfs_fruit_debug_level ) ) ;
}
return ret ;
}