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