Vfs.cpp 10 KB

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