filesdb.c 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826
  1. /*
  2. * dpkg - main program for package management
  3. * filesdb.c - management of database of files installed on system
  4. *
  5. * Copyright (C) 1995 Ian Jackson <iwj10@cus.cam.ac.uk>
  6. * Copyright (C) 2000 Wichert Akkerman <wakkerma@debian.org>
  7. *
  8. * This is free software; you can redistribute it and/or modify
  9. * it under the terms of the GNU General Public License as
  10. * published by the Free Software Foundation; either version 2,
  11. * or (at your option) any later version.
  12. *
  13. * This is distributed in the hope that it will be useful, but
  14. * WITHOUT ANY WARRANTY; without even the implied warranty of
  15. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  16. * GNU General Public License for more details.
  17. *
  18. * You should have received a copy of the GNU General Public
  19. * License along with dpkg; if not, write to the Free Software
  20. * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  21. */
  22. #include <assert.h>
  23. #include <unistd.h>
  24. #include <fcntl.h>
  25. #include <string.h>
  26. #include <errno.h>
  27. #include <sys/stat.h>
  28. #include <pwd.h>
  29. #include <grp.h>
  30. #include <sys/types.h>
  31. #include <config.h>
  32. #include <dpkg.h>
  33. #include <dpkg-db.h>
  34. #include "filesdb.h"
  35. #include "main.h"
  36. #ifdef HAVE_SYS_SYSINFO_H
  37. #include <sys/sysinfo.h>
  38. #endif
  39. /*** Generic data structures and routines ***/
  40. static int allpackagesdone= 0;
  41. static int nfiles= 0;
  42. static struct diversion *diversions= 0;
  43. static FILE *diversionsfile= 0;
  44. static FILE *statoverridefile= 0;
  45. void note_must_reread_files_inpackage(struct pkginfo *pkg) {
  46. allpackagesdone= 0;
  47. ensure_package_clientdata(pkg);
  48. pkg->clientdata->fileslistvalid= 0;
  49. }
  50. static int saidread=0;
  51. /* load the list of files in this package into memory, or update the
  52. * list if it is there but stale
  53. */
  54. void ensure_packagefiles_available(struct pkginfo *pkg) {
  55. static struct varbuf fnvb;
  56. FILE *file;
  57. const char *filelistfile;
  58. struct fileinlist **lendp, *newent, *current;
  59. struct filepackages *packageslump;
  60. int search, findlast, putat;
  61. struct stat stat_buf;
  62. char *loaded_list, *loaded_list_end, *thisline, *nextline, *ptr;
  63. if (pkg->clientdata && pkg->clientdata->fileslistvalid) return;
  64. ensure_package_clientdata(pkg);
  65. /* Throw away any the stale data, if there was any. */
  66. for (current= pkg->clientdata->files;
  67. current;
  68. current= current->next) {
  69. /* For each file that used to be in the package,
  70. * go through looking for this package's entry in the list
  71. * of packages containing this file, and blank it out.
  72. */
  73. for (packageslump= current->namenode->packages;
  74. packageslump;
  75. packageslump= packageslump->more)
  76. for (search= 0;
  77. search < PERFILEPACKAGESLUMP && packageslump->pkgs[search];
  78. search++)
  79. if (packageslump->pkgs[search] == pkg) {
  80. /* Hah! Found it. */
  81. for (findlast= search+1;
  82. findlast < PERFILEPACKAGESLUMP && packageslump->pkgs[findlast];
  83. findlast++);
  84. findlast--;
  85. /* findlast is now the last occupied entry, which may be the same as
  86. * search. We blank out the entry for this package. We also
  87. * have to copy the last entry into the empty slot, because
  88. * the list is null-pointer-terminated.
  89. */
  90. packageslump->pkgs[search]= packageslump->pkgs[findlast];
  91. packageslump->pkgs[findlast]= 0;
  92. /* This may result in an empty link in the list. This is OK. */
  93. goto xit_search_to_delete_from_perfilenodelist;
  94. }
  95. xit_search_to_delete_from_perfilenodelist:
  96. ;
  97. /* The actual filelist links were allocated using nfmalloc, so
  98. * we shouldn't free them.
  99. */
  100. }
  101. pkg->clientdata->files= 0;
  102. /* Packages which aren't installed don't have a files list. */
  103. if (pkg->status == stat_notinstalled) {
  104. pkg->clientdata->fileslistvalid= 1; return;
  105. }
  106. filelistfile= pkgadminfile(pkg,LISTFILE);
  107. onerr_abort++;
  108. file= fopen(filelistfile,"r");
  109. if (!file) {
  110. if (errno != ENOENT)
  111. ohshite(_("unable to open files list file for package `%.250s'"),pkg->name);
  112. onerr_abort--;
  113. if (pkg->status != stat_configfiles) {
  114. if (saidread == 1) putc('\n',stderr);
  115. fprintf(stderr,
  116. _("dpkg: serious warning: files list file for package `%.250s' missing,"
  117. " assuming package has no files currently installed.\n"), pkg->name);
  118. }
  119. pkg->clientdata->files= 0;
  120. pkg->clientdata->fileslistvalid= 1;
  121. return;
  122. }
  123. push_cleanup(cu_closefile,ehflag_bombout, 0,0, 1,(void*)file);
  124. if(fstat(fileno(file), &stat_buf))
  125. ohshite("unable to stat files list file for package `%.250s'",pkg->name);
  126. loaded_list = nfmalloc(stat_buf.st_size);
  127. loaded_list_end = loaded_list + stat_buf.st_size;
  128. fd_buf_copy(fileno(file), loaded_list, stat_buf.st_size, _("files list for package `%.250s'"), pkg->name);
  129. lendp= &pkg->clientdata->files;
  130. thisline = loaded_list;
  131. while (thisline < loaded_list_end) {
  132. if (!(ptr = memchr(thisline, '\n', loaded_list_end - thisline)))
  133. ohshit("files list file for package `%.250s' is missing final newline",pkg->name);
  134. /* where to start next time around */
  135. nextline = ptr + 1;
  136. /* strip trailing "/" */
  137. if (ptr > thisline && ptr[-1] == '/') ptr--;
  138. /* add the file to the list */
  139. if (ptr == thisline)
  140. ohshit(_("files list file for package `%.250s' contains empty filename"),pkg->name);
  141. *ptr = 0;
  142. newent= nfmalloc(sizeof(struct fileinlist));
  143. newent->namenode= findnamenode(thisline, fnn_nocopy);
  144. newent->next= 0;
  145. *lendp= newent;
  146. lendp= &newent->next;
  147. thisline = nextline;
  148. }
  149. pop_cleanup(ehflag_normaltidy); /* file= fopen() */
  150. if (fclose(file))
  151. ohshite(_("error closing files list file for package `%.250s'"),pkg->name);
  152. if (fnvb.used)
  153. ohshit(_("files list file for package `%.250s' is truncated"),pkg->name);
  154. onerr_abort--;
  155. for (newent= pkg->clientdata->files; newent; newent= newent->next) {
  156. packageslump= newent->namenode->packages;
  157. if (packageslump) {
  158. for (putat= 0;
  159. putat < PERFILEPACKAGESLUMP && packageslump->pkgs[putat];
  160. putat++);
  161. if (putat >= PERFILEPACKAGESLUMP) packageslump= 0;
  162. }
  163. if (!packageslump) {
  164. packageslump= nfmalloc(sizeof(struct filepackages));
  165. packageslump->more= newent->namenode->packages;
  166. newent->namenode->packages= packageslump;
  167. putat= 0;
  168. }
  169. packageslump->pkgs[putat]= pkg;
  170. if (++putat < PERFILEPACKAGESLUMP) packageslump->pkgs[putat]= 0;
  171. }
  172. pkg->clientdata->fileslistvalid= 1;
  173. }
  174. void ensure_allinstfiles_available(void) {
  175. struct pkgiterator *it;
  176. struct pkginfo *pkg;
  177. if (allpackagesdone) return;
  178. if (saidread<2) {
  179. saidread=1;
  180. printf(f_largemem>0 ? _("(Reading database ... ") : _("(Scanning database ... "));
  181. }
  182. it= iterpkgstart();
  183. while ((pkg= iterpkgnext(it)) != 0) ensure_packagefiles_available(pkg);
  184. iterpkgend(it);
  185. allpackagesdone= 1;
  186. if (saidread==1) {
  187. printf(_("%d files and directories currently installed.)\n"),nfiles);
  188. saidread=2;
  189. }
  190. }
  191. void ensure_allinstfiles_available_quiet(void) {
  192. saidread=2;
  193. ensure_allinstfiles_available();
  194. }
  195. void write_filelist_except(struct pkginfo *pkg, struct fileinlist *list, int leaveout) {
  196. /* If leaveout is nonzero, will not write any file whose filenamenode
  197. * has the fnnf_elide_other_lists flag set.
  198. */
  199. static struct varbuf vb, newvb;
  200. FILE *file;
  201. varbufreset(&vb);
  202. varbufaddstr(&vb,admindir);
  203. varbufaddstr(&vb,"/" INFODIR);
  204. varbufaddstr(&vb,pkg->name);
  205. varbufaddstr(&vb,"." LISTFILE);
  206. varbufaddc(&vb,0);
  207. varbufreset(&newvb);
  208. varbufaddstr(&newvb,vb.buf);
  209. varbufaddstr(&newvb,NEWDBEXT);
  210. varbufaddc(&newvb,0);
  211. file= fopen(newvb.buf,"w+");
  212. if (!file)
  213. ohshite(_("unable to create updated files list file for package %s"),pkg->name);
  214. push_cleanup(cu_closefile,ehflag_bombout, 0,0, 1,(void*)file);
  215. while (list) {
  216. if (!(leaveout && (list->namenode->flags & fnnf_elide_other_lists))) {
  217. fputs(list->namenode->name,file);
  218. putc('\n',file);
  219. }
  220. list= list->next;
  221. }
  222. if (ferror(file))
  223. ohshite(_("failed to write to updated files list file for package %s"),pkg->name);
  224. if (fflush(file))
  225. ohshite(_("failed to flush updated files list file for package %s"),pkg->name);
  226. if (fsync(fileno(file)))
  227. ohshite(_("failed to sync updated files list file for package %s"),pkg->name);
  228. pop_cleanup(ehflag_normaltidy); /* file= fopen() */
  229. if (fclose(file))
  230. ohshite(_("failed to close updated files list file for package %s"),pkg->name);
  231. if (rename(newvb.buf,vb.buf))
  232. ohshite(_("failed to install updated files list file for package %s"),pkg->name);
  233. note_must_reread_files_inpackage(pkg);
  234. }
  235. void reversefilelist_init(struct reversefilelistiter *iterptr,
  236. struct fileinlist *files) {
  237. /* Initialises an iterator that appears to go through the file
  238. * list `files' in reverse order, returning the namenode from
  239. * each. What actually happens is that we walk the list here,
  240. * building up a reverse list, and then peel it apart one
  241. * entry at a time.
  242. */
  243. struct fileinlist *newent;
  244. iterptr->todo= 0;
  245. while (files) {
  246. newent= m_malloc(sizeof(struct fileinlist));
  247. newent->namenode= files->namenode;
  248. newent->next= iterptr->todo;
  249. iterptr->todo= newent;
  250. files= files->next;
  251. }
  252. }
  253. struct filenamenode *reversefilelist_next(struct reversefilelistiter *iterptr) {
  254. struct filenamenode *ret;
  255. struct fileinlist *todo;
  256. todo= iterptr->todo;
  257. if (!todo) return 0;
  258. ret= todo->namenode;
  259. iterptr->todo= todo->next;
  260. free(todo);
  261. return ret;
  262. }
  263. void reversefilelist_abort(struct reversefilelistiter *iterptr) {
  264. /* Clients must call this function to clean up the reversefilelistiter
  265. * if they wish to break out of the iteration before it is all done.
  266. * Calling this function is not necessary if reversefilelist_next has
  267. * been called until it returned 0.
  268. */
  269. while (reversefilelist_next(iterptr));
  270. }
  271. void ensure_statoverrides(void) {
  272. static struct varbuf vb;
  273. struct stat stab1, stab2;
  274. FILE *file;
  275. char *loaded_list, *loaded_list_end, *thisline, *nextline, *ptr;
  276. struct filestatoverride *fso;
  277. struct filenamenode *fnn;
  278. varbufreset(&vb);
  279. varbufaddstr(&vb,admindir);
  280. varbufaddstr(&vb,"/" STATOVERRIDEFILE);
  281. varbufaddc(&vb,0);
  282. onerr_abort++;
  283. file= fopen(vb.buf,"r");
  284. if (!file) {
  285. if (errno != ENOENT) ohshite(_("failed to open statoverride file"));
  286. if (!statoverridefile) { onerr_abort--; return; }
  287. } else {
  288. if (fstat(fileno(file),&stab2))
  289. ohshite(_("failed to fstat statoverride file"));
  290. if (statoverridefile) {
  291. if (fstat(fileno(statoverridefile),&stab1))
  292. ohshite(_("failed to fstat previous statoverride file"));
  293. if (stab1.st_dev == stab2.st_dev && stab1.st_ino == stab2.st_ino) {
  294. fclose(file); onerr_abort--; return;
  295. }
  296. }
  297. }
  298. if (statoverridefile) fclose(statoverridefile);
  299. statoverridefile= file;
  300. loaded_list = nfmalloc(stab2.st_size);
  301. loaded_list_end = loaded_list + stab2.st_size;
  302. fd_buf_copy(fileno(file), loaded_list, stab2.st_size, _("statoverride file `%.250s'"), vb.buf);
  303. thisline = loaded_list;
  304. while (thisline<loaded_list_end) {
  305. char* endptr;
  306. fso= nfmalloc(sizeof(struct filestatoverride));
  307. if (!(ptr = memchr(thisline, '\n', loaded_list_end - thisline)))
  308. ohshit("statoverride file is missing final newline");
  309. /* where to start next time around */
  310. nextline = ptr + 1;
  311. if (ptr == thisline)
  312. ohshit(_("statoverride file contains empty line"));
  313. *ptr = 0;
  314. /* Extract the uid */
  315. if (!(ptr=memchr(thisline, ' ', nextline-thisline)))
  316. ohshit("syntax error in statusoverride file ");
  317. *ptr=0;
  318. if (thisline[0]=='#') {
  319. fso->uid=strtol(thisline + 1, &endptr, 10);
  320. if (*endptr!=0)
  321. ohshit("syntax error: invalid uid in statusoverride file ");
  322. } else {
  323. struct passwd* pw = getpwnam(thisline);
  324. if (pw==NULL)
  325. ohshit("syntax error: unknown user `%s' in statusoverride file ", thisline);
  326. fso->uid=pw->pw_uid;
  327. }
  328. /* Move to the next bit */
  329. thisline=ptr+1;
  330. if (thisline>=loaded_list_end)
  331. ohshit("unexpected end of line in statusoverride file");
  332. /* Extract the gid */
  333. if (!(ptr=memchr(thisline, ' ', nextline-thisline)))
  334. ohshit("syntax error in statusoverride file ");
  335. *ptr=0;
  336. if (thisline[0]=='#') {
  337. fso->gid=strtol(thisline + 1, &endptr, 10);
  338. if (*endptr!=0)
  339. ohshit("syntax error: invalid gid in statusoverride file ");
  340. } else {
  341. struct group* gr = getgrnam(thisline);
  342. if (gr==NULL)
  343. ohshit("syntax error: unknown group `%s' in statusoverride file ", thisline);
  344. fso->gid=gr->gr_gid;
  345. }
  346. /* Move to the next bit */
  347. thisline=ptr+1;
  348. if (thisline>=loaded_list_end)
  349. ohshit("unexecpted end of line in statusoverride file");
  350. /* Extract the mode */
  351. if (!(ptr=memchr(thisline, ' ', nextline-thisline)))
  352. ohshit("syntax error in statusoverride file ");
  353. *ptr=0;
  354. fso->mode=strtol(thisline, &endptr, 8);
  355. if (*endptr!=0)
  356. ohshit("syntax error: invalid mode in statusoverride file ");
  357. /* Move to the next bit */
  358. thisline=ptr+1;
  359. if (thisline>=loaded_list_end)
  360. ohshit("unexecpted end of line in statusoverride file");
  361. fnn= findnamenode(thisline, 0);
  362. if (fnn->statoverride)
  363. ohshit("multiple statusoverides present for file `%.250s'", thisline);
  364. fnn->statoverride=fso;
  365. /* Moving on.. */
  366. thisline=nextline;
  367. }
  368. onerr_abort--;
  369. }
  370. void ensure_diversions(void) {
  371. static struct varbuf vb;
  372. struct stat stab1, stab2;
  373. char linebuf[MAXDIVERTFILENAME];
  374. FILE *file;
  375. struct diversion *ov, *oicontest, *oialtname;
  376. int l;
  377. varbufreset(&vb);
  378. varbufaddstr(&vb,admindir);
  379. varbufaddstr(&vb,"/" DIVERSIONSFILE);
  380. varbufaddc(&vb,0);
  381. onerr_abort++;
  382. file= fopen(vb.buf,"r");
  383. if (!file) {
  384. if (errno != ENOENT) ohshite(_("failed to open diversions file"));
  385. if (!diversionsfile) { onerr_abort--; return; }
  386. } else if (diversionsfile) {
  387. if (fstat(fileno(diversionsfile),&stab1))
  388. ohshite(_("failed to fstat previous diversions file"));
  389. if (fstat(fileno(file),&stab2))
  390. ohshite(_("failed to fstat diversions file"));
  391. if (stab1.st_dev == stab2.st_dev && stab1.st_ino == stab2.st_ino) {
  392. fclose(file); onerr_abort--; return;
  393. }
  394. }
  395. l= fcntl(fileno(file), F_GETFD);
  396. if (l >= 0) fcntl(fileno(file), F_SETFD, l | FD_CLOEXEC);
  397. if (diversionsfile) fclose(diversionsfile);
  398. diversionsfile= file;
  399. for (ov= diversions; ov; ov= ov->next) {
  400. ov->useinstead->divert->camefrom->divert= 0;
  401. ov->useinstead->divert= 0;
  402. }
  403. diversions= 0;
  404. if (!file) { onerr_abort--; return; }
  405. while (fgets(linebuf,sizeof(linebuf),file)) {
  406. oicontest= nfmalloc(sizeof(struct diversion));
  407. oialtname= nfmalloc(sizeof(struct diversion));
  408. l= strlen(linebuf);
  409. if (l == 0) ohshit(_("fgets gave an empty string from diversions [i]"));
  410. if (linebuf[--l] != '\n') ohshit(_("diversions file has too-long line or EOF [i]"));
  411. linebuf[l]= 0;
  412. oialtname->camefrom= findnamenode(linebuf, 0);
  413. oialtname->useinstead= 0;
  414. if (!fgets(linebuf,sizeof(linebuf),file)) {
  415. if (ferror(file)) ohshite(_("read error in diversions [ii]"));
  416. else ohshit(_("unexpected EOF in diversions [ii]"));
  417. }
  418. l= strlen(linebuf);
  419. if (l == 0) ohshit(_("fgets gave an empty string from diversions [ii]"));
  420. if (linebuf[--l] != '\n') ohshit(_("diversions file has too-long line or EOF [ii]"));
  421. linebuf[l]= 0;
  422. oicontest->useinstead= findnamenode(linebuf, 0);
  423. oicontest->camefrom= 0;
  424. if (!fgets(linebuf,sizeof(linebuf),file)) {
  425. if (ferror(file)) ohshite(_("read error in diversions [iii]"));
  426. else ohshit(_("unexpected EOF in diversions [iii]"));
  427. }
  428. l= strlen(linebuf);
  429. if (l == 0) ohshit(_("fgets gave an empty string from diversions [iii]"));
  430. if (linebuf[--l] != '\n') ohshit(_("diversions file has too-long line or EOF [ii]"));
  431. linebuf[l]= 0;
  432. oicontest->pkg= oialtname->pkg=
  433. strcmp(linebuf,":") ? findpackage(linebuf) : 0;
  434. if (oialtname->camefrom->divert || oicontest->useinstead->divert)
  435. ohshit(_("conflicting diversions involving `%.250s' or `%.250s'"),
  436. oialtname->camefrom->name, oicontest->useinstead->name);
  437. oialtname->camefrom->divert= oicontest;
  438. oicontest->useinstead->divert= oialtname;
  439. oicontest->next= diversions;
  440. diversions= oicontest;
  441. }
  442. if (ferror(file)) ohshite(_("read error in diversions [i]"));
  443. onerr_abort--;
  444. }
  445. /*** Data structures common to both in-core databases ***/
  446. struct fileiterator {
  447. union {
  448. struct {
  449. struct filenamenode *current;
  450. } low;
  451. struct {
  452. struct filenamenode *namenode;
  453. int nbinn;
  454. } high;
  455. } u;
  456. };
  457. /*** Data structures for fast, large memory usage database ***/
  458. #define BINS (1 << 13)
  459. /* This must always be a power of two. If you change it
  460. * consider changing the per-character hashing factor (currently
  461. * 1785 = 137*13) too.
  462. */
  463. static struct filenamenode *bins[BINS];
  464. /*** Data structures for low-memory-footprint in-core files database ***/
  465. /* the idea is that you have a tree structure in memory which has the
  466. same structure as the names themselves.
  467. Each node in the tree gets an fdirnode. This may have a
  468. filenamenode attached to it (if there is really a filename
  469. corresponding to the path down the tree to get here) and an
  470. fdirents (if there is anything below this point.)
  471. The fdirents structure lists the entries in a directory. If there
  472. is only 1 node below us then there's just one fdirents with a
  473. single entry; if there are more then then next one (as defined by
  474. the 'more' field) contains two entries; the next four; etc.
  475. This doubling effect is enforced by findnamenow_low() rather than
  476. by a count field in the structure.
  477. 1999-07-26 RJK
  478. */
  479. struct fdirents {
  480. struct fdirents *more;
  481. struct { const char *component; struct fdirnode *go; } entries[1];
  482. /* first one has one entry, then two, then four, &c */
  483. };
  484. struct fdirnode {
  485. struct filenamenode *here;
  486. struct fdirents *contents;
  487. };
  488. static struct fdirnode fdirroot;
  489. static struct filenamenode *allfiles;
  490. struct filenamesblock {
  491. struct filenamesblock *next;
  492. char *data;
  493. };
  494. static struct filenamesblock *namesarea= 0;
  495. static int namesarealeft= 0;
  496. /*** Code for both. This is rather hacky, sorry ... ***/
  497. struct fileiterator *iterfilestart(void) {
  498. struct fileiterator *i;
  499. i= m_malloc(sizeof(struct fileiterator));
  500. switch (f_largemem) {
  501. case 1:
  502. i->u.high.namenode= 0;
  503. i->u.high.nbinn= 0;
  504. break;
  505. case -1:
  506. i->u.low.current= allfiles;
  507. break;
  508. default:
  509. internerr("iterfilestart no f_largemem");
  510. }
  511. return i;
  512. }
  513. struct filenamenode *iterfilenext(struct fileiterator *i) {
  514. struct filenamenode *r;
  515. switch (f_largemem) {
  516. case 1:
  517. while (!i->u.high.namenode) {
  518. if (i->u.high.nbinn >= BINS) return 0;
  519. i->u.high.namenode= bins[i->u.high.nbinn++];
  520. }
  521. r= i->u.high.namenode;
  522. i->u.high.namenode= r->next;
  523. break;
  524. case -1:
  525. if (!i->u.low.current) return 0;
  526. r= i->u.low.current;
  527. i->u.low.current= i->u.low.current->next;
  528. break;
  529. default:
  530. internerr("iterfilenext no f_largemem");
  531. }
  532. return r;
  533. }
  534. void iterfileend(struct fileiterator *i) {
  535. free(i);
  536. }
  537. static int autodetect_largemem(void) {
  538. #if defined(HAVE_SYSINFO) && defined (MEMINFO_IN_SYSINFO)
  539. struct sysinfo info;
  540. if (sysinfo(&info)) return 0;
  541. if (info.freeram + (info.sharedram>>2) + (info.bufferram>>2) < 24576*1024 &&
  542. info.totalram < 24576*1024)
  543. return 0;
  544. #endif
  545. return 1;
  546. }
  547. void filesdbinit(void) {
  548. struct filenamenode *fnn;
  549. int i;
  550. if (!f_largemem)
  551. f_largemem= autodetect_largemem() ? 1 : -1;
  552. switch (f_largemem) {
  553. case 1:
  554. for (i=0; i<BINS; i++)
  555. for (fnn= bins[i]; fnn; fnn= fnn->next) {
  556. fnn->flags= 0;
  557. fnn->oldhash= 0;
  558. fnn->filestat= 0;
  559. }
  560. break;
  561. case -1:
  562. for (fnn= allfiles;
  563. fnn;
  564. fnn= fnn->next) {
  565. fnn->flags= 0;
  566. fnn->oldhash= 0;
  567. fnn->filestat= 0;
  568. }
  569. break;
  570. default:
  571. internerr("filesdbinit no f_largemem");
  572. }
  573. }
  574. static struct filenamenode *findnamenode_high(const char *name,
  575. enum fnnflags flags);
  576. static struct filenamenode *findnamenode_low(const char *name,
  577. enum fnnflags flags);
  578. struct filenamenode *findnamenode(const char *name, enum fnnflags flags) {
  579. switch (f_largemem) {
  580. case 1:
  581. return findnamenode_high(name, flags);
  582. case -1:
  583. return findnamenode_low(name, flags);
  584. default:
  585. internerr("findnamenode no f_largemem");
  586. }
  587. return NULL;
  588. }
  589. /*** Code for low-memory-footprint in-core files database ***/
  590. static struct filenamenode *findnamenode_low(const char *name,
  591. enum fnnflags flags) {
  592. struct fdirnode *traverse;
  593. struct fdirents *ents, **addto;
  594. const char *nameleft, *slash;
  595. char *p;
  596. struct filenamesblock *newblock;
  597. int n, i, nentrieshere, alloc;
  598. const char *orig_name = name;
  599. /* We skip initial slashes and ./ pairs, and add our own single leading slash. */
  600. name= skip_slash_dotslash(name);
  601. nameleft= name;
  602. traverse= &fdirroot;
  603. while (nameleft) {
  604. slash= strchr(nameleft,'/');
  605. if (slash) {
  606. n= (int)(slash-nameleft); slash++;
  607. } else {
  608. n= strlen(nameleft);
  609. }
  610. ents= traverse->contents; addto= &traverse->contents; i= 0; nentrieshere= 1;
  611. for (;;) {
  612. if (!ents) break;
  613. if (!ents->entries[i].component) break;
  614. if (!strncmp(ents->entries[i].component,nameleft,n) &&
  615. !ents->entries[i].component[n]) {
  616. break;
  617. }
  618. i++;
  619. if (i < nentrieshere) continue;
  620. addto= &ents->more;
  621. ents= ents->more;
  622. nentrieshere += nentrieshere;
  623. i=0;
  624. }
  625. if (!ents) {
  626. ents= nfmalloc(sizeof(struct fdirents) +
  627. sizeof(ents->entries[0])*(nentrieshere-1));
  628. i=0;
  629. ents->entries[i].component= 0;
  630. ents->more= 0;
  631. *addto= ents;
  632. }
  633. if (!ents->entries[i].component) {
  634. p= nfmalloc(n+1);
  635. memcpy(p,nameleft,n); p[n]= 0;
  636. ents->entries[i].component= p;
  637. ents->entries[i].go= nfmalloc(sizeof(struct fdirnode));
  638. ents->entries[i].go->here= 0;
  639. ents->entries[i].go->contents= 0;
  640. if (i+1 < nentrieshere)
  641. ents->entries[i+1].component= 0;
  642. }
  643. traverse= ents->entries[i].go;
  644. nameleft= slash;
  645. }
  646. if (traverse->here) return traverse->here;
  647. traverse->here= nfmalloc(sizeof(struct filenamenode));
  648. traverse->here->packages= 0;
  649. traverse->here->flags= 0;
  650. traverse->here->divert= 0;
  651. traverse->here->statoverride= 0;
  652. traverse->here->filestat= 0;
  653. if((flags & fnn_nocopy) && name > orig_name && name[-1] == '/') {
  654. traverse->here->name = (char *)name - 1;
  655. } else {
  656. n= strlen(name)+2;
  657. if (namesarealeft < n) {
  658. newblock= m_malloc(sizeof(struct filenamesblock));
  659. alloc= 256*1024;
  660. if (alloc<n) alloc= n;
  661. newblock->data= m_malloc(alloc);
  662. newblock->next= namesarea;
  663. namesarea= newblock;
  664. namesarealeft= alloc;
  665. }
  666. namesarealeft-= n;
  667. p= namesarea->data+namesarealeft;
  668. traverse->here->name= p; *p++= '/'; strcpy(p,name);
  669. }
  670. traverse->here->next= allfiles;
  671. allfiles= traverse->here;
  672. nfiles++;
  673. return traverse->here;
  674. }
  675. /*** Code for high memory usage fast database ***/
  676. static int hash(const char *name) {
  677. int v= 0;
  678. while (*name) { v *= 1785; v += *name; name++; }
  679. return v;
  680. }
  681. struct filenamenode *findnamenode_high(const char *name,
  682. enum fnnflags flags) {
  683. struct filenamenode **pointerp, *newnode;
  684. const char *orig_name = name;
  685. /* We skip initial slashes and ./ pairs, and add our own single leading slash. */
  686. name= skip_slash_dotslash(name);
  687. pointerp= bins + (hash(name) & (BINS-1));
  688. while (*pointerp) {
  689. assert((*pointerp)->name[0] == '/');
  690. if (!strcmp((*pointerp)->name+1,name)) break;
  691. pointerp= &(*pointerp)->next;
  692. }
  693. if (*pointerp) return *pointerp;
  694. newnode= nfmalloc(sizeof(struct filenamenode));
  695. newnode->packages= 0;
  696. if((flags & fnn_nocopy) && name > orig_name && name[-1] == '/')
  697. newnode->name = (char *)name - 1;
  698. else {
  699. newnode->name= nfmalloc(strlen(name)+2);
  700. newnode->name[0]= '/'; strcpy(newnode->name+1,name);
  701. }
  702. newnode->flags= 0;
  703. newnode->next= 0;
  704. newnode->divert= 0;
  705. newnode->statoverride= 0;
  706. newnode->filestat= 0;
  707. *pointerp= newnode;
  708. nfiles++;
  709. return newnode;
  710. }
  711. /* vi: ts=8 sw=2
  712. */