2020-08-22 12:40:53 +00:00
/*
* s3fs - FUSE - based file system backed by Amazon S3
*
* Copyright ( C ) 2007 Takeshi Nakatani < ggtakec . com >
*
* 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 2
* 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 , write to the Free Software
* Foundation , Inc . , 51 Franklin Street , Fifth Floor , Boston , MA 02110 - 1301 , USA .
*/
# include <cstdio>
# include <cstdlib>
# include <cerrno>
# include <unistd.h>
# include <limits.h>
# include <sys/time.h>
# include "common.h"
# include "s3fs.h"
# include "fdcache_entity.h"
# include "fdcache.h"
# include "string_util.h"
2020-09-20 14:04:59 +00:00
# include "s3fs_util.h"
2020-08-22 12:40:53 +00:00
# include "autolock.h"
# include "curl.h"
//------------------------------------------------
// Symbols
//------------------------------------------------
static const int MAX_MULTIPART_CNT = 10 * 1000 ; // S3 multipart max count
//------------------------------------------------
// FdEntity class variables
//------------------------------------------------
bool FdEntity : : mixmultipart = true ;
//------------------------------------------------
// FdEntity class methods
//------------------------------------------------
bool FdEntity : : SetNoMixMultipart ( )
{
bool old = mixmultipart ;
mixmultipart = false ;
return old ;
}
int FdEntity : : FillFile ( int fd , unsigned char byte , off_t size , off_t start )
{
unsigned char bytes [ 1024 * 32 ] ; // 32kb
2020-09-11 09:37:24 +00:00
memset ( bytes , byte , std : : min ( static_cast < off_t > ( sizeof ( bytes ) ) , size ) ) ;
2020-08-22 12:40:53 +00:00
for ( off_t total = 0 , onewrote = 0 ; total < size ; total + = onewrote ) {
2020-09-11 09:37:24 +00:00
if ( - 1 = = ( onewrote = pwrite ( fd , bytes , std : : min ( static_cast < off_t > ( sizeof ( bytes ) ) , size - total ) , start + total ) ) ) {
2020-08-22 12:40:53 +00:00
S3FS_PRN_ERR ( " pwrite failed. errno(%d) " , errno ) ;
return - errno ;
}
}
return 0 ;
}
// [NOTE]
// If fd is wrong or something error is occurred, return 0.
// The ino_t is allowed zero, but inode 0 is not realistic.
// So this method returns 0 on error assuming the correct
// inode is never 0.
// The caller must have exclusive control.
//
ino_t FdEntity : : GetInode ( int fd )
{
if ( - 1 = = fd ) {
S3FS_PRN_ERR ( " file descriptor is wrong. " ) ;
return 0 ;
}
struct stat st ;
if ( 0 ! = fstat ( fd , & st ) ) {
S3FS_PRN_ERR ( " could not get stat for file descriptor(%d) by errno(%d). " , fd , errno ) ;
return 0 ;
}
return st . st_ino ;
}
//------------------------------------------------
// FdEntity methods
//------------------------------------------------
FdEntity : : FdEntity ( const char * tpath , const char * cpath ) :
2021-05-23 16:28:50 +00:00
is_lock_init ( false ) , path ( SAFESTRPTR ( tpath ) ) ,
physical_fd ( - 1 ) , pfile ( NULL ) , inode ( 0 ) , size_orgmeta ( 0 ) ,
2021-04-18 04:11:12 +00:00
cachepath ( SAFESTRPTR ( cpath ) ) , is_meta_pending ( false )
2020-08-22 12:40:53 +00:00
{
2021-04-18 04:11:12 +00:00
holding_mtime . tv_sec = - 1 ;
holding_mtime . tv_nsec = 0 ;
2020-08-22 12:40:53 +00:00
pthread_mutexattr_t attr ;
pthread_mutexattr_init ( & attr ) ;
# if S3FS_PTHREAD_ERRORCHECK
pthread_mutexattr_settype ( & attr , PTHREAD_MUTEX_ERRORCHECK ) ;
# endif
2021-01-24 22:56:10 +00:00
int result ;
if ( 0 ! = ( result = pthread_mutex_init ( & fdent_lock , & attr ) ) ) {
S3FS_PRN_CRIT ( " failed to init fdent_lock: %d " , result ) ;
2020-08-22 12:40:53 +00:00
abort ( ) ;
}
2021-01-24 22:56:10 +00:00
if ( 0 ! = ( result = pthread_mutex_init ( & fdent_data_lock , & attr ) ) ) {
S3FS_PRN_CRIT ( " failed to init fdent_data_lock: %d " , result ) ;
2020-08-22 12:40:53 +00:00
abort ( ) ;
}
is_lock_init = true ;
}
FdEntity : : ~ FdEntity ( )
{
Clear ( ) ;
if ( is_lock_init ) {
2021-01-24 22:56:10 +00:00
int result ;
if ( 0 ! = ( result = pthread_mutex_destroy ( & fdent_data_lock ) ) ) {
S3FS_PRN_CRIT ( " failed to destroy fdent_data_lock: %d " , result ) ;
2020-08-22 12:40:53 +00:00
abort ( ) ;
}
2021-01-24 22:56:10 +00:00
if ( 0 ! = ( result = pthread_mutex_destroy ( & fdent_lock ) ) ) {
S3FS_PRN_CRIT ( " failed to destroy fdent_lock: %d " , result ) ;
2020-08-22 12:40:53 +00:00
abort ( ) ;
}
is_lock_init = false ;
}
}
void FdEntity : : Clear ( )
{
AutoLock auto_lock ( & fdent_lock ) ;
AutoLock auto_data_lock ( & fdent_data_lock ) ;
2021-05-23 16:28:50 +00:00
for ( fdinfo_map_t : : iterator iter = pseudo_fd_map . begin ( ) ; iter ! = pseudo_fd_map . end ( ) ; + + iter ) {
PseudoFdInfo * ppseudofdinfo = iter - > second ;
if ( ppseudofdinfo ) {
delete ppseudofdinfo ;
}
}
pseudo_fd_map . clear ( ) ;
if ( - 1 ! = physical_fd ) {
2020-08-22 12:40:53 +00:00
if ( ! cachepath . empty ( ) ) {
// [NOTE]
// Compare the inode of the existing cache file with the inode of
// the cache file output by this object, and if they are the same,
// serialize the pagelist.
//
ino_t cur_inode = GetInode ( ) ;
if ( 0 ! = cur_inode & & cur_inode = = inode ) {
CacheFileStat cfstat ( path . c_str ( ) ) ;
if ( ! pagelist . Serialize ( cfstat , true , inode ) ) {
S3FS_PRN_WARN ( " failed to save cache stat file(%s). " , path . c_str ( ) ) ;
}
}
}
if ( pfile ) {
fclose ( pfile ) ;
pfile = NULL ;
}
2021-05-23 16:28:50 +00:00
physical_fd = - 1 ;
inode = 0 ;
2020-08-22 12:40:53 +00:00
if ( ! mirrorpath . empty ( ) ) {
if ( - 1 = = unlink ( mirrorpath . c_str ( ) ) ) {
S3FS_PRN_WARN ( " failed to remove mirror cache file(%s) by errno(%d). " , mirrorpath . c_str ( ) , errno ) ;
}
mirrorpath . erase ( ) ;
}
}
pagelist . Init ( 0 , false , false ) ;
2021-05-23 16:28:50 +00:00
path = " " ;
cachepath = " " ;
2020-08-22 12:40:53 +00:00
}
// [NOTE]
// This method returns the inode of the file in cachepath.
// The return value is the same as the class method GetInode().
// The caller must have exclusive control.
//
ino_t FdEntity : : GetInode ( )
{
if ( cachepath . empty ( ) ) {
S3FS_PRN_INFO ( " cache file path is empty, then return inode as 0. " ) ;
return 0 ;
}
struct stat st ;
if ( 0 ! = stat ( cachepath . c_str ( ) , & st ) ) {
S3FS_PRN_INFO ( " could not get stat for file(%s) by errno(%d). " , cachepath . c_str ( ) , errno ) ;
return 0 ;
}
return st . st_ino ;
}
2021-05-23 16:28:50 +00:00
void FdEntity : : Close ( int fd )
2020-08-22 12:40:53 +00:00
{
AutoLock auto_lock ( & fdent_lock ) ;
2021-05-23 16:28:50 +00:00
S3FS_PRN_DBG ( " [path=%s][pseudo_fd=%d][physical_fd=%d] " , path . c_str ( ) , fd , physical_fd ) ;
2020-08-22 12:40:53 +00:00
2021-05-23 16:28:50 +00:00
// search pseudo fd and close it.
fdinfo_map_t : : iterator iter = pseudo_fd_map . find ( fd ) ;
if ( pseudo_fd_map . end ( ) ! = iter ) {
PseudoFdInfo * ppseudoinfo = iter - > second ;
pseudo_fd_map . erase ( iter ) ;
delete ppseudoinfo ;
} else {
S3FS_PRN_WARN ( " Not found pseudo_fd(%d) in entity object(%s) " , fd , path . c_str ( ) ) ;
}
// check pseudo fd count
if ( - 1 ! = physical_fd & & 0 = = GetOpenCount ( true ) ) {
AutoLock auto_data_lock ( & fdent_data_lock ) ;
if ( ! cachepath . empty ( ) ) {
// [NOTE]
// Compare the inode of the existing cache file with the inode of
// the cache file output by this object, and if they are the same,
// serialize the pagelist.
//
ino_t cur_inode = GetInode ( ) ;
if ( 0 ! = cur_inode & & cur_inode = = inode ) {
CacheFileStat cfstat ( path . c_str ( ) ) ;
if ( ! pagelist . Serialize ( cfstat , true , inode ) ) {
S3FS_PRN_WARN ( " failed to save cache stat file(%s). " , path . c_str ( ) ) ;
2020-08-22 12:40:53 +00:00
}
}
2021-05-23 16:28:50 +00:00
}
if ( pfile ) {
fclose ( pfile ) ;
pfile = NULL ;
}
physical_fd = - 1 ;
inode = 0 ;
2020-08-22 12:40:53 +00:00
2021-05-23 16:28:50 +00:00
if ( ! mirrorpath . empty ( ) ) {
if ( - 1 = = unlink ( mirrorpath . c_str ( ) ) ) {
S3FS_PRN_WARN ( " failed to remove mirror cache file(%s) by errno(%d). " , mirrorpath . c_str ( ) , errno ) ;
2020-08-22 12:40:53 +00:00
}
2021-05-23 16:28:50 +00:00
mirrorpath . erase ( ) ;
2020-08-22 12:40:53 +00:00
}
}
}
2021-05-23 16:28:50 +00:00
int FdEntity : : Dup ( int fd , bool lock_already_held )
2020-08-22 12:40:53 +00:00
{
AutoLock auto_lock ( & fdent_lock , lock_already_held ? AutoLock : : ALREADY_LOCKED : AutoLock : : NONE ) ;
2021-05-23 16:28:50 +00:00
S3FS_PRN_DBG ( " [path=%s][pseudo_fd=%d][physical_fd=%d][pseudo fd count=%zu] " , path . c_str ( ) , fd , physical_fd , pseudo_fd_map . size ( ) ) ;
2020-08-22 12:40:53 +00:00
2021-05-23 16:28:50 +00:00
if ( - 1 = = physical_fd ) {
return - 1 ;
}
fdinfo_map_t : : iterator iter = pseudo_fd_map . find ( fd ) ;
if ( pseudo_fd_map . end ( ) = = iter ) {
S3FS_PRN_ERR ( " Not found pseudo_fd(%d) in entity object(%s) for physical_fd(%d) " , fd , path . c_str ( ) , physical_fd ) ;
return - 1 ;
2020-08-22 12:40:53 +00:00
}
2021-05-23 16:28:50 +00:00
PseudoFdInfo * org_pseudoinfo = iter - > second ;
PseudoFdInfo * ppseudoinfo = new PseudoFdInfo ( physical_fd , ( org_pseudoinfo ? org_pseudoinfo - > GetFlags ( ) : 0 ) ) ;
int pseudo_fd = ppseudoinfo - > GetPseudoFd ( ) ;
pseudo_fd_map [ pseudo_fd ] = ppseudoinfo ;
return pseudo_fd ;
}
int FdEntity : : OpenPseudoFd ( int flags , bool lock_already_held )
{
AutoLock auto_lock ( & fdent_lock , lock_already_held ? AutoLock : : ALREADY_LOCKED : AutoLock : : NONE ) ;
S3FS_PRN_DBG ( " [path=%s][physical_fd=%d][pseudo fd count=%zu] " , path . c_str ( ) , physical_fd , pseudo_fd_map . size ( ) ) ;
if ( - 1 = = physical_fd ) {
return - 1 ;
}
PseudoFdInfo * ppseudoinfo = new PseudoFdInfo ( physical_fd , flags ) ;
int pseudo_fd = ppseudoinfo - > GetPseudoFd ( ) ;
pseudo_fd_map [ pseudo_fd ] = ppseudoinfo ;
return pseudo_fd ;
}
int FdEntity : : GetOpenCount ( bool lock_already_held )
{
AutoLock auto_lock ( & fdent_lock , lock_already_held ? AutoLock : : ALREADY_LOCKED : AutoLock : : NONE ) ;
return static_cast < int > ( pseudo_fd_map . size ( ) ) ;
2020-08-22 12:40:53 +00:00
}
//
// Open mirror file which is linked cache file.
//
int FdEntity : : OpenMirrorFile ( )
{
if ( cachepath . empty ( ) ) {
S3FS_PRN_ERR ( " cache path is empty, why come here " ) ;
return - EIO ;
}
// make temporary directory
2020-09-11 09:37:24 +00:00
std : : string bupdir ;
2020-08-22 12:40:53 +00:00
if ( ! FdManager : : MakeCachePath ( NULL , bupdir , true , true ) ) {
S3FS_PRN_ERR ( " could not make bup cache directory path or create it. " ) ;
return - EIO ;
}
// create seed generating mirror file name
unsigned int seed = static_cast < unsigned int > ( time ( NULL ) ) ;
int urandom_fd ;
if ( - 1 ! = ( urandom_fd = open ( " /dev/urandom " , O_RDONLY ) ) ) {
unsigned int rand_data ;
if ( sizeof ( rand_data ) = = read ( urandom_fd , & rand_data , sizeof ( rand_data ) ) ) {
seed ^ = rand_data ;
}
close ( urandom_fd ) ;
}
// try to link mirror file
while ( true ) {
// make random(temp) file path
// (do not care for threading, because allowed any value returned.)
//
char szfile [ NAME_MAX + 1 ] ;
sprintf ( szfile , " %x.tmp " , rand_r ( & seed ) ) ;
mirrorpath = bupdir + " / " + szfile ;
// link mirror file to cache file
if ( 0 = = link ( cachepath . c_str ( ) , mirrorpath . c_str ( ) ) ) {
break ;
}
if ( EEXIST ! = errno ) {
S3FS_PRN_ERR ( " could not link mirror file(%s) to cache file(%s) by errno(%d). " , mirrorpath . c_str ( ) , cachepath . c_str ( ) , errno ) ;
return - errno ;
}
+ + seed ;
}
// open mirror file
int mirrorfd ;
if ( - 1 = = ( mirrorfd = open ( mirrorpath . c_str ( ) , O_RDWR ) ) ) {
S3FS_PRN_ERR ( " could not open mirror file(%s) by errno(%d). " , mirrorpath . c_str ( ) , errno ) ;
return - errno ;
}
return mirrorfd ;
}
2021-05-23 16:28:50 +00:00
bool FdEntity : : FindPseudoFd ( int fd , bool lock_already_held )
{
AutoLock auto_lock ( & fdent_lock , lock_already_held ? AutoLock : : ALREADY_LOCKED : AutoLock : : NONE ) ;
if ( - 1 = = fd ) {
return false ;
}
if ( pseudo_fd_map . end ( ) = = pseudo_fd_map . find ( fd ) ) {
return false ;
}
return true ;
}
PseudoFdInfo * FdEntity : : CheckPseudoFdFlags ( int fd , bool writable , bool lock_already_held )
{
AutoLock auto_lock ( & fdent_lock , lock_already_held ? AutoLock : : ALREADY_LOCKED : AutoLock : : NONE ) ;
if ( - 1 = = fd ) {
return NULL ;
}
fdinfo_map_t : : iterator iter = pseudo_fd_map . find ( fd ) ;
if ( pseudo_fd_map . end ( ) = = iter | | NULL = = iter - > second ) {
return NULL ;
}
if ( writable ) {
if ( ! iter - > second - > Writable ( ) ) {
return NULL ;
}
} else {
if ( ! iter - > second - > Readable ( ) ) {
return NULL ;
}
}
return iter - > second ;
}
bool FdEntity : : IsUploading ( bool lock_already_held )
{
AutoLock auto_lock ( & fdent_lock , lock_already_held ? AutoLock : : ALREADY_LOCKED : AutoLock : : NONE ) ;
for ( fdinfo_map_t : : const_iterator iter = pseudo_fd_map . begin ( ) ; iter ! = pseudo_fd_map . end ( ) ; + + iter ) {
PseudoFdInfo * ppseudoinfo = iter - > second ;
if ( ppseudoinfo & & ppseudoinfo - > IsUploading ( ) ) {
return true ;
}
}
return false ;
}
// [NOTE]
// If the open is successful, returns pseudo fd.
// If it fails, it returns an error code with a negative value.
//
int FdEntity : : Open ( headers_t * pmeta , off_t size , time_t time , int flags , AutoLock : : Type type )
2020-08-22 12:40:53 +00:00
{
2021-05-28 15:11:55 +00:00
AutoLock auto_lock ( & fdent_lock , type ) ;
2020-08-22 12:40:53 +00:00
2021-05-23 16:28:50 +00:00
S3FS_PRN_DBG ( " [path=%s][physical_fd=%d][size=%lld][time=%lld][flags=0x%x] " , path . c_str ( ) , physical_fd , static_cast < long long > ( size ) , static_cast < long long > ( time ) , flags ) ;
2020-08-22 12:40:53 +00:00
if ( ! auto_lock . isLockAcquired ( ) ) {
// had to wait for fd lock, return
S3FS_PRN_ERR ( " Could not get lock. " ) ;
return - EIO ;
}
AutoLock auto_data_lock ( & fdent_data_lock ) ;
2021-05-23 16:28:50 +00:00
if ( - 1 ! = physical_fd ) {
//
// already open file
//
2020-08-22 12:40:53 +00:00
// check only file size(do not need to save cfs and time.
if ( 0 < = size & & pagelist . Size ( ) ! = size ) {
// truncate temporary file size
2021-05-23 16:28:50 +00:00
if ( - 1 = = ftruncate ( physical_fd , size ) ) {
S3FS_PRN_ERR ( " failed to truncate temporary file(%d) by errno(%d). " , physical_fd , errno ) ;
2021-04-29 22:09:00 +00:00
return - errno ;
2020-08-22 12:40:53 +00:00
}
// resize page list
if ( ! pagelist . Resize ( size , false , true ) ) { // Areas with increased size are modified
2021-05-23 16:28:50 +00:00
S3FS_PRN_ERR ( " failed to truncate temporary file information(%d). " , physical_fd ) ;
2020-08-22 12:40:53 +00:00
return - EIO ;
}
}
// set original headers and set size.
off_t new_size = ( 0 < = size ? size : size_orgmeta ) ;
if ( pmeta ) {
orgmeta = * pmeta ;
new_size = get_size ( orgmeta ) ;
}
if ( new_size < size_orgmeta ) {
size_orgmeta = new_size ;
}
2021-05-23 16:28:50 +00:00
} else {
//
// file is not opened yet
//
bool need_save_csf = false ; // need to save(reset) cache stat file
bool is_truncate = false ; // need to truncate
2020-08-22 12:40:53 +00:00
2021-05-23 16:28:50 +00:00
if ( ! cachepath . empty ( ) ) {
// using cache
struct stat st ;
if ( stat ( cachepath . c_str ( ) , & st ) = = 0 ) {
if ( st . st_mtime < time ) {
S3FS_PRN_DBG ( " cache file stale, removing: %s " , cachepath . c_str ( ) ) ;
if ( unlink ( cachepath . c_str ( ) ) ! = 0 ) {
return ( 0 = = errno ? - EIO : - errno ) ;
}
2020-08-22 12:40:53 +00:00
}
}
2021-05-23 16:28:50 +00:00
// open cache and cache stat file, load page info.
CacheFileStat cfstat ( path . c_str ( ) ) ;
// try to open cache file
if ( - 1 ! = ( physical_fd = open ( cachepath . c_str ( ) , O_RDWR ) ) & &
0 ! = ( inode = FdEntity : : GetInode ( physical_fd ) ) & &
pagelist . Serialize ( cfstat , false , inode ) )
{
// succeed to open cache file and to load stats data
memset ( & st , 0 , sizeof ( struct stat ) ) ;
if ( - 1 = = fstat ( physical_fd , & st ) ) {
S3FS_PRN_ERR ( " fstat is failed. errno(%d) " , errno ) ;
physical_fd = - 1 ;
inode = 0 ;
return ( 0 = = errno ? - EIO : - errno ) ;
}
// check size, st_size, loading stat file
if ( - 1 = = size ) {
if ( st . st_size ! = pagelist . Size ( ) ) {
pagelist . Resize ( st . st_size , false , true ) ; // Areas with increased size are modified
need_save_csf = true ; // need to update page info
}
size = st . st_size ;
} else {
if ( size ! = pagelist . Size ( ) ) {
pagelist . Resize ( size , false , true ) ; // Areas with increased size are modified
need_save_csf = true ; // need to update page info
}
if ( size ! = st . st_size ) {
is_truncate = true ;
}
2020-08-22 12:40:53 +00:00
}
2021-05-23 16:28:50 +00:00
2020-08-22 12:40:53 +00:00
} else {
2021-05-23 16:28:50 +00:00
if ( - 1 ! = physical_fd ) {
close ( physical_fd ) ;
}
inode = 0 ;
// could not open cache file or could not load stats data, so initialize it.
if ( - 1 = = ( physical_fd = open ( cachepath . c_str ( ) , O_CREAT | O_RDWR | O_TRUNC , 0600 ) ) ) {
S3FS_PRN_ERR ( " failed to open file(%s). errno(%d) " , cachepath . c_str ( ) , errno ) ;
// remove cache stat file if it is existed
if ( ! CacheFileStat : : DeleteCacheFileStat ( path . c_str ( ) ) ) {
if ( ENOENT ! = errno ) {
S3FS_PRN_WARN ( " failed to delete current cache stat file(%s) by errno(%d), but continue... " , path . c_str ( ) , errno ) ;
}
}
return ( 0 = = errno ? - EIO : - errno ) ;
2020-08-22 12:40:53 +00:00
}
2021-05-23 16:28:50 +00:00
need_save_csf = true ; // need to update page info
inode = FdEntity : : GetInode ( physical_fd ) ;
if ( - 1 = = size ) {
size = 0 ;
pagelist . Init ( 0 , false , false ) ;
} else {
// [NOTE]
// The modify flag must not be set when opening a file,
// if the time parameter(mtime) is specified(not -1) and
// the cache file does not exist.
// If mtime is specified for the file and the cache file
// mtime is older than it, the cache file is removed and
// the processing comes here.
//
pagelist . Resize ( size , false , ( 0 < = time ? false : true ) ) ;
2020-08-22 12:40:53 +00:00
is_truncate = true ;
}
}
2021-05-23 16:28:50 +00:00
// open mirror file
int mirrorfd ;
if ( 0 > = ( mirrorfd = OpenMirrorFile ( ) ) ) {
S3FS_PRN_ERR ( " failed to open mirror file linked cache file(%s). " , cachepath . c_str ( ) ) ;
return ( 0 = = mirrorfd ? - EIO : mirrorfd ) ;
}
// switch fd
close ( physical_fd ) ;
physical_fd = mirrorfd ;
// make file pointer(for being same tmpfile)
if ( NULL = = ( pfile = fdopen ( physical_fd , " wb " ) ) ) {
S3FS_PRN_ERR ( " failed to get fileno(%s). errno(%d) " , cachepath . c_str ( ) , errno ) ;
close ( physical_fd ) ;
physical_fd = - 1 ;
inode = 0 ;
return ( 0 = = errno ? - EIO : - errno ) ;
2020-08-22 12:40:53 +00:00
}
2021-05-23 16:28:50 +00:00
} else {
// not using cache
inode = 0 ;
2020-08-22 12:40:53 +00:00
2021-05-23 16:28:50 +00:00
// open temporary file
2021-06-13 18:45:56 +00:00
if ( NULL = = ( pfile = FdManager : : MakeTempFile ( ) ) | | - 1 = = ( physical_fd = fileno ( pfile ) ) ) {
S3FS_PRN_ERR ( " failed to open temporary file by errno(%d) " , errno ) ;
2021-05-23 16:28:50 +00:00
if ( pfile ) {
fclose ( pfile ) ;
pfile = NULL ;
2020-08-22 12:40:53 +00:00
}
return ( 0 = = errno ? - EIO : - errno ) ;
}
if ( - 1 = = size ) {
size = 0 ;
pagelist . Init ( 0 , false , false ) ;
} else {
// [NOTE]
// The modify flag must not be set when opening a file,
// if the time parameter(mtime) is specified(not -1) and
// the cache file does not exist.
// If mtime is specified for the file and the cache file
// mtime is older than it, the cache file is removed and
// the processing comes here.
//
pagelist . Resize ( size , false , ( 0 < = time ? false : true ) ) ;
is_truncate = true ;
}
}
2021-05-23 16:28:50 +00:00
// truncate cache(tmp) file
if ( is_truncate ) {
if ( 0 ! = ftruncate ( physical_fd , size ) | | 0 ! = fsync ( physical_fd ) ) {
S3FS_PRN_ERR ( " ftruncate(%s) or fsync returned err(%d) " , cachepath . c_str ( ) , errno ) ;
2020-08-22 12:40:53 +00:00
fclose ( pfile ) ;
2021-05-23 16:28:50 +00:00
pfile = NULL ;
physical_fd = - 1 ;
inode = 0 ;
return ( 0 = = errno ? - EIO : - errno ) ;
2020-08-22 12:40:53 +00:00
}
}
2021-05-23 16:28:50 +00:00
// reset cache stat file
if ( need_save_csf ) {
CacheFileStat cfstat ( path . c_str ( ) ) ;
if ( ! pagelist . Serialize ( cfstat , true , inode ) ) {
S3FS_PRN_WARN ( " failed to save cache stat file(%s), but continue... " , path . c_str ( ) ) ;
}
2020-08-22 12:40:53 +00:00
}
2021-05-23 16:28:50 +00:00
// set original headers and size in it.
if ( pmeta ) {
orgmeta = * pmeta ;
size_orgmeta = get_size ( orgmeta ) ;
} else {
orgmeta . clear ( ) ;
size_orgmeta = 0 ;
2020-08-22 12:40:53 +00:00
}
2021-05-23 16:28:50 +00:00
// set mtime and ctime(set "x-amz-meta-mtime" and "x-amz-meta-ctime" in orgmeta)
if ( - 1 ! = time ) {
struct timespec ts = { time , 0 } ;
if ( 0 ! = SetMCtime ( ts , ts , /*lock_already_held=*/ true ) ) {
S3FS_PRN_ERR ( " failed to set mtime. errno(%d) " , errno ) ;
fclose ( pfile ) ;
pfile = NULL ;
physical_fd = - 1 ;
inode = 0 ;
return ( 0 = = errno ? - EIO : - errno ) ;
}
2020-08-22 12:40:53 +00:00
}
}
2021-05-23 16:28:50 +00:00
// create new pseudo fd, and set it to map
PseudoFdInfo * ppseudoinfo = new PseudoFdInfo ( physical_fd , flags ) ;
int pseudo_fd = ppseudoinfo - > GetPseudoFd ( ) ;
pseudo_fd_map [ pseudo_fd ] = ppseudoinfo ;
2020-08-22 12:40:53 +00:00
2021-05-23 16:28:50 +00:00
return pseudo_fd ;
2020-08-22 12:40:53 +00:00
}
// [NOTE]
2021-05-23 16:28:50 +00:00
// This method is called for only nocopyapi functions.
2020-08-22 12:40:53 +00:00
// So we do not check disk space for this option mode, if there is no enough
// disk space this method will be failed.
//
2021-05-23 16:28:50 +00:00
bool FdEntity : : LoadAll ( int fd , headers_t * pmeta , off_t * size , bool force_load )
2020-08-22 12:40:53 +00:00
{
AutoLock auto_lock ( & fdent_lock ) ;
2021-05-23 16:28:50 +00:00
S3FS_PRN_INFO3 ( " [path=%s][pseudo_fd=%d][physical_fd=%d] " , path . c_str ( ) , fd , physical_fd ) ;
2020-08-22 12:40:53 +00:00
2021-05-23 16:28:50 +00:00
if ( - 1 = = physical_fd | | ! FindPseudoFd ( fd , true ) ) {
S3FS_PRN_ERR ( " pseudo_fd(%d) and physical_fd(%d) for path(%s) is not opened yet " , fd , physical_fd , path . c_str ( ) ) ;
return false ;
2020-08-22 12:40:53 +00:00
}
2021-05-23 16:28:50 +00:00
2020-08-22 12:40:53 +00:00
AutoLock auto_data_lock ( & fdent_data_lock ) ;
if ( force_load ) {
SetAllStatusUnloaded ( ) ;
}
//
// TODO: possibly do background for delay loading
//
2021-05-23 16:28:50 +00:00
int result ;
2020-08-22 12:40:53 +00:00
if ( 0 ! = ( result = Load ( /*start=*/ 0 , /*size=*/ 0 , /*lock_already_held=*/ true ) ) ) {
S3FS_PRN_ERR ( " could not download, result(%d) " , result ) ;
return false ;
}
if ( size ) {
* size = pagelist . Size ( ) ;
}
return true ;
}
//
// Rename file path.
//
// This method sets the FdManager::fent map registration key to fentmapkey.
//
// [NOTE]
// This method changes the file path of FdEntity.
// Old file is deleted after linking to the new file path, and this works
// without problem because the file descriptor is not affected even if the
// cache file is open.
// The mirror file descriptor is also the same. The mirror file path does
// not need to be changed and will remain as it is.
//
2020-09-11 09:37:24 +00:00
bool FdEntity : : RenamePath ( const std : : string & newpath , std : : string & fentmapkey )
2020-08-22 12:40:53 +00:00
{
if ( ! cachepath . empty ( ) ) {
// has cache path
// make new cache path
2020-09-11 09:37:24 +00:00
std : : string newcachepath ;
2020-08-22 12:40:53 +00:00
if ( ! FdManager : : MakeCachePath ( newpath . c_str ( ) , newcachepath , true ) ) {
S3FS_PRN_ERR ( " failed to make cache path for object(%s). " , newpath . c_str ( ) ) ;
return false ;
}
// rename cache file
if ( - 1 = = rename ( cachepath . c_str ( ) , newcachepath . c_str ( ) ) ) {
S3FS_PRN_ERR ( " failed to rename old cache path(%s) to new cache path(%s) by errno(%d). " , cachepath . c_str ( ) , newcachepath . c_str ( ) , errno ) ;
return false ;
}
// link and unlink cache file stat
if ( ! CacheFileStat : : RenameCacheFileStat ( path . c_str ( ) , newpath . c_str ( ) ) ) {
S3FS_PRN_ERR ( " failed to rename cache file stat(%s to %s). " , path . c_str ( ) , newpath . c_str ( ) ) ;
return false ;
}
fentmapkey = newpath ;
cachepath = newcachepath ;
} else {
// does not have cache path
fentmapkey . erase ( ) ;
FdManager : : MakeRandomTempPath ( newpath . c_str ( ) , fentmapkey ) ;
}
// set new path
path = newpath ;
return true ;
}
2020-09-20 22:02:06 +00:00
bool FdEntity : : IsModified ( ) const
2020-08-22 12:40:53 +00:00
{
AutoLock auto_data_lock ( const_cast < pthread_mutex_t * > ( & fdent_data_lock ) ) ;
return pagelist . IsModified ( ) ;
}
bool FdEntity : : GetStats ( struct stat & st , bool lock_already_held )
{
AutoLock auto_lock ( & fdent_lock , lock_already_held ? AutoLock : : ALREADY_LOCKED : AutoLock : : NONE ) ;
2021-05-23 16:28:50 +00:00
if ( - 1 = = physical_fd ) {
2020-08-22 12:40:53 +00:00
return false ;
}
memset ( & st , 0 , sizeof ( struct stat ) ) ;
2021-05-23 16:28:50 +00:00
if ( - 1 = = fstat ( physical_fd , & st ) ) {
2020-08-22 12:40:53 +00:00
S3FS_PRN_ERR ( " fstat failed. errno(%d) " , errno ) ;
return false ;
}
return true ;
}
2021-04-18 04:11:12 +00:00
int FdEntity : : SetCtime ( struct timespec time , bool lock_already_held )
2020-08-22 12:40:53 +00:00
{
AutoLock auto_lock ( & fdent_lock , lock_already_held ? AutoLock : : ALREADY_LOCKED : AutoLock : : NONE ) ;
2021-05-23 16:28:50 +00:00
S3FS_PRN_INFO3 ( " [path=%s][physical_fd=%d][time=%lld] " , path . c_str ( ) , physical_fd , static_cast < long long > ( time . tv_sec ) ) ;
2020-08-22 12:40:53 +00:00
2021-04-18 04:11:12 +00:00
if ( - 1 = = time . tv_sec ) {
2020-08-22 12:40:53 +00:00
return 0 ;
}
orgmeta [ " x-amz-meta-ctime " ] = str ( time ) ;
return 0 ;
}
2021-04-18 04:11:12 +00:00
int FdEntity : : SetAtime ( struct timespec time , bool lock_already_held )
2020-08-22 12:40:53 +00:00
{
AutoLock auto_lock ( & fdent_lock , lock_already_held ? AutoLock : : ALREADY_LOCKED : AutoLock : : NONE ) ;
2021-05-23 16:28:50 +00:00
S3FS_PRN_INFO3 ( " [path=%s][physical_fd=%d][time=%lld] " , path . c_str ( ) , physical_fd , static_cast < long long > ( time . tv_sec ) ) ;
2020-08-22 12:40:53 +00:00
2021-04-18 04:11:12 +00:00
if ( - 1 = = time . tv_sec ) {
2020-08-22 12:40:53 +00:00
return 0 ;
}
2020-10-03 02:14:23 +00:00
orgmeta [ " x-amz-meta-atime " ] = str ( time ) ;
return 0 ;
}
// [NOTE]
// This method updates mtime as well as ctime.
//
2021-04-18 04:11:12 +00:00
int FdEntity : : SetMCtime ( struct timespec mtime , struct timespec ctime , bool lock_already_held )
2020-10-03 02:14:23 +00:00
{
AutoLock auto_lock ( & fdent_lock , lock_already_held ? AutoLock : : ALREADY_LOCKED : AutoLock : : NONE ) ;
2021-05-23 16:28:50 +00:00
S3FS_PRN_INFO3 ( " [path=%s][physical_fd=%d][mtime=%lld][ctime=%lld] " , path . c_str ( ) , physical_fd , static_cast < long long > ( mtime . tv_sec ) , static_cast < long long > ( ctime . tv_sec ) ) ;
2020-10-03 02:14:23 +00:00
2021-04-18 04:11:12 +00:00
if ( mtime . tv_sec < 0 | | ctime . tv_sec < 0 ) {
2020-10-03 02:14:23 +00:00
return 0 ;
}
2020-08-22 12:40:53 +00:00
2021-05-23 16:28:50 +00:00
if ( - 1 ! = physical_fd ) {
2020-08-22 12:40:53 +00:00
struct timeval tv [ 2 ] ;
2021-04-18 04:11:12 +00:00
tv [ 0 ] . tv_sec = mtime . tv_sec ;
tv [ 0 ] . tv_usec = mtime . tv_nsec / 1000 ;
tv [ 1 ] . tv_sec = ctime . tv_sec ;
tv [ 1 ] . tv_usec = ctime . tv_nsec / 1000 ;
2021-05-23 16:28:50 +00:00
if ( - 1 = = futimes ( physical_fd , tv ) ) {
2020-08-22 12:40:53 +00:00
S3FS_PRN_ERR ( " futimes failed. errno(%d) " , errno ) ;
return - errno ;
}
} else if ( ! cachepath . empty ( ) ) {
// not opened file yet.
2020-10-03 02:14:23 +00:00
struct utimbuf n_time ;
2021-04-18 04:11:12 +00:00
n_time . modtime = mtime . tv_sec ;
n_time . actime = ctime . tv_sec ;
2020-10-03 02:14:23 +00:00
if ( - 1 = = utime ( cachepath . c_str ( ) , & n_time ) ) {
2020-08-22 12:40:53 +00:00
S3FS_PRN_ERR ( " utime failed. errno(%d) " , errno ) ;
return - errno ;
}
}
2020-10-03 02:14:23 +00:00
orgmeta [ " x-amz-meta-mtime " ] = str ( mtime ) ;
orgmeta [ " x-amz-meta-ctime " ] = str ( ctime ) ;
2020-08-22 12:40:53 +00:00
return 0 ;
}
bool FdEntity : : UpdateCtime ( )
{
AutoLock auto_lock ( & fdent_lock ) ;
struct stat st ;
if ( ! GetStats ( st , /*lock_already_held=*/ true ) ) {
return false ;
}
orgmeta [ " x-amz-meta-ctime " ] = str ( st . st_ctime ) ;
return true ;
}
2020-10-03 02:14:23 +00:00
bool FdEntity : : UpdateAtime ( )
2020-08-22 12:40:53 +00:00
{
AutoLock auto_lock ( & fdent_lock ) ;
struct stat st ;
if ( ! GetStats ( st , /*lock_already_held=*/ true ) ) {
return false ;
}
2020-10-03 02:14:23 +00:00
orgmeta [ " x-amz-meta-atime " ] = str ( st . st_atime ) ;
return true ;
}
bool FdEntity : : UpdateMtime ( bool clear_holding_mtime )
{
AutoLock auto_lock ( & fdent_lock ) ;
2021-04-18 04:11:12 +00:00
if ( 0 < = holding_mtime . tv_sec ) {
2020-10-03 02:14:23 +00:00
// [NOTE]
// This conditional statement is very special.
// If you copy a file with "cp -p" etc., utimens or chown will be
// called after opening the file, after that call to write, flush.
// If normally utimens are not called(cases like "cp" only), mtime
// should be updated at the file flush.
// Here, check the holding_mtime value to prevent mtime from being
// overwritten.
//
if ( clear_holding_mtime ) {
if ( ! ClearHoldingMtime ( true ) ) {
return false ;
}
}
} else {
struct stat st ;
if ( ! GetStats ( st , /*lock_already_held=*/ true ) ) {
return false ;
}
orgmeta [ " x-amz-meta-mtime " ] = str ( st . st_mtime ) ;
}
return true ;
}
2021-04-18 04:11:12 +00:00
bool FdEntity : : SetHoldingMtime ( struct timespec mtime , bool lock_already_held )
2020-10-03 02:14:23 +00:00
{
AutoLock auto_lock ( & fdent_lock , lock_already_held ? AutoLock : : ALREADY_LOCKED : AutoLock : : NONE ) ;
2021-04-18 04:11:12 +00:00
if ( mtime . tv_sec < 0 ) {
2020-10-03 02:14:23 +00:00
return false ;
}
holding_mtime = mtime ;
return true ;
}
bool FdEntity : : ClearHoldingMtime ( bool lock_already_held )
{
AutoLock auto_lock ( & fdent_lock , lock_already_held ? AutoLock : : ALREADY_LOCKED : AutoLock : : NONE ) ;
2021-04-18 04:11:12 +00:00
if ( holding_mtime . tv_sec < 0 ) {
2020-10-03 02:14:23 +00:00
return false ;
}
struct stat st ;
if ( ! GetStats ( st , true ) ) {
return false ;
}
2021-05-23 16:28:50 +00:00
if ( - 1 ! = physical_fd ) {
2020-10-03 02:14:23 +00:00
struct timeval tv [ 2 ] ;
2021-04-18 04:11:12 +00:00
tv [ 0 ] . tv_sec = holding_mtime . tv_sec ;
tv [ 0 ] . tv_usec = holding_mtime . tv_nsec / 1000 ;
2020-10-03 02:14:23 +00:00
tv [ 1 ] . tv_sec = st . st_ctime ;
2021-04-18 04:11:12 +00:00
tv [ 1 ] . tv_usec = 0 ;
2021-05-23 16:28:50 +00:00
if ( - 1 = = futimes ( physical_fd , tv ) ) {
2020-10-03 02:14:23 +00:00
S3FS_PRN_ERR ( " futimes failed. errno(%d) " , errno ) ;
return false ;
}
} else if ( ! cachepath . empty ( ) ) {
// not opened file yet.
struct utimbuf n_time ;
2021-04-18 04:11:12 +00:00
n_time . modtime = holding_mtime . tv_sec ;
2020-10-03 02:14:23 +00:00
n_time . actime = st . st_ctime ;
if ( - 1 = = utime ( cachepath . c_str ( ) , & n_time ) ) {
S3FS_PRN_ERR ( " utime failed. errno(%d) " , errno ) ;
return false ;
}
}
2021-04-18 04:11:12 +00:00
holding_mtime . tv_sec = - 1 ;
holding_mtime . tv_nsec = 0 ;
2020-10-03 02:14:23 +00:00
2020-08-22 12:40:53 +00:00
return true ;
}
bool FdEntity : : GetSize ( off_t & size )
{
AutoLock auto_lock ( & fdent_lock ) ;
2021-05-23 16:28:50 +00:00
if ( - 1 = = physical_fd ) {
2020-08-22 12:40:53 +00:00
return false ;
}
AutoLock auto_data_lock ( & fdent_data_lock ) ;
size = pagelist . Size ( ) ;
return true ;
}
2020-09-11 09:37:24 +00:00
bool FdEntity : : GetXattr ( std : : string & xattr )
2020-08-22 12:40:53 +00:00
{
AutoLock auto_lock ( & fdent_lock ) ;
headers_t : : const_iterator iter = orgmeta . find ( " x-amz-meta-xattr " ) ;
if ( iter = = orgmeta . end ( ) ) {
return false ;
}
xattr = iter - > second ;
return true ;
}
bool FdEntity : : SetXattr ( const std : : string & xattr )
{
AutoLock auto_lock ( & fdent_lock ) ;
orgmeta [ " x-amz-meta-xattr " ] = xattr ;
return true ;
}
bool FdEntity : : SetMode ( mode_t mode )
{
AutoLock auto_lock ( & fdent_lock ) ;
orgmeta [ " x-amz-meta-mode " ] = str ( mode ) ;
return true ;
}
bool FdEntity : : SetUId ( uid_t uid )
{
AutoLock auto_lock ( & fdent_lock ) ;
orgmeta [ " x-amz-meta-uid " ] = str ( uid ) ;
return true ;
}
bool FdEntity : : SetGId ( gid_t gid )
{
AutoLock auto_lock ( & fdent_lock ) ;
orgmeta [ " x-amz-meta-gid " ] = str ( gid ) ;
return true ;
}
bool FdEntity : : SetContentType ( const char * path )
{
if ( ! path ) {
return false ;
}
AutoLock auto_lock ( & fdent_lock ) ;
2020-09-11 09:37:24 +00:00
orgmeta [ " Content-Type " ] = S3fsCurl : : LookupMimeType ( std : : string ( path ) ) ;
2020-08-22 12:40:53 +00:00
return true ;
}
bool FdEntity : : SetAllStatus ( bool is_loaded )
{
2021-05-23 16:28:50 +00:00
S3FS_PRN_INFO3 ( " [path=%s][physical_fd=%d][%s] " , path . c_str ( ) , physical_fd , is_loaded ? " loaded " : " unloaded " ) ;
2020-08-22 12:40:53 +00:00
2021-05-23 16:28:50 +00:00
if ( - 1 = = physical_fd ) {
2020-08-22 12:40:53 +00:00
return false ;
}
// [NOTE]
// this method is only internal use, and calling after locking.
// so do not lock now.
//
//AutoLock auto_lock(&fdent_lock);
// get file size
struct stat st ;
memset ( & st , 0 , sizeof ( struct stat ) ) ;
2021-05-23 16:28:50 +00:00
if ( - 1 = = fstat ( physical_fd , & st ) ) {
2020-08-22 12:40:53 +00:00
S3FS_PRN_ERR ( " fstat is failed. errno(%d) " , errno ) ;
return false ;
}
// Reinit
pagelist . Init ( st . st_size , is_loaded , false ) ;
return true ;
}
int FdEntity : : Load ( off_t start , off_t size , bool lock_already_held , bool is_modified_flag )
{
AutoLock auto_lock ( & fdent_lock , lock_already_held ? AutoLock : : ALREADY_LOCKED : AutoLock : : NONE ) ;
2021-05-23 16:28:50 +00:00
S3FS_PRN_DBG ( " [path=%s][physical_fd=%d][offset=%lld][size=%lld] " , path . c_str ( ) , physical_fd , static_cast < long long int > ( start ) , static_cast < long long int > ( size ) ) ;
2020-08-22 12:40:53 +00:00
2021-05-23 16:28:50 +00:00
if ( - 1 = = physical_fd ) {
2020-08-22 12:40:53 +00:00
return - EBADF ;
}
AutoLock auto_data_lock ( & fdent_data_lock , lock_already_held ? AutoLock : : ALREADY_LOCKED : AutoLock : : NONE ) ;
int result = 0 ;
// check loaded area & load
fdpage_list_t unloaded_list ;
if ( 0 < pagelist . GetUnloadedPages ( unloaded_list , start , size ) ) {
for ( fdpage_list_t : : iterator iter = unloaded_list . begin ( ) ; iter ! = unloaded_list . end ( ) ; + + iter ) {
if ( 0 ! = size & & start + size < = iter - > offset ) {
// reached end
break ;
}
// check loading size
off_t need_load_size = 0 ;
if ( iter - > offset < size_orgmeta ) {
// original file size(on S3) is smaller than request.
need_load_size = ( iter - > next ( ) < = size_orgmeta ? iter - > bytes : ( size_orgmeta - iter - > offset ) ) ;
}
// download
if ( S3fsCurl : : GetMultipartSize ( ) < = need_load_size & & ! nomultipart ) {
// parallel request
2021-05-23 16:28:50 +00:00
result = S3fsCurl : : ParallelGetObjectRequest ( path . c_str ( ) , physical_fd , iter - > offset , need_load_size ) ;
2020-08-22 12:40:53 +00:00
} else {
// single request
if ( 0 < need_load_size ) {
S3fsCurl s3fscurl ;
2021-05-23 16:28:50 +00:00
result = s3fscurl . GetObjectRequest ( path . c_str ( ) , physical_fd , iter - > offset , need_load_size ) ;
2020-08-22 12:40:53 +00:00
} else {
result = 0 ;
}
}
if ( 0 ! = result ) {
break ;
}
// Set loaded flag
pagelist . SetPageLoadedStatus ( iter - > offset , iter - > bytes , ( is_modified_flag ? PageList : : PAGE_LOAD_MODIFIED : PageList : : PAGE_LOADED ) ) ;
}
PageList : : FreeList ( unloaded_list ) ;
}
return result ;
}
// [NOTE]
// At no disk space for caching object.
// This method is downloading by dividing an object of the specified range
// and uploading by multipart after finishing downloading it.
//
// [NOTICE]
// Need to lock before calling this method.
//
2021-05-23 16:28:50 +00:00
int FdEntity : : NoCacheLoadAndPost ( PseudoFdInfo * pseudo_obj , off_t start , off_t size )
2020-08-22 12:40:53 +00:00
{
int result = 0 ;
2021-05-23 16:28:50 +00:00
S3FS_PRN_INFO3 ( " [path=%s][physical_fd=%d][offset=%lld][size=%lld] " , path . c_str ( ) , physical_fd , static_cast < long long int > ( start ) , static_cast < long long int > ( size ) ) ;
2020-08-22 12:40:53 +00:00
2021-05-23 16:28:50 +00:00
if ( ! pseudo_obj ) {
S3FS_PRN_ERR ( " Pseudo obejct is NULL. " ) ;
return - EIO ;
}
if ( - 1 = = physical_fd ) {
2020-08-22 12:40:53 +00:00
return - EBADF ;
}
// [NOTE]
// This method calling means that the cache file is never used no more.
//
if ( ! cachepath . empty ( ) ) {
// remove cache files(and cache stat file)
FdManager : : DeleteCacheFile ( path . c_str ( ) ) ;
// cache file path does not use no more.
cachepath . erase ( ) ;
mirrorpath . erase ( ) ;
}
// Change entity key in manager mapping
FdManager : : get ( ) - > ChangeEntityToTempPath ( this , path . c_str ( ) ) ;
// open temporary file
FILE * ptmpfp ;
int tmpfd ;
2021-06-13 18:45:56 +00:00
if ( NULL = = ( ptmpfp = FdManager : : MakeTempFile ( ) ) | | - 1 = = ( tmpfd = fileno ( ptmpfp ) ) ) {
S3FS_PRN_ERR ( " failed to open temporary file by errno(%d) " , errno ) ;
2020-08-22 12:40:53 +00:00
if ( ptmpfp ) {
fclose ( ptmpfp ) ;
}
return ( 0 = = errno ? - EIO : - errno ) ;
}
// loop uploading by multipart
for ( fdpage_list_t : : iterator iter = pagelist . pages . begin ( ) ; iter ! = pagelist . pages . end ( ) ; + + iter ) {
if ( iter - > end ( ) < start ) {
continue ;
}
if ( 0 ! = size & & start + size < = iter - > offset ) {
break ;
}
// download each multipart size(default 10MB) in unit
for ( off_t oneread = 0 , totalread = ( iter - > offset < start ? start : 0 ) ; totalread < static_cast < off_t > ( iter - > bytes ) ; totalread + = oneread ) {
2021-05-23 16:28:50 +00:00
int upload_fd = physical_fd ;
2020-08-22 12:40:53 +00:00
off_t offset = iter - > offset + totalread ;
2020-09-11 09:37:24 +00:00
oneread = std : : min ( static_cast < off_t > ( iter - > bytes ) - totalread , S3fsCurl : : GetMultipartSize ( ) ) ;
2020-08-22 12:40:53 +00:00
// check rest size is over minimum part size
//
// [NOTE]
// If the final part size is smaller than 5MB, it is not allowed by S3 API.
// For this case, if the previous part of the final part is not over 5GB,
// we incorporate the final part to the previous part. If the previous part
// is over 5GB, we want to even out the last part and the previous part.
//
if ( ( iter - > bytes - totalread - oneread ) < MIN_MULTIPART_SIZE ) {
if ( FIVE_GB < iter - > bytes - totalread ) {
oneread = ( iter - > bytes - totalread ) / 2 ;
} else {
oneread = iter - > bytes - totalread ;
}
}
if ( ! iter - > loaded ) {
//
// loading or initializing
//
upload_fd = tmpfd ;
// load offset & size
size_t need_load_size = 0 ;
if ( size_orgmeta < = offset ) {
// all area is over of original size
need_load_size = 0 ;
} else {
if ( size_orgmeta < ( offset + oneread ) ) {
// original file size(on S3) is smaller than request.
need_load_size = size_orgmeta - offset ;
} else {
need_load_size = oneread ;
}
}
size_t over_size = oneread - need_load_size ;
// [NOTE]
// truncate file to zero and set length to part offset + size
// after this, file length is (offset + size), but file does not use any disk space.
//
if ( - 1 = = ftruncate ( tmpfd , 0 ) | | - 1 = = ftruncate ( tmpfd , ( offset + oneread ) ) ) {
S3FS_PRN_ERR ( " failed to truncate temporary file(%d). " , tmpfd ) ;
result = - EIO ;
break ;
}
// single area get request
if ( 0 < need_load_size ) {
S3fsCurl s3fscurl ;
if ( 0 ! = ( result = s3fscurl . GetObjectRequest ( path . c_str ( ) , tmpfd , offset , oneread ) ) ) {
S3FS_PRN_ERR ( " failed to get object(start=%lld, size=%lld) for file(%d). " , static_cast < long long int > ( offset ) , static_cast < long long int > ( oneread ) , tmpfd ) ;
break ;
}
}
// initialize fd without loading
if ( 0 < over_size ) {
if ( 0 ! = ( result = FdEntity : : FillFile ( tmpfd , 0 , over_size , offset + need_load_size ) ) ) {
S3FS_PRN_ERR ( " failed to fill rest bytes for fd(%d). errno(%d) " , tmpfd , result ) ;
break ;
}
}
} else {
// already loaded area
}
// single area upload by multipart post
2021-05-23 16:28:50 +00:00
if ( 0 ! = ( result = NoCacheMultipartPost ( pseudo_obj , upload_fd , offset , oneread ) ) ) {
S3FS_PRN_ERR ( " failed to multipart post(start=%lld, size=%lld) for file(%d). " , static_cast < long long int > ( offset ) , static_cast < long long int > ( oneread ) , upload_fd ) ;
break ;
2020-08-22 12:40:53 +00:00
}
}
if ( 0 ! = result ) {
break ;
}
// set loaded flag
if ( ! iter - > loaded ) {
if ( iter - > offset < start ) {
fdpage page ( iter - > offset , start - iter - > offset , iter - > loaded , false ) ;
iter - > bytes - = ( start - iter - > offset ) ;
iter - > offset = start ;
pagelist . pages . insert ( iter , page ) ;
}
if ( 0 ! = size & & start + size < iter - > next ( ) ) {
fdpage page ( iter - > offset , start + size - iter - > offset , true , false ) ;
iter - > bytes - = ( start + size - iter - > offset ) ;
iter - > offset = start + size ;
pagelist . pages . insert ( iter , page ) ;
} else {
iter - > loaded = true ;
iter - > modified = false ;
}
}
}
if ( 0 = = result ) {
// compress pagelist
pagelist . Compress ( ) ;
// fd data do empty
2021-05-23 16:28:50 +00:00
if ( - 1 = = ftruncate ( physical_fd , 0 ) ) {
S3FS_PRN_ERR ( " failed to truncate file(%d), but continue... " , physical_fd ) ;
2020-08-22 12:40:53 +00:00
}
}
// close temporary
fclose ( ptmpfp ) ;
return result ;
}
// [NOTE]
// At no disk space for caching object.
// This method is starting multipart uploading.
//
2021-05-23 16:28:50 +00:00
int FdEntity : : NoCachePreMultipartPost ( PseudoFdInfo * pseudo_obj )
2020-08-22 12:40:53 +00:00
{
2021-05-23 16:28:50 +00:00
if ( ! pseudo_obj ) {
S3FS_PRN_ERR ( " Internal error, pseudo fd object pointer is null. " ) ;
return - EIO ;
}
2020-08-22 12:40:53 +00:00
// initialize multipart upload values
2021-05-23 16:28:50 +00:00
pseudo_obj - > ClearUploadInfo ( true ) ;
2020-08-22 12:40:53 +00:00
2021-05-23 16:28:50 +00:00
S3fsCurl s3fscurl ( true ) ;
std : : string upload_id ;
int result ;
2020-08-22 12:40:53 +00:00
if ( 0 ! = ( result = s3fscurl . PreMultipartPostRequest ( path . c_str ( ) , orgmeta , upload_id , false ) ) ) {
return result ;
}
s3fscurl . DestroyCurlHandle ( ) ;
2021-05-23 16:28:50 +00:00
// reset upload_id
if ( ! pseudo_obj - > InitialUploadInfo ( upload_id ) ) {
return - EIO ;
}
2020-08-22 12:40:53 +00:00
return 0 ;
}
// [NOTE]
// At no disk space for caching object.
// This method is uploading one part of multipart.
//
2021-05-23 16:28:50 +00:00
int FdEntity : : NoCacheMultipartPost ( PseudoFdInfo * pseudo_obj , int tgfd , off_t start , off_t size )
2020-08-22 12:40:53 +00:00
{
2021-05-23 16:28:50 +00:00
if ( - 1 = = tgfd | | ! pseudo_obj | | ! pseudo_obj - > IsUploading ( ) ) {
2020-08-22 12:40:53 +00:00
S3FS_PRN_ERR ( " Need to initialize for multipart post. " ) ;
return - EIO ;
}
2021-05-23 16:28:50 +00:00
// get upload id
std : : string upload_id ( " " ) ;
if ( ! pseudo_obj - > GetUploadId ( upload_id ) ) {
return - EIO ;
}
// append new part and get it's etag string pointer
int partnum = 0 ;
std : : string * petag = NULL ;
if ( ! pseudo_obj - > AppendUploadPart ( start , size , false , & partnum , & petag ) ) {
return - EIO ;
}
2020-08-22 12:40:53 +00:00
S3fsCurl s3fscurl ( true ) ;
2021-05-23 16:28:50 +00:00
return s3fscurl . MultipartUploadRequest ( upload_id , path . c_str ( ) , tgfd , start , size , partnum , petag ) ;
2020-08-22 12:40:53 +00:00
}
// [NOTE]
// At no disk space for caching object.
// This method is finishing multipart uploading.
//
2021-05-23 16:28:50 +00:00
int FdEntity : : NoCacheCompleteMultipartPost ( PseudoFdInfo * pseudo_obj )
2020-08-22 12:40:53 +00:00
{
2021-05-23 16:28:50 +00:00
etaglist_t etaglist ;
if ( ! pseudo_obj | | ! pseudo_obj - > IsUploading ( ) | | ! pseudo_obj - > GetEtaglist ( etaglist ) ) {
2020-08-22 12:40:53 +00:00
S3FS_PRN_ERR ( " There is no upload id or etag list. " ) ;
return - EIO ;
}
2021-05-23 16:28:50 +00:00
// get upload id
std : : string upload_id ( " " ) ;
if ( ! pseudo_obj - > GetUploadId ( upload_id ) ) {
return - EIO ;
}
2020-08-22 12:40:53 +00:00
S3fsCurl s3fscurl ( true ) ;
int result ;
if ( 0 ! = ( result = s3fscurl . CompleteMultipartPostRequest ( path . c_str ( ) , upload_id , etaglist ) ) ) {
return result ;
}
s3fscurl . DestroyCurlHandle ( ) ;
2021-05-23 16:28:50 +00:00
// clear multipart upload info
pseudo_obj - > ClearUploadInfo ( false ) ;
2020-08-22 12:40:53 +00:00
return 0 ;
}
2021-05-23 16:28:50 +00:00
off_t FdEntity : : BytesModified ( )
{
2021-05-28 15:11:55 +00:00
AutoLock auto_lock ( & fdent_data_lock ) ;
2020-10-13 13:30:42 +00:00
return pagelist . BytesModified ( ) ;
}
2021-05-23 16:28:50 +00:00
int FdEntity : : RowFlush ( int fd , const char * tpath , bool force_sync )
2020-08-22 12:40:53 +00:00
{
2021-05-23 16:28:50 +00:00
S3FS_PRN_INFO3 ( " [tpath=%s][path=%s][pseudo_fd=%d][physical_fd=%d] " , SAFESTRPTR ( tpath ) , path . c_str ( ) , fd , physical_fd ) ;
2020-08-22 12:40:53 +00:00
2021-05-23 16:28:50 +00:00
if ( - 1 = = physical_fd ) {
return - EBADF ;
}
int result = 0 ;
std : : string tmppath = path ;
2020-08-22 12:40:53 +00:00
2021-05-23 16:28:50 +00:00
AutoLock auto_lock ( & fdent_lock ) ;
2021-06-13 07:14:24 +00:00
headers_t tmporgmeta = orgmeta ;
2020-08-22 12:40:53 +00:00
2021-05-23 16:28:50 +00:00
// check pseudo fd and its flag
fdinfo_map_t : : iterator miter = pseudo_fd_map . find ( fd ) ;
if ( pseudo_fd_map . end ( ) = = miter | | NULL = = miter - > second ) {
2020-08-22 12:40:53 +00:00
return - EBADF ;
}
2021-05-23 16:28:50 +00:00
if ( ! miter - > second - > Writable ( ) ) {
// If the entity is opened read-only, it will end normally without updating.
return 0 ;
}
PseudoFdInfo * pseudo_obj = miter - > second ;
2021-05-28 15:11:55 +00:00
AutoLock auto_lock2 ( & fdent_data_lock ) ;
2020-08-22 12:40:53 +00:00
if ( ! force_sync & & ! pagelist . IsModified ( ) ) {
// nothing to update.
return 0 ;
}
// If there is no loading all of the area, loading all area.
off_t restsize = pagelist . GetTotalUnloadedPageSize ( ) ;
if ( 0 < restsize ) {
2021-05-23 16:28:50 +00:00
if ( ! pseudo_obj - > IsUploading ( ) ) {
2020-08-22 12:40:53 +00:00
// check disk space
if ( ReserveDiskSpace ( restsize ) ) {
// enough disk space
// Load all uninitialized area(no mix multipart uploading)
if ( ! FdEntity : : mixmultipart ) {
result = Load ( /*start=*/ 0 , /*size=*/ 0 , /*lock_already_held=*/ true ) ;
}
FdManager : : FreeReservedDiskSpace ( restsize ) ;
if ( 0 ! = result ) {
S3FS_PRN_ERR ( " failed to upload all area(errno=%d) " , result ) ;
2021-05-21 15:27:49 +00:00
return result ;
2020-08-22 12:40:53 +00:00
}
} else {
2021-05-21 15:27:49 +00:00
// no enough disk space
if ( nomultipart ) {
S3FS_PRN_WARN ( " Not enough local storage to flush: [path=%s][fd=%d] " , path . c_str ( ) , fd ) ;
return - ENOSPC ; // No space left on device
}
2021-05-23 16:28:50 +00:00
if ( 0 ! = ( result = NoCachePreMultipartPost ( pseudo_obj ) ) ) {
2021-05-21 15:27:49 +00:00
S3FS_PRN_ERR ( " failed to switch multipart uploading with no cache(errno=%d) " , result ) ;
return result ;
}
// upload all by multipart uploading
2021-05-23 16:28:50 +00:00
if ( 0 ! = ( result = NoCacheLoadAndPost ( pseudo_obj ) ) ) {
2021-05-21 15:27:49 +00:00
S3FS_PRN_ERR ( " failed to upload all area by multipart uploading(errno=%d) " , result ) ;
return result ;
}
2021-05-23 16:28:50 +00:00
}
} else {
// already start multipart uploading
}
2020-08-22 12:40:53 +00:00
}
2021-05-23 16:28:50 +00:00
if ( ! pseudo_obj - > IsUploading ( ) ) {
2020-08-22 12:40:53 +00:00
// normal uploading
//
// Make decision to do multi upload (or not) based upon file size
//
// According to the AWS spec:
// - 1 to 10,000 parts are allowed
// - minimum size of parts is 5MB (expect for the last part)
//
// For our application, we will define minimum part size to be 10MB (10 * 2^20 Bytes)
// minimum file size will be 64 GB - 2 ** 36
//
// Initially uploads will be done serially
//
// If file is > 20MB, then multipart will kick in
//
if ( pagelist . Size ( ) > MAX_MULTIPART_CNT * S3fsCurl : : GetMultipartSize ( ) ) {
// close f ?
S3FS_PRN_ERR ( " Part count exceeds %d. Increase multipart size and try again. " , MAX_MULTIPART_CNT ) ;
2021-03-28 03:33:01 +00:00
return - EFBIG ;
2020-08-22 12:40:53 +00:00
}
// seek to head of file.
2021-05-23 16:28:50 +00:00
if ( 0 ! = lseek ( physical_fd , 0 , SEEK_SET ) ) {
2020-08-22 12:40:53 +00:00
S3FS_PRN_ERR ( " lseek error(%d) " , errno ) ;
return - errno ;
}
// backup upload file size
struct stat st ;
memset ( & st , 0 , sizeof ( struct stat ) ) ;
2021-05-23 16:28:50 +00:00
if ( - 1 = = fstat ( physical_fd , & st ) ) {
2020-08-22 12:40:53 +00:00
S3FS_PRN_ERR ( " fstat is failed by errno(%d), but continue... " , errno ) ;
}
if ( pagelist . Size ( ) > = S3fsCurl : : GetMultipartSize ( ) & & ! nomultipart ) {
if ( FdEntity : : mixmultipart ) {
// multipart uploading can use copy api
// This is to ensure that each part is 5MB or more.
// If the part is less than 5MB, download it.
fdpage_list_t dlpages ;
fdpage_list_t mixuppages ;
if ( ! pagelist . GetPageListsForMultipartUpload ( dlpages , mixuppages , S3fsCurl : : GetMultipartSize ( ) ) ) {
S3FS_PRN_ERR ( " something error occurred during getting download pagelist. " ) ;
return - 1 ;
}
// [TODO] should use parallel downloading
//
for ( fdpage_list_t : : const_iterator iter = dlpages . begin ( ) ; iter ! = dlpages . end ( ) ; + + iter ) {
if ( 0 ! = ( result = Load ( iter - > offset , iter - > bytes , /*lock_already_held=*/ true , /*is_modified_flag=*/ true ) ) ) { // set loaded and modified flag
S3FS_PRN_ERR ( " failed to get parts(start=%lld, size=%lld) before uploading. " , static_cast < long long int > ( iter - > offset ) , static_cast < long long int > ( iter - > bytes ) ) ;
return result ;
}
}
// multipart uploading with copy api
2021-05-23 16:28:50 +00:00
result = S3fsCurl : : ParallelMixMultipartUploadRequest ( tpath ? tpath : tmppath . c_str ( ) , tmporgmeta , physical_fd , mixuppages ) ;
2020-08-22 12:40:53 +00:00
} else {
// multipart uploading not using copy api
2021-05-23 16:28:50 +00:00
result = S3fsCurl : : ParallelMultipartUploadRequest ( tpath ? tpath : tmppath . c_str ( ) , tmporgmeta , physical_fd ) ;
2020-08-22 12:40:53 +00:00
}
} else {
// If there are unloaded pages, they are loaded at here.
if ( 0 ! = ( result = Load ( /*start=*/ 0 , /*size=*/ 0 , /*lock_already_held=*/ true ) ) ) {
S3FS_PRN_ERR ( " failed to load parts before uploading object(%d) " , result ) ;
return result ;
}
S3fsCurl s3fscurl ( true ) ;
2021-05-23 16:28:50 +00:00
result = s3fscurl . PutRequest ( tpath ? tpath : tmppath . c_str ( ) , tmporgmeta , physical_fd ) ;
2020-08-22 12:40:53 +00:00
}
// seek to head of file.
2021-05-23 16:28:50 +00:00
if ( 0 = = result & & 0 ! = lseek ( physical_fd , 0 , SEEK_SET ) ) {
2020-08-22 12:40:53 +00:00
S3FS_PRN_ERR ( " lseek error(%d) " , errno ) ;
return - errno ;
}
// reset uploaded file size
size_orgmeta = st . st_size ;
} else {
// upload rest data
2021-05-23 16:28:50 +00:00
off_t untreated_start = 0 ;
off_t untreated_size = 0 ;
pseudo_obj - > GetUntreated ( untreated_start , untreated_size ) ;
if ( 0 < untreated_size ) {
if ( 0 ! = ( result = NoCacheMultipartPost ( pseudo_obj , physical_fd , untreated_start , untreated_size ) ) ) {
S3FS_PRN_ERR ( " failed to multipart post(start=%lld, size=%lld) for file(%d). " , static_cast < long long int > ( untreated_start ) , static_cast < long long int > ( untreated_size ) , physical_fd ) ;
2020-08-22 12:40:53 +00:00
return result ;
}
2021-05-23 16:28:50 +00:00
pseudo_obj - > ClearUntreated ( ) ;
2020-08-22 12:40:53 +00:00
}
// complete multipart uploading.
2021-05-23 16:28:50 +00:00
if ( 0 ! = ( result = NoCacheCompleteMultipartPost ( pseudo_obj ) ) ) {
S3FS_PRN_ERR ( " failed to complete(finish) multipart post for file(%d). " , physical_fd ) ;
2020-08-22 12:40:53 +00:00
return result ;
}
// truncate file to zero
2021-05-23 16:28:50 +00:00
if ( - 1 = = ftruncate ( physical_fd , 0 ) ) {
2020-08-22 12:40:53 +00:00
// So the file has already been removed, skip error.
2021-05-23 16:28:50 +00:00
S3FS_PRN_ERR ( " failed to truncate file(%d) to zero, but continue... " , physical_fd ) ;
2020-08-22 12:40:53 +00:00
}
// put pading headers
if ( 0 ! = ( result = UploadPendingMeta ( ) ) ) {
return result ;
}
}
if ( 0 = = result ) {
pagelist . ClearAllModified ( ) ;
}
return result ;
}
// [NOTICE]
// Need to lock before calling this method.
bool FdEntity : : ReserveDiskSpace ( off_t size )
{
if ( FdManager : : ReserveDiskSpace ( size ) ) {
return true ;
}
if ( ! pagelist . IsModified ( ) ) {
// try to clear all cache for this fd.
pagelist . Init ( pagelist . Size ( ) , false , false ) ;
2021-05-23 16:28:50 +00:00
if ( - 1 = = ftruncate ( physical_fd , 0 ) | | - 1 = = ftruncate ( physical_fd , pagelist . Size ( ) ) ) {
S3FS_PRN_ERR ( " failed to truncate temporary file(%d). " , physical_fd ) ;
2020-08-22 12:40:53 +00:00
return false ;
}
if ( FdManager : : ReserveDiskSpace ( size ) ) {
return true ;
}
}
FdManager : : get ( ) - > CleanupCacheDir ( ) ;
return FdManager : : ReserveDiskSpace ( size ) ;
}
2021-05-23 16:28:50 +00:00
ssize_t FdEntity : : Read ( int fd , char * bytes , off_t start , size_t size , bool force_load )
2020-08-22 12:40:53 +00:00
{
2021-05-23 16:28:50 +00:00
S3FS_PRN_DBG ( " [path=%s][pseudo_fd=%d][physical_fd=%d][offset=%lld][size=%zu] " , path . c_str ( ) , fd , physical_fd , static_cast < long long int > ( start ) , size ) ;
2020-08-22 12:40:53 +00:00
2021-05-23 16:28:50 +00:00
if ( - 1 = = physical_fd | | NULL = = CheckPseudoFdFlags ( fd , false ) ) {
S3FS_PRN_DBG ( " pseudo_fd(%d) to physical_fd(%d) for path(%s) is not opened or not readable " , fd , physical_fd , path . c_str ( ) ) ;
2020-08-22 12:40:53 +00:00
return - EBADF ;
}
2021-05-23 16:28:50 +00:00
2021-05-28 15:11:55 +00:00
AutoLock auto_lock ( & fdent_lock ) ;
AutoLock auto_lock2 ( & fdent_data_lock ) ;
2020-08-22 12:40:53 +00:00
if ( force_load ) {
pagelist . SetPageLoadedStatus ( start , size , PageList : : PAGE_NOT_LOAD_MODIFIED ) ;
}
ssize_t rsize ;
// check disk space
if ( 0 < pagelist . GetTotalUnloadedPageSize ( start , size ) ) {
// load size(for prefetch)
size_t load_size = size ;
if ( start + static_cast < ssize_t > ( size ) < pagelist . Size ( ) ) {
2020-09-11 09:37:24 +00:00
ssize_t prefetch_max_size = std : : max ( static_cast < off_t > ( size ) , S3fsCurl : : GetMultipartSize ( ) * S3fsCurl : : GetMaxParallelCount ( ) ) ;
2020-08-22 12:40:53 +00:00
if ( start + prefetch_max_size < pagelist . Size ( ) ) {
load_size = prefetch_max_size ;
} else {
load_size = pagelist . Size ( ) - start ;
}
}
if ( ! ReserveDiskSpace ( load_size ) ) {
S3FS_PRN_WARN ( " could not reserve disk space for pre-fetch download " ) ;
load_size = size ;
if ( ! ReserveDiskSpace ( load_size ) ) {
S3FS_PRN_ERR ( " could not reserve disk space for pre-fetch download " ) ;
return - ENOSPC ;
}
}
// Loading
int result = 0 ;
if ( 0 < size ) {
result = Load ( start , load_size , /*lock_already_held=*/ true ) ;
}
FdManager : : FreeReservedDiskSpace ( load_size ) ;
if ( 0 ! = result ) {
S3FS_PRN_ERR ( " could not download. start(%lld), size(%zu), errno(%d) " , static_cast < long long int > ( start ) , size , result ) ;
2021-04-29 22:09:00 +00:00
return result ;
2020-08-22 12:40:53 +00:00
}
}
// Reading
2021-05-23 16:28:50 +00:00
if ( - 1 = = ( rsize = pread ( physical_fd , bytes , size , start ) ) ) {
2020-08-22 12:40:53 +00:00
S3FS_PRN_ERR ( " pread failed. errno(%d) " , errno ) ;
return - errno ;
}
return rsize ;
}
2021-05-23 16:28:50 +00:00
ssize_t FdEntity : : Write ( int fd , const char * bytes , off_t start , size_t size )
2020-08-22 12:40:53 +00:00
{
2021-05-23 16:28:50 +00:00
S3FS_PRN_DBG ( " [path=%s][pseudo_fd=%d][physical_fd=%d][offset=%lld][size=%zu] " , path . c_str ( ) , fd , physical_fd , static_cast < long long int > ( start ) , size ) ;
2020-08-22 12:40:53 +00:00
2021-05-23 16:28:50 +00:00
PseudoFdInfo * pseudo_obj = NULL ;
2021-05-23 16:55:25 +00:00
if ( - 1 = = physical_fd | | NULL = = ( pseudo_obj = CheckPseudoFdFlags ( fd , false ) ) ) {
2021-05-23 16:28:50 +00:00
S3FS_PRN_DBG ( " pseudo_fd(%d) to physical_fd(%d) for path(%s) is not opened or not writable " , fd , physical_fd , path . c_str ( ) ) ;
2020-08-22 12:40:53 +00:00
return - EBADF ;
}
2021-05-23 16:28:50 +00:00
2020-08-22 12:40:53 +00:00
// check if not enough disk space left BEFORE locking fd
if ( FdManager : : IsCacheDir ( ) & & ! FdManager : : IsSafeDiskSpace ( NULL , size ) ) {
FdManager : : get ( ) - > CleanupCacheDir ( ) ;
}
2021-05-28 15:11:55 +00:00
AutoLock auto_lock ( & fdent_lock ) ;
AutoLock auto_lock2 ( & fdent_data_lock ) ;
2020-08-22 12:40:53 +00:00
// check file size
if ( pagelist . Size ( ) < start ) {
// grow file size
2021-05-23 16:28:50 +00:00
if ( - 1 = = ftruncate ( physical_fd , start ) ) {
S3FS_PRN_ERR ( " failed to truncate temporary file(%d). " , physical_fd ) ;
2021-04-29 22:09:00 +00:00
return - errno ;
2020-08-22 12:40:53 +00:00
}
// add new area
pagelist . SetPageLoadedStatus ( pagelist . Size ( ) , start - pagelist . Size ( ) , PageList : : PAGE_MODIFIED ) ;
}
int result = 0 ;
ssize_t wsize ;
2021-05-23 16:28:50 +00:00
if ( ! pseudo_obj - > IsUploading ( ) ) {
2020-08-22 12:40:53 +00:00
// check disk space
off_t restsize = pagelist . GetTotalUnloadedPageSize ( 0 , start ) + size ;
if ( ReserveDiskSpace ( restsize ) ) {
// enough disk space
// Load uninitialized area which starts from 0 to (start + size) before writing.
if ( ! FdEntity : : mixmultipart ) {
if ( 0 < start ) {
result = Load ( 0 , start , /*lock_already_held=*/ true ) ;
}
}
FdManager : : FreeReservedDiskSpace ( restsize ) ;
if ( 0 ! = result ) {
S3FS_PRN_ERR ( " failed to load uninitialized area before writing(errno=%d) " , result ) ;
2021-05-21 15:27:49 +00:00
return result ;
2020-08-22 12:40:53 +00:00
}
} else {
// no enough disk space
2021-03-09 20:22:54 +00:00
if ( nomultipart ) {
2021-05-23 16:28:50 +00:00
S3FS_PRN_WARN ( " Not enough local storage to cache write request: [path=%s][physical_fd=%d][offset=%lld][size=%zu] " , path . c_str ( ) , physical_fd , static_cast < long long int > ( start ) , size ) ;
2021-03-09 20:22:54 +00:00
return - ENOSPC ; // No space left on device
}
2021-05-04 19:09:35 +00:00
if ( ( start + static_cast < off_t > ( size ) ) < = S3fsCurl : : GetMultipartSize ( ) ) {
2021-05-23 16:28:50 +00:00
S3FS_PRN_WARN ( " Not enough local storage to cache write request till multipart upload can start: [path=%s][physical_fd=%d][offset=%lld][size=%zu] " , path . c_str ( ) , physical_fd , static_cast < long long int > ( start ) , size ) ;
2021-05-04 19:09:35 +00:00
return - ENOSPC ; // No space left on device
}
2021-05-23 16:28:50 +00:00
if ( 0 ! = ( result = NoCachePreMultipartPost ( pseudo_obj ) ) ) {
2020-08-22 12:40:53 +00:00
S3FS_PRN_ERR ( " failed to switch multipart uploading with no cache(errno=%d) " , result ) ;
2021-05-21 15:27:49 +00:00
return result ;
2020-08-22 12:40:53 +00:00
}
// start multipart uploading
2021-05-23 16:28:50 +00:00
if ( 0 ! = ( result = NoCacheLoadAndPost ( pseudo_obj , 0 , start ) ) ) {
2020-08-22 12:40:53 +00:00
S3FS_PRN_ERR ( " failed to load uninitialized area and multipart uploading it(errno=%d) " , result ) ;
2021-05-21 15:27:49 +00:00
return result ;
2020-08-22 12:40:53 +00:00
}
2021-05-23 16:28:50 +00:00
pseudo_obj - > SetUntreated ( start , 0 ) ;
2020-08-22 12:40:53 +00:00
}
} else {
// already start multipart uploading
}
// Writing
2021-05-23 16:28:50 +00:00
if ( - 1 = = ( wsize = pwrite ( physical_fd , bytes , size , start ) ) ) {
2020-08-22 12:40:53 +00:00
S3FS_PRN_ERR ( " pwrite failed. errno(%d) " , errno ) ;
return - errno ;
}
if ( 0 < wsize ) {
pagelist . SetPageLoadedStatus ( start , wsize , PageList : : PAGE_LOAD_MODIFIED ) ;
}
// Load uninitialized area which starts from (start + size) to EOF after writing.
if ( ! FdEntity : : mixmultipart ) {
if ( pagelist . Size ( ) > start + static_cast < off_t > ( size ) ) {
result = Load ( start + size , pagelist . Size ( ) , /*lock_already_held=*/ true ) ;
if ( 0 ! = result ) {
S3FS_PRN_ERR ( " failed to load uninitialized area after writing(errno=%d) " , result ) ;
2021-05-21 15:27:49 +00:00
return result ;
2020-08-22 12:40:53 +00:00
}
}
}
// check multipart uploading
2021-05-23 16:28:50 +00:00
if ( pseudo_obj - > IsUploading ( ) ) {
off_t untreated_start = 0 ;
off_t untreated_size = 0 ;
pseudo_obj - > GetUntreated ( untreated_start , untreated_size ) ;
untreated_size + = wsize ;
if ( S3fsCurl : : GetMultipartSize ( ) < = untreated_size ) {
2020-08-22 12:40:53 +00:00
// over one multipart size
2021-05-23 16:28:50 +00:00
if ( 0 ! = ( result = NoCacheMultipartPost ( pseudo_obj , physical_fd , untreated_start , untreated_size ) ) ) {
S3FS_PRN_ERR ( " failed to multipart post(start=%lld, size=%lld) for file(%d). " , static_cast < long long int > ( untreated_start ) , static_cast < long long int > ( untreated_size ) , physical_fd ) ;
2020-08-22 12:40:53 +00:00
return result ;
}
2021-05-23 16:28:50 +00:00
2020-08-22 12:40:53 +00:00
// [NOTE]
// truncate file to zero and set length to part offset + size
// after this, file length is (offset + size), but file does not use any disk space.
//
2021-05-23 16:28:50 +00:00
if ( - 1 = = ftruncate ( physical_fd , 0 ) | | - 1 = = ftruncate ( physical_fd , ( untreated_start + untreated_size ) ) ) {
S3FS_PRN_ERR ( " failed to truncate file(%d). " , physical_fd ) ;
2021-04-29 22:09:00 +00:00
return - errno ;
2020-08-22 12:40:53 +00:00
}
2021-05-23 16:28:50 +00:00
pseudo_obj - > SetUntreated ( untreated_start + untreated_size , 0 ) ;
2020-08-22 12:40:53 +00:00
}
}
return wsize ;
}
// [NOTE]
// Returns true if merged to orgmeta.
// If true is returned, the caller can update the header.
// If it is false, do not update the header because multipart upload is in progress.
// In this case, the header is pending internally and is updated after the upload
// is complete(flush file).
//
bool FdEntity : : MergeOrgMeta ( headers_t & updatemeta )
{
AutoLock auto_lock ( & fdent_lock ) ;
2020-09-16 14:45:28 +00:00
merge_headers ( orgmeta , updatemeta , true ) ; // overwrite all keys
// [NOTE]
// this is special cases, we remove the key which has empty values.
for ( headers_t : : iterator hiter = orgmeta . begin ( ) ; hiter ! = orgmeta . end ( ) ; ) {
if ( hiter - > second . empty ( ) ) {
orgmeta . erase ( hiter + + ) ;
} else {
+ + hiter ;
}
}
updatemeta = orgmeta ;
2020-09-20 14:04:59 +00:00
orgmeta . erase ( " x-amz-copy-source " ) ;
2020-10-03 02:14:23 +00:00
// update ctime/mtime/atime
2021-04-18 04:11:12 +00:00
struct timespec mtime = get_mtime ( updatemeta , false ) ; // not overcheck
struct timespec ctime = get_ctime ( updatemeta , false ) ; // not overcheck
struct timespec atime = get_atime ( updatemeta , false ) ; // not overcheck
if ( 0 < = mtime . tv_sec ) {
SetMCtime ( mtime , ( ctime . tv_sec < 0 ? mtime : ctime ) , true ) ;
2020-10-03 02:14:23 +00:00
}
2021-04-18 04:11:12 +00:00
if ( 0 < = atime . tv_sec ) {
2020-10-03 02:14:23 +00:00
SetAtime ( atime , true ) ;
2020-08-22 12:40:53 +00:00
}
2021-05-23 16:28:50 +00:00
is_meta_pending | = IsUploading ( true ) ;
2020-09-16 14:45:28 +00:00
2020-09-20 14:04:59 +00:00
return is_meta_pending ;
2020-08-22 12:40:53 +00:00
}
// global function in s3fs.cpp
2021-02-20 17:03:41 +00:00
int put_headers ( const char * path , headers_t & meta , bool is_copy ) ;
2020-08-22 12:40:53 +00:00
int FdEntity : : UploadPendingMeta ( )
{
2020-09-20 14:04:59 +00:00
if ( ! is_meta_pending ) {
return 0 ;
}
2020-08-22 12:40:53 +00:00
2020-09-20 14:04:59 +00:00
AutoLock auto_lock ( & fdent_lock ) ;
headers_t updatemeta = orgmeta ;
updatemeta [ " x-amz-copy-source " ] = urlEncode ( service_path + bucket + get_realpath ( path . c_str ( ) ) ) ;
2020-09-16 14:45:28 +00:00
// put headers, no need to update mtime to avoid dead lock
2021-02-20 17:03:41 +00:00
int result = put_headers ( path . c_str ( ) , updatemeta , true ) ;
2020-09-16 14:45:28 +00:00
if ( 0 ! = result ) {
S3FS_PRN_ERR ( " failed to put header after flushing file(%s) by(%d). " , path . c_str ( ) , result ) ;
2020-08-22 12:40:53 +00:00
}
2020-09-20 14:04:59 +00:00
is_meta_pending = false ;
2020-08-22 12:40:53 +00:00
return result ;
}
2020-11-03 06:42:03 +00:00
// [NOTE]
// For systems where the fallocate function cannot be detected, use a dummy function.
// ex. OSX
//
# ifndef HAVE_FALLOCATE
static int fallocate ( int /*fd*/ , int /*mode*/ , off_t /*offset*/ , off_t /*len*/ )
{
errno = ENOSYS ; // This is a bad idea, but the caller can handle it simply.
return - 1 ;
}
# endif // HAVE_FALLOCATE
2021-04-21 13:45:49 +00:00
// [NOTE]
// If HAVE_FALLOCATE is undefined, or versions prior to 2.6.38(fallocate function exists),
// following flags are undefined. Then we need these symbols defined in fallocate, so we
// define them here.
// The definitions are copied from linux/falloc.h, but if HAVE_FALLOCATE is undefined,
// these values can be anything.
//
# ifndef FALLOC_FL_PUNCH_HOLE
# define FALLOC_FL_PUNCH_HOLE 0x02 /* de-allocates range */
# endif
# ifndef FALLOC_FL_KEEP_SIZE
# define FALLOC_FL_KEEP_SIZE 0x01
# endif
2020-11-03 06:42:03 +00:00
// [NOTE]
// This method punches an area(on cache file) that has no data at the time it is called.
// This is called to prevent the cache file from growing.
// However, this method uses the non-portable(Linux specific) system call fallocate().
// Also, depending on the file system, FALLOC_FL_PUNCH_HOLE mode may not work and HOLE
// will not open.(Filesystems for which this method works are ext4, btrfs, xfs, etc.)
//
bool FdEntity : : PunchHole ( off_t start , size_t size )
{
2021-05-23 16:28:50 +00:00
S3FS_PRN_DBG ( " [path=%s][physical_fd=%d][offset=%lld][size=%zu] " , path . c_str ( ) , physical_fd , static_cast < long long int > ( start ) , size ) ;
2020-11-03 06:42:03 +00:00
2021-05-23 16:28:50 +00:00
if ( - 1 = = physical_fd ) {
2020-11-03 06:42:03 +00:00
return false ;
}
AutoLock auto_lock ( & fdent_data_lock ) ;
// get page list that have no data
fdpage_list_t nodata_pages ;
if ( ! pagelist . GetNoDataPageLists ( nodata_pages ) ) {
S3FS_PRN_ERR ( " filed to get page list that have no data. " ) ;
return false ;
}
if ( nodata_pages . empty ( ) ) {
S3FS_PRN_DBG ( " there is no page list that have no data, so nothing to do. " ) ;
return true ;
}
// try to punch hole to file
for ( fdpage_list_t : : const_iterator iter = nodata_pages . begin ( ) ; iter ! = nodata_pages . end ( ) ; + + iter ) {
2021-05-23 16:28:50 +00:00
if ( 0 ! = fallocate ( physical_fd , FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE , iter - > offset , iter - > bytes ) ) {
2020-11-03 06:42:03 +00:00
if ( ENOSYS = = errno | | EOPNOTSUPP = = errno ) {
S3FS_PRN_ERR ( " filed to fallocate for punching hole to file with errno(%d), it maybe the fallocate function is not implemented in this kernel, or the file system does not support FALLOC_FL_PUNCH_HOLE. " , errno ) ;
} else {
S3FS_PRN_ERR ( " filed to fallocate for punching hole to file with errno(%d) " , errno ) ;
}
return false ;
}
if ( ! pagelist . SetPageLoadedStatus ( iter - > offset , iter - > bytes , PageList : : PAGE_NOT_LOAD_MODIFIED ) ) {
S3FS_PRN_ERR ( " succeed to punch HOLEs in the cache file, but failed to update the cache stat. " ) ;
return false ;
}
S3FS_PRN_DBG ( " made a hole at [%lld - %lld bytes](into a boundary) of the cache file. " , static_cast < long long int > ( iter - > offset ) , static_cast < long long int > ( iter - > bytes ) ) ;
}
return true ;
}
2020-08-22 12:40:53 +00:00
/*
* Local variables :
* tab - width : 4
* c - basic - offset : 4
* End :
* vim600 : expandtab sw = 4 ts = 4 fdm = marker
* vim < 600 : expandtab sw = 4 ts = 4
*/