1 // Ryzom - MMORPG Framework <http://dev.ryzom.com/projects/ryzom/>
2 // Copyright (C) 2010 Winch Gate Property Limited
4 // This program is free software: you can redistribute it and/or modify
5 // it under the terms of the GNU Affero General Public License as
6 // published by the Free Software Foundation, either version 3 of the
7 // License, or (at your option) any later version.
9 // This program is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 // GNU Affero General Public License for more details.
14 // You should have received a copy of the GNU Affero General Public License
15 // along with this program. If not, see <http://www.gnu.org/licenses/>.
17 #include "backup_file_access.h"
19 #include "nel/misc/path.h"
20 #include "nel/misc/mem_stream.h"
21 #include "nel/misc/file.h"
22 #include "nel/net/service.h"
24 #include "game_share/backup_service_messages.h"
25 #include "server_share/backup_service_itf.h"
27 #include "backup_service.h"
29 using namespace NLNET
;
32 /* These variables are deprecated. Now the BS only deals with paths relative to SaveShardRoot.
33 NLMISC::CVariable<std::string> BSFilePrefix("backup", "BSFilePrefix", "file read/write prefix", "", 0, true);
34 NLMISC::CVariable<std::string> BSFileSubst("backup", "BSFileSubst", "file read/write substitute part", "", 0, true);
36 NLMISC::CVariable
<bool> VerboseLog("backup", "VerboseLog", "Activate verbose logging of BS activity", false);
37 NLMISC::CVariable
<bool> UseTempFile("backup", "UseTempFile", "Flag the use of temporary file for safe write or append operation", true, true);
39 extern NLMISC::CVariable
<std::string
> SaveShardRootGameShare
;
41 bool bsstrincmp(const char* s1
, const char* s2
, int n
)
44 for (nn
= (int)n
; nn
>0 && *s1
!= '\0' && *s2
!= '\0' && tolower(*s1
) == tolower(*s2
); --nn
, ++s1
, ++s2
)
46 return nn
== 0 || (*s1
== *s2
);
49 std::string
getBackupFileName(const std::string
& filename
)
51 return SaveShardRootGameShare
.get() + filename
;
52 /* // BSFilePrefix and BSFileSubst are deprecated
53 if (BSFilePrefix.get().empty())
56 if (!BSFileSubst.get().empty() && bsstrincmp(BSFileSubst.get().c_str(), filename.c_str(), BSFileSubst.get().size()))
57 return BSFilePrefix.get() + filename.substr(BSFileSubst.get().size());
59 return BSFilePrefix.get() + filename;
65 void CFileAccessManager::init()
72 // Add an access to perform to stack of accesses
73 void CFileAccessManager::stackFileAccess(IFileAccess
* access
)
75 _Accesses
.push_back(access
);
78 // Flushes file accesses
79 void CFileAccessManager::update()
81 // while we are in normal mode, and there are still accesses to perform
82 while (_Mode
== Normal
&& !_Accesses
.empty())
85 IFileAccess
* access
= _Accesses
.front();
86 IFileAccess::TReturnCode rc
= access
->execute(*this);
88 if (rc
== IFileAccess::MajorFailure
)
90 nlwarning("Failed to execute access to file '%s', setting to STALLED mode", access
->Filename
.c_str());
91 setMode(Stalled
, access
->FailureReason
);
95 if (rc
== IFileAccess::MinorFailure
)
97 nlwarning("Minor failure in access to file '%s', access is discarded yet.", access
->Filename
.c_str());
100 _Accesses
.pop_front();
106 // Release File manager
107 void CFileAccessManager::release()
109 if (!_Accesses
.empty())
112 if (!_Accesses
.empty())
114 nlwarning("WARNING: release asked as file manager still contains pending accesses!");
115 nlwarning("DATA WILL BE LOST!!");
121 // Force manager mode
122 void CFileAccessManager::setMode(TMode mode
, const std::string
& reason
)
130 NLNET::CMessage
msgout("STALL_MODE");
131 bool stalled
= (_Mode
!= Normal
);
132 std::string reasonstr
= _Reason
;
134 msgout
.serial(stalled
, reasonstr
);
135 NLNET::CUnifiedNetwork::getInstance()->send("EGS", msgout
);
137 if (_Mode
== Stalled
)
138 IService::getInstance()->setCurrentStatus("Stalled");
140 IService::getInstance()->clearCurrentStatus("Stalled");
146 // Notify service connection
147 void CFileAccessManager::notifyServiceConnection(NLNET::TServiceId serviceId
, const std::string
& serviceName
)
149 if (serviceName
== "EGS" && _Mode
== Stalled
)
151 NLNET::CMessage
msgout("STALL_MODE");
153 std::string reason
= _Reason
;
155 msgout
.serial(stalled
, reason
);
156 NLNET::CUnifiedNetwork::getInstance()->send(serviceId
, msgout
);
162 // display stacked accesses
163 void CFileAccessManager::displayFileAccesses(NLMISC::CLog
& log
)
166 for (i
=0; i
<_Accesses
.size(); ++i
)
168 log
.displayRawNL("%2d %08p %s %s %s", i
, _Accesses
[i
], _Accesses
[i
]->Requester
.toString().c_str(), _Accesses
[i
]->Filename
.c_str(), _Accesses
[i
]->FailureReason
.c_str());
173 // Remove a file access
174 void CFileAccessManager::removeFileAccess(IFileAccess
* access
)
176 std::deque
<IFileAccess
*>::iterator it
;
177 for (it
=_Accesses
.begin(); it
!=_Accesses
.end(); ++it
)
190 IFileAccess::TReturnCode
CLoadFile::execute(CFileAccessManager
& manager
)
192 bool fileExists
= NLMISC::CFile::fileExists(getBackupFileName(Filename
));
194 if (!fileExists
&& checkFailureMode(MajorFailureIfFileNotExists
))
196 FailureReason
= NLMISC::toString("MAJOR_FAILURE:LOAD: file '%s' doesn't not exist", Filename
.c_str());
200 CBackupMsgReceiveFile outMsg
;
201 outMsg
.RequestId
= RequestId
;
204 bool fileOpen
= (fileExists
&& f
.open(getBackupFileName(Filename
)));
205 bool fileRead
= false;
209 outMsg
.FileDescription
.set(getBackupFileName(Filename
));
211 // restore filename with the provided one for the response
212 outMsg
.FileDescription
.FileName
= Filename
;
216 H_AUTO(LoadFileSerial
);
217 outMsg
.Data
.invert();
218 f
.serialBuffer( outMsg
.Data
.bufferToFill(outMsg
.FileDescription
.FileSize
), outMsg
.FileDescription
.FileSize
);
219 outMsg
.Data
.invert();
223 catch(const NLMISC::Exception
&)
228 if (!fileRead
&& checkFailureMode(MajorFailureIfFileUnreaddable
))
230 FailureReason
= NLMISC::toString("MAJOR_FAILURE:LOAD: can't %s file '%s'", (!fileOpen
? "open" : "read"), Filename
.c_str());
234 // outMsg.send(Requester);
235 switch (Requester
.RequesterType
)
237 case TRequester::rt_service
:
239 H_AUTO(CBackupMsgReceiveFile1
);
240 NLNET::CMessage
msgOut("bs_file");
241 outMsg
.serial(msgOut
);
242 NLNET::CUnifiedNetwork::getInstance()->send( Requester
.ServiceId
, msgOut
);
245 case TRequester::rt_layer3
:
247 NLNET::CMessage
msgOut("bs_file");
248 outMsg
.serial(msgOut
);
249 Requester
.Netbase
->send(msgOut
, Requester
.From
);
252 case TRequester::rt_module
:
254 BS::CBackupServiceClientProxy
bsc(Requester
.ModuleProxy
);
256 bsc
.loadFileResult(CBackupService::getInstance()->getBackupModule(),
258 outMsg
.FileDescription
.FileName
,
259 outMsg
.FileDescription
.FileTimeStamp
,
260 NLNET::TBinBuffer(outMsg
.Data
.buffer(), outMsg
.Data
.length()));
264 // If the file read failed for any other reason than file not found then complain
265 if (!fileRead
&& fileExists
)
267 FailureReason
= NLMISC::toString("MINOR_FAILURE:LOAD: can't %s file '%s'", (!fileOpen
? "open" : "read"), Filename
.c_str());
273 // We can assume that this is the only case where the file read failed that hasn't already been treated above
276 nldebug("Load file Failed (but MajorFailureIfFileUnreaddable==false): file '%s' doesn't not exist", Filename
.c_str());
280 nlinfo("Loaded file '%s'", Filename
.c_str());
289 CWriteFile::CWriteFile(const std::string
& filename
, const TRequester
&requester
, uint32 requestid
, NLMISC::CMemStream
& data
)
290 : IFileAccess(filename
, requester
, requestid
)
295 FailureMode
= MajorFailureMode
;
296 uint32 startPos
= (uint32
)data
.getPos();
297 uint32 actualLen
= data
.length()-startPos
;
298 Data
.resize(actualLen
);
299 memcpy(&(Data
[0]), data
.buffer()+startPos
, actualLen
);
302 CWriteFile::CWriteFile(const std::string
& filename
, const TRequester
&requester
, uint32 requestid
, uint8
* data
, uint dataSize
)
303 : IFileAccess(filename
, requester
, requestid
)
308 FailureMode
= MajorFailureMode
;
309 Data
.resize(dataSize
);
310 memcpy(&(Data
[0]), data
, dataSize
);
313 IFileAccess::TReturnCode
CWriteFile::execute(CFileAccessManager
& manager
)
315 bool fileExists
= NLMISC::CFile::fileExists(getBackupFileName(Filename
));
319 if (checkFailureMode(MajorFailureIfFileNotExists
))
321 FailureReason
= NLMISC::toString("MAJOR_FAILURE:WRITE: file '%s' does not exist", Filename
.c_str());
324 if (checkFailureMode(MinorFailureIfFileNotExists
))
326 FailureReason
= NLMISC::toString("MINOR_FAILURE:WRITE: file '%s' does not exist", Filename
.c_str());
332 if (checkFailureMode(MajorFailureIfFileExists
))
334 FailureReason
= NLMISC::toString("MAJOR_FAILURE:WRITE: file '%s' already exists", Filename
.c_str());
337 if (checkFailureMode(MinorFailureIfFileExists
))
339 FailureReason
= NLMISC::toString("MINOR_FAILURE:WRITE: file '%s' already exists", Filename
.c_str());
346 std::string dir
= NLMISC::CFile::getPath(getBackupFileName(Filename
));
348 if (!NLMISC::CFile::isExists(dir
))
350 if (!NLMISC::CFile::createDirectoryTree(dir
) ||
351 !NLMISC::CFile::setRWAccess(dir
))
353 if (checkFailureMode(MajorFailureIfFileUnwritable
))
355 FailureReason
= NLMISC::toString("MAJOR_FAILURE:WRITE: can't open file '%s', failed to create directory", Filename
.c_str());
358 FailureReason
= NLMISC::toString("MINOR_FAILURE:WRITE: can't open file '%s', failed to create directory", Filename
.c_str());
363 nlinfo("Created directory tree '%s'", dir
.c_str());
367 // if can't open file, file is unwritable, failure in all cases (and no backup)
368 // file is kept untouched
370 bool fileOpen
= f
.open(getBackupFileName(Filename
), Append
, false, UseTempFile
);
373 if (checkFailureMode(MajorFailureIfFileUnwritable
))
375 FailureReason
= NLMISC::toString("MAJOR_FAILURE:WRITE: can't open file '%s'", Filename
.c_str());
378 FailureReason
= NLMISC::toString("MINOR_FAILURE:WRITE: can't open file '%s'", Filename
.c_str());
382 bool fileSaved
= false;
386 f
.serialBuffer(&(Data
[0]), (uint
)Data
.size());
390 nlinfo("%s %u octets to file '%s'", Append
? "Append" : "Save", Data
.size(), Filename
.c_str());
392 catch(const NLMISC::Exception
&)
396 // if can't write in file, file is unwritable, failure in all cases (and no backup)
397 // file is kept untouched
398 if (!fileSaved
&& checkFailureMode(MajorFailureIfFileUnwritable
))
400 if (checkFailureMode(MajorFailureIfFileUnwritable
))
402 FailureReason
= NLMISC::toString("MAJOR_FAILURE:WRITE: can't write file '%s'", Filename
.c_str());
405 FailureReason
= NLMISC::toString("MINOR_FAILURE:WRITE: can't write file '%s'", Filename
.c_str());
410 // -> if it is a major issue, don't write file
411 // -> else write file anyway
412 bool fileBackuped
= true;
413 if (fileExists
&& BackupFile
)
415 if (!NLMISC::CFile::copyFile( getBackupFileName(Filename
)+".backup", getBackupFileName(Filename
)))
417 fileBackuped
= false;
418 if (checkFailureMode(MajorFailureIfFileUnbackupable
))
420 FailureReason
= NLMISC::toString("MAJOR_FAILURE:WRITE: can't backup file '%s'", Filename
.c_str());
430 FailureReason
= NLMISC::toString("MINOR_FAILURE:WRITE: can't backup file '%s'", Filename
.c_str());
435 std::string str
= getBackupFileName(Filename
)+"\n";
436 if(str
.find("characters")!=std::string::npos
)
438 flog
.open(getBackupFileName("new_save.txt"), true);
439 flog
.serialBuffer((uint8
*)&(str
[0]), str
.size());
447 IFileAccess::TReturnCode
CDeleteFile::execute(CFileAccessManager
& manager
)
449 bool fileExists
= NLMISC::CFile::fileExists(getBackupFileName(Filename
));
453 if (checkFailureMode(MajorFailureIfFileNotExists
))
455 FailureReason
= NLMISC::toString("MAJOR_FAILURE:DELETE: file '%s' does not exist", Filename
.c_str());
458 if (checkFailureMode(MinorFailureIfFileNotExists
))
460 FailureReason
= NLMISC::toString("MINOR_FAILURE:DELETE: file '%s' does not exist", Filename
.c_str());
468 bool fileBackuped
= false;
471 std::string path
= NLMISC::CFile::getPath(getBackupFileName(Filename
));
472 std::string file
= NLMISC::CFile::getFilename(Filename
);
477 backup
= NLMISC::toString("%sdelete.%04u.%s", path
.c_str(), i
++, file
.c_str());
479 while (i
<= 10000 && NLMISC::CFile::fileExists(backup
));
481 fileBackuped
= (i
<= 10000 && NLMISC::CFile::moveFile(backup
, getBackupFileName(Filename
)));
489 if (checkFailureMode(MajorFailureIfFileUnbackupable
))
491 FailureReason
= NLMISC::toString("MAJOR_FAILURE:DELETE: can't backup file '%s'", Filename
.c_str());
494 FailureReason
= NLMISC::toString("MINOR_FAILURE:DELETE: can't backup file '%s'", Filename
.c_str());
500 if (!NLMISC::CFile::deleteFile(getBackupFileName(Filename
)))
502 if (checkFailureMode(MajorFailureIfFileUnDeletable
))
504 FailureReason
= NLMISC::toString("MAJOR_FAILURE:DELETE: can't delete file '%s'", Filename
.c_str());
507 FailureReason
= NLMISC::toString("MINOR_FAILURE:DELETE: can't delete file '%s'", Filename
.c_str());
513 nlinfo("Deleted file '%s'", Filename
.c_str());