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 <unistd.h>
25 #include <sys/file.h>
26 
27 #include "common.h"
28 #include "s3fs.h"
29 #include "fdcache_stat.h"
30 #include "fdcache.h"
31 #include "s3fs_util.h"
32 #include "string_util.h"
33 
34 //------------------------------------------------
35 // CacheFileStat class methods
36 //------------------------------------------------
GetCacheFileStatTopDir()37 std::string CacheFileStat::GetCacheFileStatTopDir()
38 {
39     std::string top_path;
40     if(!FdManager::IsCacheDir() || bucket.empty()){
41         return top_path;
42     }
43 
44     // stat top dir( "/<cache_dir>/.<bucket_name>.stat" )
45     top_path += FdManager::GetCacheDir();
46     top_path += "/.";
47     top_path += bucket;
48     top_path += ".stat";
49     return top_path;
50 }
51 
MakeCacheFileStatPath(const char * path,std::string & sfile_path,bool is_create_dir)52 bool CacheFileStat::MakeCacheFileStatPath(const char* path, std::string& sfile_path, bool is_create_dir)
53 {
54     std::string top_path = CacheFileStat::GetCacheFileStatTopDir();
55     if(top_path.empty()){
56         S3FS_PRN_ERR("The path to cache top dir is empty.");
57         return false;
58     }
59 
60     if(is_create_dir){
61       int result;
62       if(0 != (result = mkdirp(top_path + mydirname(path), 0777))){
63           S3FS_PRN_ERR("failed to create dir(%s) by errno(%d).", path, result);
64           return false;
65       }
66     }
67     if(!path || '\0' == path[0]){
68         sfile_path = top_path;
69     }else{
70         sfile_path = top_path + SAFESTRPTR(path);
71     }
72     return true;
73 }
74 
CheckCacheFileStatTopDir()75 bool CacheFileStat::CheckCacheFileStatTopDir()
76 {
77     std::string top_path = CacheFileStat::GetCacheFileStatTopDir();
78     if(top_path.empty()){
79         S3FS_PRN_INFO("The path to cache top dir is empty, thus not need to check permission.");
80         return true;
81     }
82 
83     return check_exist_dir_permission(top_path.c_str());
84 }
85 
DeleteCacheFileStat(const char * path)86 bool CacheFileStat::DeleteCacheFileStat(const char* path)
87 {
88     if(!path || '\0' == path[0]){
89         return false;
90     }
91     // stat path
92     std::string sfile_path;
93     if(!CacheFileStat::MakeCacheFileStatPath(path, sfile_path, false)){
94         S3FS_PRN_ERR("failed to create cache stat file path(%s)", path);
95         return false;
96     }
97     if(0 != unlink(sfile_path.c_str())){
98         if(ENOENT == errno){
99             S3FS_PRN_DBG("failed to delete file(%s): errno=%d", path, errno);
100         }else{
101             S3FS_PRN_ERR("failed to delete file(%s): errno=%d", path, errno);
102         }
103         return false;
104     }
105     return true;
106 }
107 
108 // [NOTE]
109 // If remove stat file directory, it should do before removing
110 // file cache directory.
111 //
DeleteCacheFileStatDirectory()112 bool CacheFileStat::DeleteCacheFileStatDirectory()
113 {
114     std::string top_path = CacheFileStat::GetCacheFileStatTopDir();
115     if(top_path.empty()){
116         S3FS_PRN_INFO("The path to cache top dir is empty, thus not need to remove it.");
117         return true;
118     }
119     return delete_files_in_dir(top_path.c_str(), true);
120 }
121 
RenameCacheFileStat(const char * oldpath,const char * newpath)122 bool CacheFileStat::RenameCacheFileStat(const char* oldpath, const char* newpath)
123 {
124     if(!oldpath || '\0' == oldpath[0] || !newpath || '\0' == newpath[0]){
125         return false;
126     }
127 
128     // stat path
129     std::string old_filestat;
130     std::string new_filestat;
131     if(!CacheFileStat::MakeCacheFileStatPath(oldpath, old_filestat, false) || !CacheFileStat::MakeCacheFileStatPath(newpath, new_filestat, false)){
132         return false;
133     }
134 
135     // check new stat path
136     struct stat st;
137     if(0 == stat(new_filestat.c_str(), &st)){
138         // new stat path is existed, then unlink it.
139         if(-1 == unlink(new_filestat.c_str())){
140             S3FS_PRN_ERR("failed to unlink new cache file stat path(%s) by errno(%d).", new_filestat.c_str(), errno);
141             return false;
142         }
143     }
144 
145     // check old stat path
146     if(0 != stat(old_filestat.c_str(), &st)){
147         // old stat path is not existed, then nothing to do any more.
148         return true;
149     }
150 
151     // link and unlink
152     if(-1 == link(old_filestat.c_str(), new_filestat.c_str())){
153         S3FS_PRN_ERR("failed to link old cache file stat path(%s) to new cache file stat path(%s) by errno(%d).", old_filestat.c_str(), new_filestat.c_str(), errno);
154         return false;
155     }
156     if(-1 == unlink(old_filestat.c_str())){
157         S3FS_PRN_ERR("failed to unlink old cache file stat path(%s) by errno(%d).", old_filestat.c_str(), errno);
158         return false;
159     }
160    return true;
161 }
162 
163 //------------------------------------------------
164 // CacheFileStat methods
165 //------------------------------------------------
CacheFileStat(const char * tpath)166 CacheFileStat::CacheFileStat(const char* tpath) : fd(-1)
167 {
168     if(tpath && '\0' != tpath[0]){
169         SetPath(tpath, true);
170     }
171 }
172 
~CacheFileStat()173 CacheFileStat::~CacheFileStat()
174 {
175     Release();
176 }
177 
SetPath(const char * tpath,bool is_open)178 bool CacheFileStat::SetPath(const char* tpath, bool is_open)
179 {
180     if(!tpath || '\0' == tpath[0]){
181         return false;
182     }
183     if(!Release()){
184         // could not close old stat file.
185         return false;
186     }
187     path = tpath;
188     if(!is_open){
189         return true;
190     }
191     return Open();
192 }
193 
RawOpen(bool readonly)194 bool CacheFileStat::RawOpen(bool readonly)
195 {
196     if(path.empty()){
197         return false;
198     }
199     if(-1 != fd){
200         // already opened
201         return true;
202     }
203     // stat path
204     std::string sfile_path;
205     if(!CacheFileStat::MakeCacheFileStatPath(path.c_str(), sfile_path, true)){
206         S3FS_PRN_ERR("failed to create cache stat file path(%s)", path.c_str());
207         return false;
208     }
209     // open
210     if(readonly){
211         if(-1 == (fd = open(sfile_path.c_str(), O_RDONLY))){
212             S3FS_PRN_ERR("failed to read only open cache stat file path(%s) - errno(%d)", path.c_str(), errno);
213             return false;
214         }
215     }else{
216         if(-1 == (fd = open(sfile_path.c_str(), O_CREAT|O_RDWR, 0600))){
217             S3FS_PRN_ERR("failed to open cache stat file path(%s) - errno(%d)", path.c_str(), errno);
218             return false;
219         }
220     }
221     // lock
222     if(-1 == flock(fd, LOCK_EX)){
223         S3FS_PRN_ERR("failed to lock cache stat file(%s) - errno(%d)", path.c_str(), errno);
224         close(fd);
225         fd = -1;
226         return false;
227     }
228     // seek top
229     if(0 != lseek(fd, 0, SEEK_SET)){
230         S3FS_PRN_ERR("failed to lseek cache stat file(%s) - errno(%d)", path.c_str(), errno);
231         flock(fd, LOCK_UN);
232         close(fd);
233         fd = -1;
234         return false;
235     }
236     S3FS_PRN_DBG("file locked(%s - %s)", path.c_str(), sfile_path.c_str());
237 
238     return true;
239 }
240 
Open()241 bool CacheFileStat::Open()
242 {
243     return RawOpen(false);
244 }
245 
ReadOnlyOpen()246 bool CacheFileStat::ReadOnlyOpen()
247 {
248     return RawOpen(true);
249 }
250 
Release()251 bool CacheFileStat::Release()
252 {
253     if(-1 == fd){
254         // already release
255         return true;
256     }
257     // unlock
258     if(-1 == flock(fd, LOCK_UN)){
259         S3FS_PRN_ERR("failed to unlock cache stat file(%s) - errno(%d)", path.c_str(), errno);
260         return false;
261     }
262     S3FS_PRN_DBG("file unlocked(%s)", path.c_str());
263 
264     if(-1 == close(fd)){
265         S3FS_PRN_ERR("failed to close cache stat file(%s) - errno(%d)", path.c_str(), errno);
266         return false;
267     }
268     fd = -1;
269 
270     return true;
271 }
272 
273 /*
274 * Local variables:
275 * tab-width: 4
276 * c-basic-offset: 4
277 * End:
278 * vim600: expandtab sw=4 ts=4 fdm=marker
279 * vim<600: expandtab sw=4 ts=4
280 */
281