1 /*
2  * s3fs - FUSE-based file system backed by Amazon S3
3  *
4  * Copyright(C) 2007 Takeshi Nakatani <ggtakec.com>
5  *
6  * This program is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU General Public License
8  * as published by the Free Software Foundation; either version 2
9  * of the License, or (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
19  */
20 
21 #include <cstdio>
22 #include <cstdlib>
23 #include <cerrno>
24 #include <limits.h>
25 #include <unistd.h>
26 #include <sys/types.h>
27 #include <dirent.h>
28 
29 #include "common.h"
30 #include "s3fs.h"
31 #include "fdcache.h"
32 #include "fdcache_pseudofd.h"
33 #include "s3fs_util.h"
34 #include "s3fs_logger.h"
35 #include "string_util.h"
36 #include "autolock.h"
37 
38 //
39 // The following symbols are used by FdManager::RawCheckAllCache().
40 //
41 #define CACHEDBG_FMT_DIR_PROB   "Directory: %s"
42 #define CACHEDBG_FMT_HEAD       "---------------------------------------------------------------------------\n" \
43                                 "Check cache file and its stats file consistency at %s\n"                       \
44                                 "---------------------------------------------------------------------------"
45 #define CACHEDBG_FMT_FOOT       "---------------------------------------------------------------------------\n" \
46                                 "Summary - Total files:                %d\n" \
47                                 "          Detected error files:       %d\n" \
48                                 "          Detected error directories: %d\n" \
49                                 "---------------------------------------------------------------------------"
50 #define CACHEDBG_FMT_FILE_OK    "File:      %s%s    -> [OK] no problem"
51 #define CACHEDBG_FMT_FILE_PROB  "File:      %s%s"
52 #define CACHEDBG_FMT_DIR_PROB   "Directory: %s"
53 #define CACHEDBG_FMT_ERR_HEAD   "           -> [E] there is a mark that data exists in stats, but there is no data in the cache file."
54 #define CACHEDBG_FMT_WARN_HEAD  "           -> [W] These show no data in stats, but there is evidence of data in the cache file(no problem)."
55 #define CACHEDBG_FMT_WARN_OPEN  "\n           -> [W] This file is currently open and may not provide accurate analysis results."
56 #define CACHEDBG_FMT_CRIT_HEAD  "           -> [C] %s"
57 #define CACHEDBG_FMT_CRIT_HEAD2 "           -> [C] "
58 #define CACHEDBG_FMT_PROB_BLOCK "                  0x%016zx(0x%016zx bytes)"
59 
60 // [NOTE]
61 // NOCACHE_PATH_PREFIX symbol needs for not using cache mode.
62 // Now s3fs I/F functions in s3fs.cpp has left the processing
63 // to FdManager and FdEntity class. FdManager class manages
64 // the list of local file stat and file descriptor in conjunction
65 // with the FdEntity class.
66 // When s3fs is not using local cache, it means FdManager must
67 // return new temporary file descriptor at each opening it.
68 // Then FdManager caches fd by key which is dummy file path
69 // instead of real file path.
70 // This process may not be complete, but it is easy way can
71 // be realized.
72 //
73 #define NOCACHE_PATH_PREFIX_FORM    " __S3FS_UNEXISTED_PATH_%lx__ / "      // important space words for simply
74 
75 //------------------------------------------------
76 // FdManager class variable
77 //------------------------------------------------
78 FdManager       FdManager::singleton;
79 pthread_mutex_t FdManager::fd_manager_lock;
80 pthread_mutex_t FdManager::cache_cleanup_lock;
81 pthread_mutex_t FdManager::reserved_diskspace_lock;
82 bool            FdManager::is_lock_init(false);
83 std::string     FdManager::cache_dir;
84 bool            FdManager::check_cache_dir_exist(false);
85 off_t           FdManager::free_disk_space = 0;
86 off_t           FdManager::fake_used_disk_space = 0;
87 std::string     FdManager::check_cache_output;
88 bool            FdManager::checked_lseek(false);
89 bool            FdManager::have_lseek_hole(false);
90 std::string     FdManager::tmp_dir = "/tmp";
91 
92 //------------------------------------------------
93 // FdManager class methods
94 //------------------------------------------------
SetCacheDir(const char * dir)95 bool FdManager::SetCacheDir(const char* dir)
96 {
97     if(!dir || '\0' == dir[0]){
98         cache_dir = "";
99     }else{
100         cache_dir = dir;
101     }
102     return true;
103 }
104 
SetCacheCheckOutput(const char * path)105 bool FdManager::SetCacheCheckOutput(const char* path)
106 {
107     if(!path || '\0' == path[0]){
108         check_cache_output.erase();
109     }else{
110         check_cache_output = path;
111     }
112     return true;
113 }
114 
DeleteCacheDirectory()115 bool FdManager::DeleteCacheDirectory()
116 {
117     if(FdManager::cache_dir.empty()){
118         return true;
119     }
120 
121     std::string cache_path;
122     if(!FdManager::MakeCachePath(NULL, cache_path, false)){
123         return false;
124     }
125     if(!delete_files_in_dir(cache_path.c_str(), true)){
126         return false;
127     }
128 
129     std::string mirror_path = FdManager::cache_dir + "/." + bucket + ".mirror";
130     if(!delete_files_in_dir(mirror_path.c_str(), true)){
131         return false;
132     }
133 
134     return true;
135 }
136 
DeleteCacheFile(const char * path)137 int FdManager::DeleteCacheFile(const char* path)
138 {
139     S3FS_PRN_INFO3("[path=%s]", SAFESTRPTR(path));
140 
141     if(!path){
142         return -EIO;
143     }
144     if(FdManager::cache_dir.empty()){
145         return 0;
146     }
147     std::string cache_path;
148     if(!FdManager::MakeCachePath(path, cache_path, false)){
149         return 0;
150     }
151     int result = 0;
152     if(0 != unlink(cache_path.c_str())){
153         if(ENOENT == errno){
154             S3FS_PRN_DBG("failed to delete file(%s): errno=%d", path, errno);
155         }else{
156             S3FS_PRN_ERR("failed to delete file(%s): errno=%d", path, errno);
157         }
158         result = -errno;
159     }
160     if(!CacheFileStat::DeleteCacheFileStat(path)){
161         if(ENOENT == errno){
162             S3FS_PRN_DBG("failed to delete stat file(%s): errno=%d", path, errno);
163         }else{
164             S3FS_PRN_ERR("failed to delete stat file(%s): errno=%d", path, errno);
165         }
166         if(0 != errno){
167             result = -errno;
168         }else{
169             result = -EIO;
170         }
171     }
172     return result;
173 }
174 
MakeCachePath(const char * path,std::string & cache_path,bool is_create_dir,bool is_mirror_path)175 bool FdManager::MakeCachePath(const char* path, std::string& cache_path, bool is_create_dir, bool is_mirror_path)
176 {
177     if(FdManager::cache_dir.empty()){
178         cache_path = "";
179         return true;
180     }
181 
182     std::string resolved_path(FdManager::cache_dir);
183     if(!is_mirror_path){
184         resolved_path += "/";
185         resolved_path += bucket;
186     }else{
187         resolved_path += "/.";
188         resolved_path += bucket;
189         resolved_path += ".mirror";
190     }
191 
192     if(is_create_dir){
193         int result;
194         if(0 != (result = mkdirp(resolved_path + mydirname(path), 0777))){
195             S3FS_PRN_ERR("failed to create dir(%s) by errno(%d).", path, result);
196             return false;
197         }
198     }
199     if(!path || '\0' == path[0]){
200         cache_path = resolved_path;
201     }else{
202         cache_path = resolved_path + SAFESTRPTR(path);
203     }
204     return true;
205 }
206 
CheckCacheTopDir()207 bool FdManager::CheckCacheTopDir()
208 {
209     if(FdManager::cache_dir.empty()){
210         return true;
211     }
212     std::string toppath(FdManager::cache_dir + "/" + bucket);
213 
214     return check_exist_dir_permission(toppath.c_str());
215 }
216 
MakeRandomTempPath(const char * path,std::string & tmppath)217 bool FdManager::MakeRandomTempPath(const char* path, std::string& tmppath)
218 {
219     char szBuff[64];
220 
221     sprintf(szBuff, NOCACHE_PATH_PREFIX_FORM, random());     // worry for performance, but maybe don't worry.
222     tmppath  = szBuff;
223     tmppath += path ? path : "";
224     return true;
225 }
226 
SetCheckCacheDirExist(bool is_check)227 bool FdManager::SetCheckCacheDirExist(bool is_check)
228 {
229     bool old = FdManager::check_cache_dir_exist;
230     FdManager::check_cache_dir_exist = is_check;
231     return old;
232 }
233 
CheckCacheDirExist()234 bool FdManager::CheckCacheDirExist()
235 {
236     if(!FdManager::check_cache_dir_exist){
237         return true;
238     }
239     if(FdManager::cache_dir.empty()){
240         return true;
241     }
242     return IsDir(&cache_dir);
243 }
244 
GetEnsureFreeDiskSpace()245 off_t FdManager::GetEnsureFreeDiskSpace()
246 {
247     AutoLock auto_lock(&FdManager::reserved_diskspace_lock);
248     return FdManager::free_disk_space;
249 }
250 
SetEnsureFreeDiskSpace(off_t size)251 off_t FdManager::SetEnsureFreeDiskSpace(off_t size)
252 {
253     AutoLock auto_lock(&FdManager::reserved_diskspace_lock);
254     off_t old = FdManager::free_disk_space;
255     FdManager::free_disk_space = size;
256     return old;
257 }
258 
InitFakeUsedDiskSize(off_t fake_freesize)259 bool FdManager::InitFakeUsedDiskSize(off_t fake_freesize)
260 {
261     FdManager::fake_used_disk_space = 0;    // At first, clear this value because this value is used in GetFreeDiskSpace.
262 
263     off_t actual_freesize = FdManager::GetFreeDiskSpace(NULL);
264 
265     if(fake_freesize < actual_freesize){
266         FdManager::fake_used_disk_space = actual_freesize - fake_freesize;
267     }else{
268         FdManager::fake_used_disk_space = 0;
269     }
270     return true;
271 }
272 
GetFreeDiskSpace(const char * path)273 off_t FdManager::GetFreeDiskSpace(const char* path)
274 {
275     struct statvfs vfsbuf;
276     std::string ctoppath;
277     if(!FdManager::cache_dir.empty()){
278         ctoppath = FdManager::cache_dir + "/";
279         ctoppath = get_exist_directory_path(ctoppath);    // existed directory
280         if(ctoppath != "/"){
281             ctoppath += "/";
282         }
283     }else{
284         ctoppath = tmp_dir + "/";
285     }
286     if(path && '\0' != *path){
287         ctoppath += path;
288     }else{
289         ctoppath += ".";
290     }
291     if(-1 == statvfs(ctoppath.c_str(), &vfsbuf)){
292         S3FS_PRN_ERR("could not get vfs stat by errno(%d)", errno);
293         return 0;
294     }
295 
296     off_t actual_freesize = vfsbuf.f_bavail * vfsbuf.f_frsize;
297 
298     return (FdManager::fake_used_disk_space < actual_freesize ? (actual_freesize - FdManager::fake_used_disk_space) : 0);
299 }
300 
IsSafeDiskSpace(const char * path,off_t size)301 bool FdManager::IsSafeDiskSpace(const char* path, off_t size)
302 {
303     off_t fsize = FdManager::GetFreeDiskSpace(path);
304     return size + FdManager::GetEnsureFreeDiskSpace() <= fsize;
305 }
306 
HaveLseekHole()307 bool FdManager::HaveLseekHole()
308 {
309     if(FdManager::checked_lseek){
310         return FdManager::have_lseek_hole;
311     }
312 
313     // create temporary file
314     FILE* ptmpfp;
315     int   fd;
316     if(NULL == (ptmpfp = MakeTempFile()) || -1 == (fd = fileno(ptmpfp))){
317         S3FS_PRN_ERR("failed to open temporary file by errno(%d)", errno);
318         if(ptmpfp){
319             fclose(ptmpfp);
320         }
321         FdManager::checked_lseek   = true;
322         FdManager::have_lseek_hole = false;
323         return FdManager::have_lseek_hole;
324     }
325 
326     // check SEEK_DATA/SEEK_HOLE options
327     bool result = true;
328     if(-1 == lseek(fd, 0, SEEK_DATA)){
329         if(EINVAL == errno){
330             S3FS_PRN_ERR("lseek does not support SEEK_DATA");
331             result = false;
332         }
333     }
334     if(result && -1 == lseek(fd, 0, SEEK_HOLE)){
335         if(EINVAL == errno){
336             S3FS_PRN_ERR("lseek does not support SEEK_HOLE");
337             result = false;
338         }
339     }
340     close(fd);
341 
342     FdManager::checked_lseek   = true;
343     FdManager::have_lseek_hole = result;
344     return FdManager::have_lseek_hole;
345 }
346 
SetTmpDir(const char * dir)347 bool FdManager::SetTmpDir(const char *dir)
348 {
349     if(!dir || '\0' == dir[0]){
350         tmp_dir = "/tmp";
351     }else{
352         tmp_dir = dir;
353     }
354     return true;
355 }
356 
IsDir(const std::string * dir)357 bool FdManager::IsDir(const std::string* dir)
358 {
359     // check the directory
360     struct stat st;
361     if(0 != stat(dir->c_str(), &st)){
362         S3FS_PRN_ERR("could not stat() directory %s by errno(%d).", dir->c_str(), errno);
363         return false;
364     }
365     if(!S_ISDIR(st.st_mode)){
366         S3FS_PRN_ERR("the directory %s is not a directory.", dir->c_str());
367         return false;
368     }
369     return true;
370 }
371 
CheckTmpDirExist()372 bool FdManager::CheckTmpDirExist()
373 {
374     if(FdManager::tmp_dir.empty()){
375         return true;
376     }
377     return IsDir(&tmp_dir);
378 }
379 
MakeTempFile()380 FILE* FdManager::MakeTempFile() {
381     int fd;
382     char cfn[PATH_MAX];
383     std::string fn = tmp_dir + "/s3fstmp.XXXXXX";
384     strncpy(cfn, fn.c_str(), sizeof(cfn) - 1);
385     cfn[sizeof(cfn) - 1] = '\0';
386 
387     fd = mkstemp(cfn);
388     if (-1 == fd) {
389         S3FS_PRN_ERR("failed to create tmp file. errno(%d)", errno);
390         return NULL;
391     }
392     if (-1 == unlink(cfn)) {
393         S3FS_PRN_ERR("failed to delete tmp file. errno(%d)", errno);
394         return NULL;
395     }
396     return fdopen(fd, "rb+");
397 }
398 
HasOpenEntityFd(const char * path)399 bool FdManager::HasOpenEntityFd(const char* path)
400 {
401     AutoLock auto_lock(&FdManager::fd_manager_lock);
402 
403     FdEntity*   ent;
404     int         fd = -1;
405     if(NULL == (ent = FdManager::singleton.GetFdEntity(path, fd, false, true))){
406         return false;
407     }
408     return (0 < ent->GetOpenCount());
409 }
410 
411 //------------------------------------------------
412 // FdManager methods
413 //------------------------------------------------
FdManager()414 FdManager::FdManager()
415 {
416     if(this == FdManager::get()){
417         pthread_mutexattr_t attr;
418         pthread_mutexattr_init(&attr);
419 #if S3FS_PTHREAD_ERRORCHECK
420         pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_ERRORCHECK);
421 #endif
422         int result;
423         if(0 != (result = pthread_mutex_init(&FdManager::fd_manager_lock, &attr))){
424             S3FS_PRN_CRIT("failed to init fd_manager_lock: %d", result);
425             abort();
426         }
427         if(0 != (result = pthread_mutex_init(&FdManager::cache_cleanup_lock, &attr))){
428             S3FS_PRN_CRIT("failed to init cache_cleanup_lock: %d", result);
429             abort();
430         }
431         if(0 != (result = pthread_mutex_init(&FdManager::reserved_diskspace_lock, &attr))){
432             S3FS_PRN_CRIT("failed to init reserved_diskspace_lock: %d", result);
433             abort();
434         }
435         FdManager::is_lock_init = true;
436     }else{
437         abort();
438     }
439 }
440 
~FdManager()441 FdManager::~FdManager()
442 {
443     if(this == FdManager::get()){
444         for(fdent_map_t::iterator iter = fent.begin(); fent.end() != iter; ++iter){
445             FdEntity* ent = (*iter).second;
446             S3FS_PRN_WARN("To exit with the cache file opened: path=%s, refcnt=%d", ent->GetPath(), ent->GetOpenCount());
447             delete ent;
448         }
449         fent.clear();
450 
451         if(FdManager::is_lock_init){
452             int result;
453             if(0 != (result = pthread_mutex_destroy(&FdManager::fd_manager_lock))){
454                 S3FS_PRN_CRIT("failed to destroy fd_manager_lock: %d", result);
455                 abort();
456             }
457             if(0 != (result = pthread_mutex_destroy(&FdManager::cache_cleanup_lock))){
458                 S3FS_PRN_CRIT("failed to destroy cache_cleanup_lock: %d", result);
459                 abort();
460             }
461             if(0 != (result = pthread_mutex_destroy(&FdManager::reserved_diskspace_lock))){
462                 S3FS_PRN_CRIT("failed to destroy reserved_diskspace_lock: %d", result);
463                 abort();
464             }
465             FdManager::is_lock_init = false;
466         }
467     }else{
468         abort();
469     }
470 }
471 
GetFdEntity(const char * path,int & existfd,bool newfd,bool lock_already_held)472 FdEntity* FdManager::GetFdEntity(const char* path, int& existfd, bool newfd, bool lock_already_held)
473 {
474     S3FS_PRN_INFO3("[path=%s][pseudo_fd=%d]", SAFESTRPTR(path), existfd);
475 
476     if(!path || '\0' == path[0]){
477         return NULL;
478     }
479     AutoLock auto_lock(&FdManager::fd_manager_lock, lock_already_held ? AutoLock::ALREADY_LOCKED : AutoLock::NONE);
480 
481     fdent_map_t::iterator iter = fent.find(std::string(path));
482     if(fent.end() != iter && iter->second){
483         if(-1 == existfd){
484             if(newfd){
485                 existfd = iter->second->OpenPseudoFd(O_RDWR);    // [NOTE] O_RDWR flags
486             }
487             return iter->second;
488         }else if(iter->second->FindPseudoFd(existfd)){
489             if(newfd){
490                 existfd = iter->second->Dup(existfd);
491             }
492             return iter->second;
493         }
494     }
495 
496     if(-1 != existfd){
497         for(iter = fent.begin(); iter != fent.end(); ++iter){
498             if(iter->second && iter->second->FindPseudoFd(existfd)){
499                 // found opened fd in map
500                 if(0 == strcmp(iter->second->GetPath(), path)){
501                     if(newfd){
502                         existfd = iter->second->Dup(existfd);
503                     }
504                     return iter->second;
505                 }
506                 // found fd, but it is used another file(file descriptor is recycled)
507                 // so returns NULL.
508                 break;
509             }
510         }
511     }
512 
513     // If the cache directory is not specified, s3fs opens a temporary file
514     // when the file is opened.
515     if(!FdManager::IsCacheDir()){
516         for(iter = fent.begin(); iter != fent.end(); ++iter){
517             if(iter->second && iter->second->IsOpen() && 0 == strcmp(iter->second->GetPath(), path)){
518                 return iter->second;
519             }
520         }
521     }
522     return NULL;
523 }
524 
Open(int & fd,const char * path,headers_t * pmeta,off_t size,time_t time,int flags,bool force_tmpfile,bool is_create,AutoLock::Type type)525 FdEntity* FdManager::Open(int& fd, const char* path, headers_t* pmeta, off_t size, time_t time, int flags, bool force_tmpfile, bool is_create, AutoLock::Type type)
526 {
527     S3FS_PRN_DBG("[path=%s][size=%lld][time=%lld][flags=0x%x]", SAFESTRPTR(path), static_cast<long long>(size), static_cast<long long>(time), flags);
528 
529     if(!path || '\0' == path[0]){
530         return NULL;
531     }
532 
533     AutoLock auto_lock(&FdManager::fd_manager_lock);
534 
535     // search in mapping by key(path)
536     fdent_map_t::iterator iter = fent.find(std::string(path));
537     if(fent.end() == iter && !force_tmpfile && !FdManager::IsCacheDir()){
538         // If the cache directory is not specified, s3fs opens a temporary file
539         // when the file is opened.
540         // Then if it could not find a entity in map for the file, s3fs should
541         // search a entity in all which opened the temporary file.
542         //
543         for(iter = fent.begin(); iter != fent.end(); ++iter){
544             if(iter->second && iter->second->IsOpen() && 0 == strcmp(iter->second->GetPath(), path)){
545                 break;      // found opened fd in mapping
546             }
547         }
548     }
549 
550     FdEntity* ent;
551     if(fent.end() != iter){
552         // found
553         ent = iter->second;
554 
555         if(ent->IsModified()){
556             // If the file is being modified and it's size is larger than size parameter, it will not be resized.
557             off_t cur_size = 0;
558             if(ent->GetSize(cur_size) && size <= cur_size){
559                 size = -1;
560             }
561         }
562 
563         // (re)open
564         if(-1 == (fd = ent->Open(pmeta, size, time, flags, type))){
565             S3FS_PRN_ERR("failed to (re)open and create new pseudo fd for path(%s).", path);
566             return NULL;
567         }
568 
569     }else if(is_create){
570         // not found
571         std::string cache_path;
572         if(!force_tmpfile && !FdManager::MakeCachePath(path, cache_path, true)){
573             S3FS_PRN_ERR("failed to make cache path for object(%s).", path);
574             return NULL;
575         }
576         // make new obj
577         ent = new FdEntity(path, cache_path.c_str());
578 
579         // open
580         if(-1 == (fd = ent->Open(pmeta, size, time, flags, type))){
581             delete ent;
582             return NULL;
583         }
584 
585         if(!cache_path.empty()){
586             // using cache
587             fent[std::string(path)] = ent;
588         }else{
589             // not using cache, so the key of fdentity is set not really existing path.
590             // (but not strictly unexisting path.)
591             //
592             // [NOTE]
593             // The reason why this process here, please look at the definition of the
594             // comments of NOCACHE_PATH_PREFIX_FORM symbol.
595             //
596             std::string tmppath;
597             FdManager::MakeRandomTempPath(path, tmppath);
598             fent[tmppath] = ent;
599         }
600     }else{
601         return NULL;
602     }
603     return ent;
604 }
605 
606 // [NOTE]
607 // This method does not create a new pseudo fd.
608 // It just finds existfd and returns the corresponding entity.
609 //
GetExistFdEntity(const char * path,int existfd)610 FdEntity* FdManager::GetExistFdEntity(const char* path, int existfd)
611 {
612     S3FS_PRN_DBG("[path=%s][pseudo_fd=%d]", SAFESTRPTR(path), existfd);
613 
614     AutoLock auto_lock(&FdManager::fd_manager_lock);
615 
616     // search from all entity.
617     for(fdent_map_t::iterator iter = fent.begin(); iter != fent.end(); ++iter){
618         if(iter->second && iter->second->FindPseudoFd(existfd)){
619             // found existfd in entity
620             return iter->second;
621         }
622     }
623     // not found entity
624     return NULL;
625 }
626 
OpenExistFdEntity(const char * path,int & fd,int flags)627 FdEntity* FdManager::OpenExistFdEntity(const char* path, int& fd, int flags)
628 {
629     S3FS_PRN_DBG("[path=%s][flags=0x%x]", SAFESTRPTR(path), flags);
630 
631     // search entity by path, and create pseudo fd
632     FdEntity* ent = Open(fd, path, NULL, -1, -1, flags, false, false, AutoLock::NONE);
633     if(!ent){
634         // Not found entity
635         return NULL;
636     }
637     return ent;
638 }
639 
Rename(const std::string & from,const std::string & to)640 void FdManager::Rename(const std::string &from, const std::string &to)
641 {
642     AutoLock auto_lock(&FdManager::fd_manager_lock);
643 
644     fdent_map_t::iterator iter = fent.find(from);
645     if(fent.end() == iter && !FdManager::IsCacheDir()){
646         // If the cache directory is not specified, s3fs opens a temporary file
647         // when the file is opened.
648         // Then if it could not find a entity in map for the file, s3fs should
649         // search a entity in all which opened the temporary file.
650         //
651         for(iter = fent.begin(); iter != fent.end(); ++iter){
652             if(iter->second && iter->second->IsOpen() && 0 == strcmp(iter->second->GetPath(), from.c_str())){
653                 break;              // found opened fd in mapping
654             }
655         }
656     }
657 
658     if(fent.end() != iter){
659         // found
660         S3FS_PRN_DBG("[from=%s][to=%s]", from.c_str(), to.c_str());
661 
662         FdEntity* ent = iter->second;
663 
664         // retrieve old fd entity from map
665         fent.erase(iter);
666 
667         // rename path and caches in fd entity
668         std::string fentmapkey;
669         if(!ent->RenamePath(to, fentmapkey)){
670             S3FS_PRN_ERR("Failed to rename FdEntity object for %s to %s", from.c_str(), to.c_str());
671             return;
672         }
673 
674         // set new fd entity to map
675         fent[fentmapkey] = ent;
676     }
677 }
678 
Close(FdEntity * ent,int fd)679 bool FdManager::Close(FdEntity* ent, int fd)
680 {
681     S3FS_PRN_DBG("[ent->file=%s][pseudo_fd=%d]", ent ? ent->GetPath() : "", fd);
682 
683     if(!ent || -1 == fd){
684         return true;  // returns success
685     }
686     AutoLock auto_lock(&FdManager::fd_manager_lock);
687 
688     for(fdent_map_t::iterator iter = fent.begin(); iter != fent.end(); ++iter){
689         if(iter->second == ent){
690             ent->Close(fd);
691             if(!ent->IsOpen()){
692                 // remove found entity from map.
693                 fent.erase(iter++);
694 
695                 // check another key name for entity value to be on the safe side
696                 for(; iter != fent.end(); ){
697                     if(iter->second == ent){
698                         fent.erase(iter++);
699                     }else{
700                         ++iter;
701                     }
702                 }
703                 delete ent;
704             }
705             return true;
706         }
707     }
708     return false;
709 }
710 
ChangeEntityToTempPath(FdEntity * ent,const char * path)711 bool FdManager::ChangeEntityToTempPath(FdEntity* ent, const char* path)
712 {
713     AutoLock auto_lock(&FdManager::fd_manager_lock);
714 
715     for(fdent_map_t::iterator iter = fent.begin(); iter != fent.end(); ){
716         if(iter->second == ent){
717             fent.erase(iter++);
718 
719             std::string tmppath;
720             FdManager::MakeRandomTempPath(path, tmppath);
721             fent[tmppath] = ent;
722         }else{
723             ++iter;
724         }
725     }
726     return false;
727 }
728 
CleanupCacheDir()729 void FdManager::CleanupCacheDir()
730 {
731     //S3FS_PRN_DBG("cache cleanup requested");
732 
733     if(!FdManager::IsCacheDir()){
734         return;
735     }
736 
737     AutoLock auto_lock_no_wait(&FdManager::cache_cleanup_lock, AutoLock::NO_WAIT);
738 
739     if(auto_lock_no_wait.isLockAcquired()){
740         //S3FS_PRN_DBG("cache cleanup started");
741         CleanupCacheDirInternal("");
742         //S3FS_PRN_DBG("cache cleanup ended");
743     }else{
744         // wait for other thread to finish cache cleanup
745         AutoLock auto_lock(&FdManager::cache_cleanup_lock);
746     }
747 }
748 
CleanupCacheDirInternal(const std::string & path)749 void FdManager::CleanupCacheDirInternal(const std::string &path)
750 {
751     DIR*           dp;
752     struct dirent* dent;
753     std::string    abs_path = cache_dir + "/" + bucket + path;
754 
755     if(NULL == (dp = opendir(abs_path.c_str()))){
756         S3FS_PRN_ERR("could not open cache dir(%s) - errno(%d)", abs_path.c_str(), errno);
757         return;
758     }
759 
760     for(dent = readdir(dp); dent; dent = readdir(dp)){
761         if(0 == strcmp(dent->d_name, "..") || 0 == strcmp(dent->d_name, ".")){
762             continue;
763         }
764         std::string fullpath = abs_path;
765         fullpath         += "/";
766         fullpath         += dent->d_name;
767         struct stat st;
768         if(0 != lstat(fullpath.c_str(), &st)){
769             S3FS_PRN_ERR("could not get stats of file(%s) - errno(%d)", fullpath.c_str(), errno);
770             closedir(dp);
771             return;
772         }
773         std::string next_path = path + "/" + dent->d_name;
774         if(S_ISDIR(st.st_mode)){
775             CleanupCacheDirInternal(next_path);
776         }else{
777             AutoLock auto_lock(&FdManager::fd_manager_lock, AutoLock::NO_WAIT);
778             if (!auto_lock.isLockAcquired()) {
779                 S3FS_PRN_ERR("could not get fd_manager_lock when clean up file(%s)", next_path.c_str());
780                 continue;
781             }
782             fdent_map_t::iterator iter = fent.find(next_path);
783             if(fent.end() == iter) {
784                 S3FS_PRN_DBG("cleaned up: %s", next_path.c_str());
785                 FdManager::DeleteCacheFile(next_path.c_str());
786             }
787         }
788     }
789     closedir(dp);
790 }
791 
ReserveDiskSpace(off_t size)792 bool FdManager::ReserveDiskSpace(off_t size)
793 {
794     if(IsSafeDiskSpace(NULL, size)){
795         AutoLock auto_lock(&FdManager::reserved_diskspace_lock);
796         free_disk_space += size;
797         return true;
798     }
799     return false;
800 }
801 
FreeReservedDiskSpace(off_t size)802 void FdManager::FreeReservedDiskSpace(off_t size)
803 {
804     AutoLock auto_lock(&FdManager::reserved_diskspace_lock);
805     free_disk_space -= size;
806 }
807 
808 //
809 // Inspect all files for stats file for cache file
810 //
811 // [NOTE]
812 // The minimum sub_path parameter is "/".
813 // The sub_path is a directory path starting from "/" and ending with "/".
814 //
815 // This method produces the following output.
816 //
817 // * Header
818 //    ------------------------------------------------------------
819 //    Check cache file and its stats file consistency
820 //    ------------------------------------------------------------
821 // * When the cache file and its stats information match
822 //    File path: <file path> -> [OK] no problem
823 //
824 // * If there is a problem with the cache file and its stats information
825 //    File path: <file path>
826 //      -> [P] <If the problem is that parsing is not possible in the first place, the message is output here with this prefix.>
827 //      -> [E] there is a mark that data exists in stats, but there is no data in the cache file.
828 //             <offset address>(bytes)
829 //                 ...
830 //                 ...
831 //      -> [W] These show no data in stats, but there is evidence of data in the cache file.(no problem.)
832 //             <offset address>(bytes)
833 //                 ...
834 //                 ...
835 //
RawCheckAllCache(FILE * fp,const char * cache_stat_top_dir,const char * sub_path,int & total_file_cnt,int & err_file_cnt,int & err_dir_cnt)836 bool FdManager::RawCheckAllCache(FILE* fp, const char* cache_stat_top_dir, const char* sub_path, int& total_file_cnt, int& err_file_cnt, int& err_dir_cnt)
837 {
838     if(!cache_stat_top_dir || '\0' == cache_stat_top_dir[0] || !sub_path || '\0' == sub_path[0]){
839         S3FS_PRN_ERR("Parameter cache_stat_top_dir is empty.");
840         return false;
841     }
842 
843     // open directory of cache file's stats
844     DIR*   statsdir;
845     std::string target_dir = cache_stat_top_dir;
846     target_dir       += sub_path;
847     if(NULL == (statsdir = opendir(target_dir.c_str()))){
848         S3FS_PRN_ERR("Could not open directory(%s) by errno(%d)", target_dir.c_str(), errno);
849         return false;
850     }
851 
852     // loop in directory of cache file's stats
853     struct dirent* pdirent = NULL;
854     while(NULL != (pdirent = readdir(statsdir))){
855         if(DT_DIR == pdirent->d_type){
856             // found directory
857             if(0 == strcmp(pdirent->d_name, ".") || 0 == strcmp(pdirent->d_name, "..")){
858                 continue;
859             }
860 
861             // reentrant for sub directory
862             std::string subdir_path = sub_path;
863             subdir_path       += pdirent->d_name;
864             subdir_path       += '/';
865             if(!RawCheckAllCache(fp, cache_stat_top_dir, subdir_path.c_str(), total_file_cnt, err_file_cnt, err_dir_cnt)){
866                 // put error message for this dir.
867                 ++err_dir_cnt;
868                 S3FS_PRN_CACHE(fp, CACHEDBG_FMT_DIR_PROB, subdir_path.c_str());
869                 S3FS_PRN_CACHE(fp, CACHEDBG_FMT_CRIT_HEAD, "Something error is occurred in checking this directory");
870             }
871 
872         }else{
873             ++total_file_cnt;
874 
875             // make cache file path
876             std::string strOpenedWarn;
877             std::string cache_path;
878             std::string object_file_path = sub_path;
879             object_file_path       += pdirent->d_name;
880             if(!FdManager::MakeCachePath(object_file_path.c_str(), cache_path, false, false) || cache_path.empty()){
881                 ++err_file_cnt;
882                 S3FS_PRN_CACHE(fp, CACHEDBG_FMT_FILE_PROB, object_file_path.c_str(), strOpenedWarn.c_str());
883                 S3FS_PRN_CACHE(fp, CACHEDBG_FMT_CRIT_HEAD, "Could not make cache file path");
884                 continue;
885             }
886 
887             // check if the target file is currently in operation.
888             {
889                 AutoLock auto_lock(&FdManager::fd_manager_lock);
890 
891                 fdent_map_t::iterator iter = fent.find(object_file_path);
892                 if(fent.end() != iter){
893                     // This file is opened now, then we need to put warning message.
894                     strOpenedWarn = CACHEDBG_FMT_WARN_OPEN;
895                 }
896             }
897 
898             // open cache file
899             int cache_file_fd;
900             if(-1 == (cache_file_fd = open(cache_path.c_str(), O_RDONLY))){
901                 ++err_file_cnt;
902                 S3FS_PRN_CACHE(fp, CACHEDBG_FMT_FILE_PROB, object_file_path.c_str(), strOpenedWarn.c_str());
903                 S3FS_PRN_CACHE(fp, CACHEDBG_FMT_CRIT_HEAD, "Could not open cache file");
904                 continue;
905             }
906 
907             // get inode number for cache file
908             struct stat st;
909             if(0 != fstat(cache_file_fd, &st)){
910                 ++err_file_cnt;
911                 S3FS_PRN_CACHE(fp, CACHEDBG_FMT_FILE_PROB, object_file_path.c_str(), strOpenedWarn.c_str());
912                 S3FS_PRN_CACHE(fp, CACHEDBG_FMT_CRIT_HEAD, "Could not get file inode number for cache file");
913 
914                 close(cache_file_fd);
915                 continue;
916             }
917             ino_t cache_file_inode = st.st_ino;
918 
919             // open cache stat file and load page info.
920             PageList      pagelist;
921             CacheFileStat cfstat(object_file_path.c_str());
922             if(!cfstat.ReadOnlyOpen() || !pagelist.Serialize(cfstat, false, cache_file_inode)){
923                 ++err_file_cnt;
924                 S3FS_PRN_CACHE(fp, CACHEDBG_FMT_FILE_PROB, object_file_path.c_str(), strOpenedWarn.c_str());
925                 S3FS_PRN_CACHE(fp, CACHEDBG_FMT_CRIT_HEAD, "Could not load cache file stats information");
926 
927                 close(cache_file_fd);
928                 continue;
929             }
930             cfstat.Release();
931 
932             // compare cache file size and stats information
933             if(st.st_size != pagelist.Size()){
934                 ++err_file_cnt;
935                 S3FS_PRN_CACHE(fp, CACHEDBG_FMT_FILE_PROB, object_file_path.c_str(), strOpenedWarn.c_str());
936                 S3FS_PRN_CACHE(fp, CACHEDBG_FMT_CRIT_HEAD2 "The cache file size(%lld) and the value(%lld) from cache file stats are different", static_cast<long long int>(st.st_size), static_cast<long long int>(pagelist.Size()));
937 
938                 close(cache_file_fd);
939                 continue;
940             }
941 
942             // compare cache file stats and cache file blocks
943             fdpage_list_t err_area_list;
944             fdpage_list_t warn_area_list;
945             if(!pagelist.CompareSparseFile(cache_file_fd, st.st_size, err_area_list, warn_area_list)){
946                 // Found some error or warning
947                 S3FS_PRN_CACHE(fp, CACHEDBG_FMT_FILE_PROB, object_file_path.c_str(), strOpenedWarn.c_str());
948                 if(!warn_area_list.empty()){
949                     S3FS_PRN_CACHE(fp, CACHEDBG_FMT_WARN_HEAD);
950                     for(fdpage_list_t::const_iterator witer = warn_area_list.begin(); witer != warn_area_list.end(); ++witer){
951                         S3FS_PRN_CACHE(fp, CACHEDBG_FMT_PROB_BLOCK, static_cast<size_t>(witer->offset), static_cast<size_t>(witer->bytes));
952                     }
953                 }
954                 if(!err_area_list.empty()){
955                     ++err_file_cnt;
956                     S3FS_PRN_CACHE(fp, CACHEDBG_FMT_ERR_HEAD);
957                     for(fdpage_list_t::const_iterator eiter = err_area_list.begin(); eiter != err_area_list.end(); ++eiter){
958                         S3FS_PRN_CACHE(fp, CACHEDBG_FMT_PROB_BLOCK, static_cast<size_t>(eiter->offset), static_cast<size_t>(eiter->bytes));
959                     }
960                 }
961             }else{
962                 // There is no problem!
963                 if(!strOpenedWarn.empty()){
964                     strOpenedWarn += "\n ";
965                 }
966                 S3FS_PRN_CACHE(fp, CACHEDBG_FMT_FILE_OK, object_file_path.c_str(), strOpenedWarn.c_str());
967             }
968             err_area_list.clear();
969             warn_area_list.clear();
970             close(cache_file_fd);
971         }
972     }
973     closedir(statsdir);
974 
975     return true;
976 }
977 
CheckAllCache()978 bool FdManager::CheckAllCache()
979 {
980     if(!FdManager::HaveLseekHole()){
981         S3FS_PRN_ERR("lseek does not support SEEK_DATA/SEEK_HOLE, then could not check cache.");
982         return false;
983     }
984 
985     FILE* fp;
986     if(FdManager::check_cache_output.empty()){
987         fp = stdout;
988     }else{
989         if(NULL == (fp = fopen(FdManager::check_cache_output.c_str(), "a+"))){
990             S3FS_PRN_ERR("Could not open(create) output file(%s) for checking all cache by errno(%d)", FdManager::check_cache_output.c_str(), errno);
991             return false;
992         }
993     }
994 
995     // print head message
996     S3FS_PRN_CACHE(fp, CACHEDBG_FMT_HEAD, S3fsLog::GetCurrentTime().c_str());
997 
998     // Loop in directory of cache file's stats
999     std::string top_path  = CacheFileStat::GetCacheFileStatTopDir();
1000     int    total_file_cnt = 0;
1001     int    err_file_cnt   = 0;
1002     int    err_dir_cnt    = 0;
1003     bool   result         = RawCheckAllCache(fp, top_path.c_str(), "/", total_file_cnt, err_file_cnt, err_dir_cnt);
1004     if(!result){
1005         S3FS_PRN_ERR("Processing failed due to some problem.");
1006     }
1007 
1008     // print foot message
1009     S3FS_PRN_CACHE(fp, CACHEDBG_FMT_FOOT, total_file_cnt, err_file_cnt, err_dir_cnt);
1010 
1011     if(stdout != fp){
1012         fclose(fp);
1013     }
1014 
1015     return result;
1016 }
1017 
1018 /*
1019 * Local variables:
1020 * tab-width: 4
1021 * c-basic-offset: 4
1022 * End:
1023 * vim600: expandtab sw=4 ts=4 fdm=marker
1024 * vim<600: expandtab sw=4 ts=4
1025 */
1026