Vfs.cpp 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539
  1. /* ---------------------------------------------------------------------------
  2. Nullsoft Database Engine
  3. --------------------
  4. codename: Near Death Experience
  5. --------------------------------------------------------------------------- */
  6. /* ---------------------------------------------------------------------------
  7. Virtual File System
  8. --------------------------------------------------------------------------- */
  9. #include "../nde.h"
  10. #include <stdio.h>
  11. #include <sys/types.h>
  12. #include <sys/stat.h>
  13. #include "Vfs.h"
  14. #ifndef EOF
  15. #define EOF -1
  16. #endif
  17. /* the FILE (fopen/fwrite/etc) implementation for Mac and Linux */
  18. VFILE *Vfnew(const char *fl, const char *mode, int Cached)
  19. {
  20. if (!fl) return NULL;
  21. VFILE *f = (VFILE *)malloc(sizeof(VFILE));
  22. if (!f)
  23. return NULL;
  24. memset(f, 0, sizeof(VFILE));
  25. #ifdef NDE_ALLOW_NONCACHED
  26. f->cached = Cached;
  27. #else
  28. f->cached = 1;
  29. #endif
  30. if (!strchr(mode, '+'))
  31. {
  32. if (strchr(mode, 'r'))
  33. f->mode = VFS_READ | VFS_MUSTEXIST;
  34. if (strchr(mode, 'w'))
  35. f->mode = VFS_WRITE | VFS_CREATE | VFS_NEWCONTENT;
  36. if (strchr(mode, 'a'))
  37. f->mode = VFS_WRITE | VFS_CREATE | VFS_SEEKEOF;
  38. }
  39. else
  40. {
  41. if (strstr(mode, "r+"))
  42. f->mode = VFS_WRITE | VFS_MUSTEXIST;
  43. if (strstr(mode, "w+"))
  44. f->mode = VFS_WRITE | VFS_CREATE | VFS_NEWCONTENT;
  45. if (strstr(mode, "a+"))
  46. f->mode = VFS_WRITE | VFS_CREATE | VFS_SEEKEOF;
  47. }
  48. if (f->mode == 0 || ((f->mode & VFS_READ) && (f->mode & VFS_WRITE)))
  49. {
  50. free(f);
  51. return NULL;
  52. }
  53. snprintf(f->lockname, sizeof(f->lockname), "%s.lock", fl);
  54. return f;
  55. }
  56. //----------------------------------------------------------------------------
  57. VFILE *Vfopen(VFILE *f, const char *fl, const char *mode, int Cached)
  58. {
  59. if (!f)
  60. {
  61. f = Vfnew(fl, mode, Cached);
  62. if (!f)
  63. return 0;
  64. }
  65. if (!f && !fl) return NULL;
  66. #ifdef NDE_ALLOW_NONCACHED
  67. if (!f->cached)
  68. {
  69. f->rfile = fopen(fl, mode);
  70. if (f->rfile)
  71. f->filename = _strdup(fl);
  72. }
  73. else
  74. {
  75. free(f);
  76. return NULL;
  77. }
  78. return f;
  79. }
  80. #endif
  81. if (f->mode & VFS_MUSTEXIST)
  82. {
  83. if (access(fl, 0) != 0)
  84. {
  85. free(f);
  86. return NULL;
  87. }
  88. }
  89. if (!(f->mode & VFS_NEWCONTENT))
  90. {
  91. FILE *pf;
  92. pf = fopen(fl, "rb");
  93. if (!pf)
  94. {
  95. f->data = (unsigned char *)calloc(VFILE_INC, 1);
  96. if (f->data == NULL)
  97. {
  98. free(f);
  99. return NULL;
  100. }
  101. f->filesize = 0;
  102. f->maxsize = VFILE_INC;
  103. }
  104. else
  105. {
  106. fseek(pf, 0, SEEK_END);
  107. f->filesize = ftell(pf);
  108. fseek(pf, 0, SEEK_SET);
  109. f->data = (unsigned char *)calloc(f->filesize, 1);
  110. if (f->data == NULL)
  111. {
  112. free(f);
  113. return NULL;
  114. }
  115. f->maxsize = f->filesize;
  116. fread(f->data, f->filesize, 1, pf);
  117. fclose(pf);
  118. }
  119. }
  120. if (f->mode & VFS_SEEKEOF)
  121. f->ptr = f->filesize;
  122. f->filename = strdup(fl);
  123. return f;
  124. }
  125. //----------------------------------------------------------------------------
  126. void Vfclose(VFILE *f)
  127. {
  128. if (!f) return;
  129. #ifdef NDE_ALLOW_NONCACHED
  130. if (!f->cached)
  131. {
  132. free(f->filename);
  133. if (f->rfile)
  134. fclose(f->rfile);
  135. f->rfile=0;
  136. // free(f);
  137. return;
  138. }
  139. #endif
  140. if (!(f->mode & VFS_WRITE))
  141. {
  142. free(f->filename);
  143. free(f->data);
  144. //free(f);
  145. return;
  146. }
  147. Vsync(f);
  148. while (f->locks)
  149. Vfunlock(f, 1);
  150. free(f->filename);
  151. free(f->data);
  152. //free(f);
  153. }
  154. //----------------------------------------------------------------------------
  155. size_t Vfread(void *ptr, size_t size, VFILE *f)
  156. {
  157. if (!ptr || !f) return 0;
  158. #ifdef NDE_ALLOW_NONCACHED
  159. if (!f->cached)
  160. {
  161. return fread(ptr, 1, size, f->rfile);
  162. }
  163. #endif
  164. size_t s = size;
  165. if (!s) return 0;
  166. if (s + f->ptr > f->filesize)
  167. {
  168. //FUCKO: remove this
  169. if (!(f->ptr < f->filesize))
  170. {
  171. // if (!f->flushtable) // this would be ideal, if we could figure out f->flushtable
  172. // f->flushtable=MessageBox(g_hwnd,"DB read failed, DB may be corrupted.\r\n\r\n"
  173. // "Hit Retry to continue, or Cancel to clear the DB and start over","Winamp Library Error",MB_RETRYCANCEL) == IDCANCEL; //fucko
  174. //MessageBox(g_hwnd,"DB read failed, DB may be corrupted. If this error persists, remove all files from the library.",
  175. // "Winamp Library Error",MB_OK);
  176. return 0;
  177. }
  178. s = f->filesize - f->ptr;
  179. }
  180. memcpy(ptr, f->data+f->ptr, s);
  181. f->ptr += s;
  182. return (s/size);
  183. }
  184. //----------------------------------------------------------------------------
  185. void Vfwrite(const void *ptr, size_t size, VFILE *f)
  186. {
  187. if (!ptr || !f) return;
  188. #ifdef NDE_ALLOW_NONCACHED
  189. if (!f->cached)
  190. {
  191. fwrite(ptr, size*n, f->rfile);
  192. return;
  193. }
  194. #endif
  195. f->dirty=1;
  196. if (size + f->ptr > f->maxsize)
  197. {
  198. // grow f->data,f->maxsize to be (size + f->ptr + VFILE_INC-1)&~(VFILE_INC-1)
  199. // instead of calling Vgrow again which gets kinda slow
  200. size_t newsize=(size + f->ptr + VFILE_INC-1)&~(VFILE_INC-1);
  201. f->data=(unsigned char *)realloc(f->data,newsize);
  202. if (f->data == NULL) return;
  203. memset(f->data+f->maxsize,0,newsize-f->maxsize);
  204. f->maxsize=newsize;
  205. }
  206. memcpy(f->data + f->ptr, ptr, size);
  207. f->ptr += size;
  208. if (f->ptr > f->filesize)
  209. f->filesize = f->ptr;
  210. }
  211. //----------------------------------------------------------------------------
  212. void Vgrow(VFILE *f)
  213. {
  214. if (!f) return;
  215. #ifdef NDE_ALLOW_NONCACHED
  216. if (!f->cached) return;
  217. #endif
  218. f->data = (unsigned char *)realloc(f->data, f->maxsize + VFILE_INC);
  219. f->maxsize += VFILE_INC;
  220. }
  221. //----------------------------------------------------------------------------
  222. void Vfputs(char *str, VFILE *f)
  223. {
  224. if (!f) return;
  225. #ifdef NDE_ALLOW_NONCACHED
  226. if (!f->cached)
  227. {
  228. fputs(str, f->rfile);
  229. return;
  230. }
  231. #endif
  232. Vfwrite(str, strlen(str), f);
  233. }
  234. //----------------------------------------------------------------------------
  235. void Vfputc(char c, VFILE *f)
  236. {
  237. if (!f) return;
  238. #ifdef NDE_ALLOW_NONCACHED
  239. if (!f->cached)
  240. {
  241. fputc(c, f->rfile);
  242. return;
  243. }
  244. #endif
  245. Vfwrite(&c, 1, f);
  246. }
  247. /* benski> unused:
  248. // not mac compliant
  249. //----------------------------------------------------------------------------
  250. char *Vfgets(char *dest, int n, VFILE *f) {
  251. if (!f) return NULL;
  252. #ifdef NDE_ALLOW_NONCACHED
  253. if (!f->cached)
  254. {
  255. #ifdef NDE_NOWIN32FILEIO
  256. return fgets(dest, n, f->rfile);
  257. #else
  258. #error port me!
  259. #endif
  260. }
  261. #endif
  262. unsigned char c=0;
  263. char *p;
  264. int l=0;
  265. p = dest;
  266. while (l < n && !Vfeof(f)) {
  267. c = f->data[f->ptr];
  268. f->ptr++;
  269. *p = c;
  270. p++;
  271. l++;
  272. if (c == '\n') {
  273. if (!Vfeof(f) && f->data[f->ptr] == '\r') {
  274. f->ptr++;
  275. }
  276. break;
  277. }
  278. }
  279. *p=0;
  280. return dest;
  281. }
  282. */
  283. /* benski> unused:
  284. //----------------------------------------------------------------------------
  285. char Vfgetc(VFILE *f) {
  286. if (!f) return EOF;
  287. #ifdef NDE_ALLOW_NONCACHED
  288. if (!f->cached)
  289. #ifdef NDE_NOWIN32FILEIO
  290. return fgetc(f->rfile);
  291. #else
  292. #error port me#
  293. #endif
  294. #endif
  295. if (!Vfeof(f))
  296. return f->data[f->ptr++];
  297. return EOF;
  298. }
  299. */
  300. //----------------------------------------------------------------------------
  301. unsigned long Vftell(VFILE *f)
  302. {
  303. if (!f) return (unsigned)-1;
  304. #ifdef NDE_ALLOW_NONCACHED
  305. if (!f->cached)
  306. {
  307. return ftell(f->rfile);
  308. }
  309. #endif
  310. return f->ptr;
  311. }
  312. //----------------------------------------------------------------------------
  313. void Vfseek(VFILE *f, long i, int whence)
  314. {
  315. if (!f) return;
  316. #ifdef NDE_ALLOW_NONCACHED
  317. if (!f->cached)
  318. {
  319. fseek(f->rfile, i, whence);
  320. return;
  321. }
  322. #endif
  323. switch (whence)
  324. {
  325. case SEEK_SET:
  326. f->ptr = i;
  327. break;
  328. case SEEK_CUR:
  329. f->ptr += i;
  330. break;
  331. case SEEK_END:
  332. f->ptr = f->filesize+i;
  333. break;
  334. }
  335. }
  336. //----------------------------------------------------------------------------
  337. int Vfeof(VFILE *f)
  338. {
  339. if (!f) return -1;
  340. #ifdef NDE_ALLOW_NONCACHED
  341. if (!f->cached)
  342. {
  343. return feof(f->rfile);
  344. }
  345. #endif
  346. return (f->ptr >= f->filesize);
  347. }
  348. //----------------------------------------------------------------------------
  349. int Vsync(VFILE *f)
  350. {
  351. if (!f) return 0;
  352. if (!f->dirty) return 0;
  353. if (f->mode & VFS_WRITE)
  354. {
  355. #ifdef NDE_ALLOW_NONCACHED
  356. if (!f->cached)
  357. {
  358. int p=ftell(f->rfile);
  359. fclose(f->rfile);
  360. f->rfile = fopen(f->filename, "r+b");
  361. if (!f->rfile)
  362. return 1;
  363. fseek(f->rfile, p, SEEK_SET);
  364. return 0;
  365. }
  366. #endif
  367. char newfn[1024], oldfn[1024];
  368. DWORD mypid=getpid();
  369. snprintf(newfn,sizeof(newfn), "%s.n3w%08X",f->filename,mypid);
  370. snprintf(oldfn,sizeof(oldfn), "%s.o1d%08X",f->filename,mypid);
  371. unlink(newfn);
  372. unlink(oldfn);
  373. tryagain:
  374. FILE *pf = fopen(newfn, "wb");
  375. if (!pf || fwrite(f->data, f->filesize, 1, pf) != 1)
  376. {
  377. if (pf) fclose(pf);
  378. printf("Error flushing DB to disk (is your drive full?)\n\nHit (R)etry to try again (recommended), or (C)ancel to abort (NOT recommended, and may leave the DB in an unuseable state)");
  379. fflush(stdout);
  380. char c;
  381. while (1)
  382. {
  383. scanf("%c", &c);
  384. // clear_stdin();
  385. c = toupper(c);
  386. if (c == 'R') goto tryagain;
  387. else if (c == 'C') break;
  388. }
  389. unlink(newfn);
  390. return 1;
  391. }
  392. fclose(pf);
  393. rename(f->filename,oldfn); // save old file
  394. int rv=0;
  395. tryagain2:
  396. if (rename(newfn,f->filename))
  397. {
  398. printf("Error updating DB file on disk. This should never really happen\n\nHit (R)etry to try again (recommended), or (C)ancel to abort (NOT recommended, and may leave the DB in an unuseable state)");
  399. fflush(stdout);
  400. char c;
  401. while (1)
  402. {
  403. scanf("%c", &c);
  404. // clear_stdin();
  405. c = toupper(c);
  406. if (c == 'R') goto tryagain2;
  407. else if (c == 'C') break;
  408. }
  409. rename(oldfn,f->filename); // restore old file
  410. rv=1;
  411. }
  412. // clean up our temp files
  413. unlink(oldfn);
  414. unlink(newfn);
  415. //free(newfn);
  416. //free(oldfn);
  417. return rv;
  418. }
  419. f->dirty=0;
  420. return 0;
  421. }
  422. void Vfdestroy(VFILE *f)
  423. {
  424. // benski> TODO:
  425. if (f)
  426. {
  427. Vfclose(f);
  428. while (f->locks)
  429. Vfunlock(f, 1);
  430. // TODO if (f->mutex) CloseHandle(f->mutex);
  431. free(f);
  432. }
  433. }
  434. // benski> TODO implement these with fopen
  435. // returns 0 on failure
  436. int Vflock(VFILE *fl, bool is_sync)
  437. {
  438. #ifndef NO_TABLE_WIN32_LOCKING
  439. if (!fl) return 0;
  440. if (!is_sync && fl->cached)
  441. return 1;
  442. if (fl->locks++ == 0)
  443. {
  444. int retry_cnt=0;
  445. FILE *fFile;
  446. do
  447. {
  448. fFile = fopen(fl->lockname, "wb");
  449. if (fFile == 0) Sleep(100);
  450. }
  451. while (fFile == 0 && retry_cnt++ < 100); // try for 10 seconds
  452. if (fFile == INVALID_HANDLE_VALUE) return 0; // db already locked, fail gracefully
  453. fl->lockFile = fFile;
  454. }
  455. #endif
  456. return 1;
  457. }
  458. void Vfunlock(VFILE *fl, bool is_sync)
  459. {
  460. #ifndef NO_TABLE_WIN32_LOCKING
  461. if (!is_sync && fl->cached)
  462. return;
  463. if (--fl->locks == 0)
  464. {
  465. if (fl && fl->lockFile && fl->lockFile != INVALID_HANDLE_VALUE)
  466. {
  467. fclose(fl->lockFile);
  468. unlink(fl->lockname);
  469. }
  470. }
  471. #endif
  472. }