llfilesystem.cpp 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407
  1. /**
  2. * @file llfilesystem.cpp
  3. * @brief Implementation of the local file system.
  4. *
  5. * $LicenseInfo:firstyear=2002&license=viewergpl$
  6. *
  7. * Copyright (c) 2020, Linden Research, Inc. (c) 2021 Henri Beauchamp.
  8. *
  9. * Modifications by Henri Beauchamp:
  10. * - Pointless per-asset-type file naming removed.
  11. * - Cached filename for faster operations.
  12. * - Use of faster LLFile operations where possible.
  13. * - Fixed various bugs in write operations. Removed the pointless READ_WRITE
  14. * mode, added the OVERWRITE one, and changed seek() to auto-padding files
  15. * with zeros in WRITE mode when seeking past the end of an existing file.
  16. * - Real time tracking of bytes added to/removed from cache.
  17. * - Proper cache validity verification.
  18. * - Immediate date-stamping on creation of LLFileSystem instances, to prevent
  19. * potential race conditions with the threaded cache purging mechanism.
  20. * - Multiple threads and multiple viewer instances deconfliction.
  21. * - Added LLFile::sFlushOnWrite to work around a bug in Wine (*) which
  22. * reports a wrong file position after non flushed writes. (*) This is for
  23. * people perverted enough to run a Windows build under Wine under Linux
  24. * instead of a Linux native build: yes, I'm perverted since I do it to test
  25. * Windows builds under Linux... :-P
  26. *
  27. * Second Life Viewer Source Code
  28. * The source code in this file ("Source Code") is provided by Linden Lab
  29. * to you under the terms of the GNU General Public License, version 2.0
  30. * ("GPL"), unless you have obtained a separate licensing agreement
  31. * ("Other License"), formally executed by you and Linden Lab. Terms of
  32. * the GPL can be found in doc/GPL-license.txt in this distribution, or
  33. * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
  34. *
  35. * There are special exceptions to the terms and conditions of the GPL as
  36. * it is applied to this Source Code. View the full text of the exception
  37. * in the file doc/FLOSS-exception.txt in this software distribution, or
  38. * online at
  39. * http://secondlifegrid.net/programs/open_source/licensing/flossexception
  40. *
  41. * By copying, modifying or distributing this software, you acknowledge
  42. * that you have read and understood your obligations described above,
  43. * and agree to abide by those obligations.
  44. *
  45. * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
  46. * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
  47. * COMPLETENESS OR PERFORMANCE.
  48. * $/LicenseInfo$
  49. */
  50. #include "linden_common.h"
  51. #include "llfilesystem.h"
  52. #include "lldiskcache.h"
  53. LLFileSystem::LLFileSystem(const LLUUID& id, S32 mode, const char* extra_info)
  54. : mFileID(id),
  55. mMode(mode),
  56. mPosition(0),
  57. mBytesRead(0),
  58. mTotalBytesWritten(0),
  59. mFilename(LLDiskCache::getFilePath(id, extra_info)),
  60. mValid(LLDiskCache::isValid())
  61. {
  62. if (extra_info && *extra_info)
  63. {
  64. mExtraInfo.assign(extra_info);
  65. }
  66. mExists = mValid && LLFile::exists(mFilename);
  67. if (mExists)
  68. {
  69. // Update the last access time for the file since this is the way the
  70. // cache works; it relies on a valid "last accessed time" for each file
  71. // so that it knows how to remove the oldest, unused files.
  72. // Since LLFileSystem instances are short-lived, we update the file
  73. // access time on construction (which also allows to update that time
  74. // at once during cache purging, preventing an old file that would be
  75. // reused to get purged in between LLFileSystem instance construction
  76. // and its actual usage). HB
  77. LLDiskCache::updateFileAccessTime(mFilename);
  78. }
  79. // In append mode, we always write to the end of file, so make sure to
  80. // initialize the current position there... HB
  81. if (mExists && mMode == APPEND)
  82. {
  83. mPosition = LLFile::getFileSize(mFilename);
  84. }
  85. }
  86. LLFileSystem::~LLFileSystem()
  87. {
  88. if (mTotalBytesWritten)
  89. {
  90. // Inform the disk cache about how much bytes we added or removed. HB
  91. LLDiskCache::addBytesWritten(mTotalBytesWritten);
  92. }
  93. }
  94. bool LLFileSystem::read(U8* buffer, S32 bytes)
  95. {
  96. if (!mValid || bytes < 0 || !buffer)
  97. {
  98. return false;
  99. }
  100. if (!bytes)
  101. {
  102. mExists = LLFile::isfile(mFilename);
  103. return mExists;
  104. }
  105. LLFILE* file = LLFile::open(mFilename, "rb");
  106. mExists = file != NULL;
  107. if (!mExists)
  108. {
  109. return false;
  110. }
  111. if (mPosition > 0)
  112. {
  113. fseek(file, mPosition, SEEK_SET);
  114. }
  115. mBytesRead = fread(buffer, 1, bytes, file);
  116. LLFile::close(file);
  117. if (mBytesRead > 0)
  118. {
  119. mPosition += mBytesRead;
  120. // Short reads are also considered a success (needed due to how
  121. // buffered reads are implemented in the viewer code such as in,
  122. // for example, LLAssetStorage::legacyGetDataCallback())... HB
  123. return true;
  124. }
  125. return false;
  126. }
  127. bool LLFileSystem::write(const U8* buffer, S32 bytes)
  128. {
  129. if (!mValid)
  130. {
  131. return false;
  132. }
  133. if (mMode == APPEND)
  134. {
  135. // Write to file, appending to it if it already exists.
  136. LLFILE* file = LLFile::open(mFilename, "a+b");
  137. if (file)
  138. {
  139. fwrite((void*)buffer, 1, bytes, file);
  140. if (LLFile::sFlushOnWrite)
  141. {
  142. fflush(file);
  143. }
  144. mPosition = ftell(file);
  145. LLFile::close(file);
  146. mTotalBytesWritten += bytes;
  147. mExists = true;
  148. return true;
  149. }
  150. }
  151. else if (mMode == OVERWRITE)
  152. {
  153. // Discard any existing contents and write.
  154. mTotalBytesWritten -= LLFile::getFileSize(mFilename);
  155. LLFILE* file = LLFile::open(mFilename, "wb");
  156. if (file)
  157. {
  158. fwrite((void*)buffer, 1, bytes, file);
  159. if (LLFile::sFlushOnWrite)
  160. {
  161. fflush(file);
  162. }
  163. mPosition = ftell(file);
  164. LLFile::close(file);
  165. mTotalBytesWritten += bytes;
  166. mExists = true;
  167. return true;
  168. }
  169. }
  170. else if (mMode == WRITE)
  171. {
  172. // Write at current position, without truncating
  173. S32 size = LLFile::getFileSize(mFilename); // Remember current size
  174. mExists = size > 0;
  175. const char* mode = mExists ? "r+b" : "wb";
  176. LLFILE* file = LLFile::open(mFilename, mode);
  177. if (file)
  178. {
  179. if (mExists && mPosition > 0)
  180. {
  181. fseek(file, mPosition, SEEK_SET);
  182. }
  183. fwrite((void*)buffer, 1, bytes, file);
  184. if (LLFile::sFlushOnWrite)
  185. {
  186. fflush(file);
  187. }
  188. mPosition = ftell(file);
  189. LLFile::close(file);
  190. if (mPosition > size)
  191. {
  192. mTotalBytesWritten += mPosition - size;
  193. }
  194. mExists = true;
  195. return true;
  196. }
  197. }
  198. else
  199. {
  200. llerrs << "Cannot write in READ mode." << llendl;
  201. }
  202. mExists = false;
  203. return false;
  204. }
  205. bool LLFileSystem::seek(S32 offset, S32 origin)
  206. {
  207. if (!mValid)
  208. {
  209. return false;
  210. }
  211. if (mMode == OVERWRITE || mMode == APPEND)
  212. {
  213. llerrs << "Cannot seek in file before writing into it in mode "
  214. << (mMode == APPEND ? "APPEND" : "OVERWRITE") << llendl;
  215. }
  216. if (origin < 0)
  217. {
  218. origin = mPosition;
  219. }
  220. S32 new_pos = origin + offset;
  221. S32 size = LLFile::getFileSize(mFilename);
  222. if (new_pos > size)
  223. {
  224. if (mMode == READ)
  225. {
  226. llwarns << "Attempt to seek past end of file: " << mFilename
  227. << llendl;
  228. mPosition = size;
  229. return false;
  230. }
  231. else // Append zeros to the file up to the new position. HB
  232. {
  233. mPosition = size;
  234. LLFILE* file = LLFile::open(mFilename, "a+b");
  235. if (!file)
  236. {
  237. llwarns << "Attempt to seek past end of file \"" << mFilename
  238. << "\", and could not open it to pad it with zeros."
  239. << llendl;
  240. return false;
  241. }
  242. mExists = true;
  243. size_t bytes = new_pos - size;
  244. char* buffer = new (std::nothrow) char[bytes];
  245. if (buffer)
  246. {
  247. LL_DEBUGS("FileSystem") << "Appending " << bytes
  248. << " padding bytes to: " << mFilename
  249. << LL_ENDL;
  250. memset((void*)buffer, 0, bytes);
  251. fwrite((void*)buffer, 1, bytes, file);
  252. if (LLFile::sFlushOnWrite)
  253. {
  254. fflush(file);
  255. }
  256. mPosition = ftell(file);
  257. mTotalBytesWritten += mPosition - size;
  258. delete[] buffer;
  259. }
  260. LLFile::close(file);
  261. if (mPosition == new_pos)
  262. {
  263. return true;
  264. }
  265. llwarns << "Could not append enough padding bytes to seek to position: "
  266. << size << " in \"" << mFilename << "\" (position "
  267. << mPosition << " reached)." << llendl;
  268. return false;
  269. }
  270. }
  271. if (new_pos < 0)
  272. {
  273. llwarns << "Attempt to seek past beginning of file: " << mFilename
  274. << llendl;
  275. mPosition = 0;
  276. return false;
  277. }
  278. mPosition = new_pos;
  279. return true;
  280. }
  281. S32 LLFileSystem::getSize() const
  282. {
  283. return mValid ? (S32)LLFile::getFileSize(mFilename) : 0;
  284. }
  285. bool LLFileSystem::remove()
  286. {
  287. if (!mValid)
  288. {
  289. return false;
  290. }
  291. mExists = false;
  292. llstat st;
  293. if (LLFile::stat(mFilename, &st))
  294. {
  295. // No such file, we are done.
  296. return true;
  297. }
  298. mTotalBytesWritten -= st.st_size;
  299. return LLFile::remove(mFilename);
  300. }
  301. bool LLFileSystem::rename(const LLUUID& new_id)
  302. {
  303. mFileID = new_id;
  304. if (!mValid)
  305. {
  306. return false;
  307. }
  308. std::string newfname = LLDiskCache::getFilePath(new_id,
  309. mExtraInfo.c_str());
  310. // First remove the new file when it exists
  311. llstat st;
  312. if (LLFile::stat(newfname, &st) == 0)
  313. {
  314. mTotalBytesWritten -= st.st_size;
  315. LLFile::remove(newfname);
  316. }
  317. // Note: this call may fail and will appropriately warn in the log...
  318. mExists = LLFile::rename(mFilename, newfname);
  319. mFilename = newfname;
  320. return mExists;
  321. }
  322. //static
  323. bool LLFileSystem::getExists(const LLUUID& id, const char* extra_info)
  324. {
  325. if (!LLDiskCache::isValid())
  326. {
  327. return false;
  328. }
  329. return LLFile::isfile(LLDiskCache::getFilePath(id, extra_info));
  330. }
  331. //static
  332. S32 LLFileSystem::getFileSize(const LLUUID& id, const char* extra_info)
  333. {
  334. if (!LLDiskCache::isValid())
  335. {
  336. return 0;
  337. }
  338. return LLFile::getFileSize(LLDiskCache::getFilePath(id, extra_info));
  339. }
  340. //static
  341. bool LLFileSystem::removeFile(const LLUUID& id, const char* extra_info)
  342. {
  343. if (!LLDiskCache::isValid())
  344. {
  345. return false;
  346. }
  347. std::string filename = LLDiskCache::getFilePath(id, extra_info);
  348. llstat st;
  349. if (LLFile::stat(filename, &st))
  350. {
  351. // No such file, we are done.
  352. return true;
  353. }
  354. if (st.st_size)
  355. {
  356. LLDiskCache::addBytesWritten(-st.st_size);
  357. }
  358. return LLFile::remove(filename);
  359. }
  360. //static
  361. bool LLFileSystem::renameFile(const LLUUID& old_id, const LLUUID& new_id,
  362. const char* extra_info)
  363. {
  364. if (!LLDiskCache::isValid())
  365. {
  366. return false;
  367. }
  368. std::string old_filename = LLDiskCache::getFilePath(old_id, extra_info);
  369. std::string new_filename = LLDiskCache::getFilePath(new_id, extra_info);
  370. // First remove the new file when it exists
  371. llstat st;
  372. if (LLFile::stat(old_filename, &st) == 0)
  373. {
  374. if (st.st_size)
  375. {
  376. LLDiskCache::addBytesWritten(-st.st_size);
  377. }
  378. LLFile::remove(new_filename);
  379. }
  380. // Note: this call may fail and will appropriately warn in the log...
  381. return LLFile::rename(old_filename, new_filename);
  382. }