1 /*
2 * filedb3.c -- part of filesys.mod
3 * low level functions for file db handling
4 *
5 * Rewritten by Fabian Knittel <fknittel@gmx.de>
6 */
7 /*
8 * Copyright (C) 1997 Robey Pointer
9 * Copyright (C) 1999 - 2021 Eggheads Development Team
10 *
11 * This program is free software; you can redistribute it and/or
12 * modify it under the terms of the GNU General Public License
13 * as published by the Free Software Foundation; either version 2
14 * of the License, or (at your option) any later version.
15 *
16 * This program is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU General Public License for more details.
20 *
21 * You should have received a copy of the GNU General Public License
22 * along with this program; if not, write to the Free Software
23 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
24 */
25
26 /* filedb structure:
27 *
28 * +---------------+ _
29 * | filedb_top | _| DB header
30 * |---------------|
31 * . .
32 * . .
33 * | ... |
34 * |---------------| _ _
35 * | filedb_header | _| Static length |
36 * |- --- - --- - -| _ |
37 * | filename | | |
38 * |- - - - - - - -| | |
39 * | description | | |
40 * |- - - - - - - -| | Dynamic length | Complete entry
41 * | channel name | | |
42 * |- - - - - - - -| | |
43 * | uploader | | |
44 * |- - - - - - - -| | |
45 * | flags_req | | |
46 * |- - - - - - - -| | |
47 * | share link | | |
48 * |- - - - - - - -| | |
49 * | buffer | _| _|
50 * |---------------|
51 * | ... |
52 * . .
53 * . .
54 *
55 * To know how long the complete entry is, you need to read out the
56 * header first. This concept allows us to have unlimited filename
57 * lengths, unlimited description lengths, etc.
58 *
59 * Buffer is an area which doesn't contain any information and which
60 * is just skipped. It only serves as a placeholder to allow entries
61 * which shrink in size to maintain their position without messing up
62 * the position of all following entries.
63 */
64
65 /* Number of databases opened simultaneously. More than 2 is
66 * be unnormal.
67 */
68 static int count = 0;
69
70
71 /*
72 * Memory management helper functions
73 */
74
75 /* Frees a filedb entry and all it's elements.
76 */
free_fdbe(filedb_entry ** fdbe)77 static void free_fdbe(filedb_entry ** fdbe)
78 {
79 if (!fdbe || !*fdbe)
80 return;
81 if ((*fdbe)->filename)
82 my_free((*fdbe)->filename);
83 if ((*fdbe)->desc)
84 my_free((*fdbe)->desc);
85 if ((*fdbe)->sharelink)
86 my_free((*fdbe)->sharelink);
87 if ((*fdbe)->chan)
88 my_free((*fdbe)->chan);
89 if ((*fdbe)->uploader)
90 my_free((*fdbe)->uploader);
91 if ((*fdbe)->flags_req)
92 my_free((*fdbe)->flags_req);
93 my_free(*fdbe);
94 }
95
96 /* Allocates and initialises a filedb entry
97 */
_malloc_fdbe(char * file,int line)98 static filedb_entry *_malloc_fdbe(char *file, int line)
99 {
100 filedb_entry *fdbe = NULL;
101
102 #ifdef DEBUG_MEM
103 /* This is a hack to access the nmalloc function with
104 * special file and line information
105 */
106 fdbe = (((void *(*)())global[0])(sizeof(filedb_entry),MODULE_NAME,file,line));
107 #else
108 fdbe = nmalloc(sizeof(filedb_entry));
109 #endif
110 egg_bzero(fdbe, sizeof(filedb_entry));
111
112 /* Mark as new, will be overwritten if necessary. */
113 fdbe->_type = TYPE_NEW;
114 return fdbe;
115 }
116
117
118 /*
119 * File locking
120 */
121
122 /* Locks the file, using fcntl. Execution is locked until we
123 * have exclusive access to it.
124 */
lockfile(FILE * fdb)125 static void lockfile(FILE *fdb)
126 {
127 struct flock fl;
128
129 fl.l_type = F_WRLCK;
130 fl.l_start = 0;
131 fl.l_whence = SEEK_SET;
132 fl.l_len = 0;
133 fcntl(fileno(fdb), F_SETLKW, &fl);
134 }
135
136 /* Unlocks the file using fcntl.
137 */
unlockfile(FILE * f)138 static void unlockfile(FILE *f)
139 {
140 struct flock fl;
141
142 fl.l_type = F_UNLCK;
143 fl.l_start = 0;
144 fl.l_whence = SEEK_SET;
145 fl.l_len = 0;
146 fcntl(fileno(f), F_SETLKW, &fl);
147 }
148
149
150 /*
151 * filedb functions
152 */
153
154 /* Copies the DB header to fdbt or just positions the file
155 * position pointer onto the first entry after the db header.
156 */
filedb_readtop(FILE * fdb,filedb_top * fdbt)157 static int filedb_readtop(FILE *fdb, filedb_top *fdbt)
158 {
159 if (fdbt) {
160 /* Read header */
161 fseek(fdb, 0L, SEEK_SET);
162 if (feof(fdb) || !fread(fdbt, sizeof *fdbt, 1, fdb) || ferror(fdb))
163 return 0;
164 } else
165 fseek(fdb, sizeof(filedb_top), SEEK_SET);
166 return 1;
167 }
168
169 /* Writes the DB header to the top of the filedb.
170 */
filedb_writetop(FILE * fdb,filedb_top * fdbt)171 static int filedb_writetop(FILE *fdb, filedb_top *fdbt)
172 {
173 fseek(fdb, 0L, SEEK_SET);
174 fwrite(fdbt, 1, sizeof(filedb_top), fdb);
175 return 1;
176 }
177
178 /* Deletes the entry at 'pos'. It just adjusts the header to
179 * mark it as 'unused' and to assign all dynamic space to
180 * the buffer.
181 */
filedb_delfile(FILE * fdb,long pos)182 static int filedb_delfile(FILE *fdb, long pos)
183 {
184 filedb_header fdh;
185
186 fseek(fdb, pos, SEEK_SET); /* Go to start of entry */
187 /* Read header */
188 if (feof(fdb) || !fread(&fdh, sizeof fdh, 1, fdb) || ferror(fdb))
189 return 0;
190 fdh.stat = FILE_UNUSED;
191
192 /* Assign all available space to buffer. Simplifies
193 * space calculation later on.
194 */
195 fdh.buffer_len += filedb_tot_dynspace(fdh);
196 filedb_zero_dynspace(fdh);
197
198 fseek(fdb, pos, SEEK_SET); /* Go back to start */
199 fwrite(&fdh, 1, sizeof(filedb_header), fdb); /* Write new header */
200 return 1;
201 }
202
203 /* Searches for a suitable place to write an entry which uses tot
204 * bytes for dynamic data.
205 *
206 * * If there is no such existing entry, it just points to the
207 * end of the DB.
208 * * If it finds an empty entry and it has enough space to fit
209 * in another entry, we split it up and only use the space we
210 * really need.
211 *
212 * Note: We can assume that empty entries' dyn_lengths are zero.
213 * Therefore we only need to check buf_len.
214 */
filedb_findempty(FILE * fdb,int tot)215 static filedb_entry *filedb_findempty(FILE *fdb, int tot)
216 {
217 filedb_entry *fdbe;
218
219 filedb_readtop(fdb, NULL);
220 fdbe = filedb_getfile(fdb, ftell(fdb), GET_HEADER);
221 while (fdbe) {
222 /* Found an existing, empty entry? */
223 if ((fdbe->stat & FILE_UNUSED) && (fdbe->buf_len >= tot)) {
224 /* Do we have enough space to split up the entry to form
225 * another empty entry? That way we would use the space
226 * more efficiently.
227 */
228 if (fdbe->buf_len > (tot + sizeof(filedb_header) + FILEDB_ESTDYN)) {
229 filedb_entry *fdbe_oe;
230
231 /* Create new entry containing the additional space */
232 fdbe_oe = malloc_fdbe();
233 fdbe_oe->stat = FILE_UNUSED;
234 fdbe_oe->pos = fdbe->pos + sizeof(filedb_header) + tot;
235 fdbe_oe->buf_len = fdbe->buf_len - tot - sizeof(filedb_header);
236 filedb_movefile(fdb, fdbe_oe->pos, fdbe_oe);
237 free_fdbe(&fdbe_oe);
238
239 /* Cut down buf_len of entry as the rest is now used in the new
240 * entry.
241 */
242 fdbe->buf_len = tot;
243 }
244 return fdbe;
245 }
246 free_fdbe(&fdbe);
247 fdbe = filedb_getfile(fdb, ftell(fdb), GET_HEADER);
248 }
249
250 /* No existing entries, so create new entry at end of DB instead. */
251 fdbe = malloc_fdbe();
252 fseeko(fdb, 0, SEEK_END);
253 fdbe->pos = ftello(fdb);
254 return fdbe;
255 }
256
257 /* Updates or creates entries and information in the filedb.
258 *
259 * * If the new entry is the same size or smaller than the original
260 * one, we use the old position and just adjust the buffer length
261 * appropriately.
262 * * If the entry is completely _new_ or if the entry's new size is
263 * _bigger_ than the old one, we search for a new position which
264 * suits our needs.
265 *
266 * Note that the available space also includes the buffer.
267 *
268 * The file pointer will _always_ position directly after the updated
269 * entry.
270 */
_filedb_updatefile(FILE * fdb,long pos,filedb_entry * fdbe,int update,char * file,int line)271 static int _filedb_updatefile(FILE *fdb, long pos, filedb_entry *fdbe,
272 int update, char *file, int line)
273 {
274 filedb_header fdh;
275 int reposition = 0;
276 int ndyntot, odyntot, nbuftot, obuftot;
277
278 egg_bzero(&fdh, sizeof(filedb_header));
279 fdh.uploaded = fdbe->uploaded;
280 fdh.size = fdbe->size;
281 fdh.stat = fdbe->stat;
282 fdh.gots = fdbe->gots;
283
284 /* Only add the buffer length if the buffer is not empty. Otherwise it
285 * would result in lots of 1 byte entries which actually don't contain
286 * any data.
287 */
288 if (fdbe->filename)
289 fdh.filename_len = strlen(fdbe->filename) + 1;
290 if (fdbe->desc)
291 fdh.desc_len = strlen(fdbe->desc) + 1;
292 if (fdbe->chan)
293 fdh.chan_len = strlen(fdbe->chan) + 1;
294 if (fdbe->uploader)
295 fdh.uploader_len = strlen(fdbe->uploader) + 1;
296 if (fdbe->flags_req)
297 fdh.flags_req_len = strlen(fdbe->flags_req) + 1;
298 if (fdbe->sharelink)
299 fdh.sharelink_len = strlen(fdbe->sharelink) + 1;
300
301 odyntot = fdbe->dyn_len; /* Old length of dynamic data */
302 obuftot = fdbe->buf_len; /* Old length of spare space */
303 ndyntot = filedb_tot_dynspace(fdh); /* New length of dynamic data */
304 nbuftot = obuftot;
305
306 if (fdbe->_type == TYPE_EXIST) {
307 /* If we only update the header, we don't need to worry about
308 * sizes and just use the old place (i.e. the place pointed
309 * to by pos).
310 */
311 if (update < UPDATE_ALL) {
312 /* Unless forced to it, we ignore new buffer sizes if we do not
313 * run in UPDATE_ALL mode.
314 */
315 if (update != UPDATE_SIZE) {
316 ndyntot = odyntot;
317 nbuftot = obuftot;
318 }
319 } else {
320 /* If we have a given/preferred position */
321 if ((pos != POS_NEW) &&
322 /* and if our new size is smaller than the old size, we
323 * just adjust the buffer length and still use the same
324 * position
325 */
326 (ndyntot <= (odyntot + obuftot))) {
327 nbuftot = (odyntot + obuftot) - ndyntot;
328 } else {
329 /* If we have an existing position, but the new entry doesn't
330 * fit into it's old home, we need to delete it before
331 * repositioning.
332 */
333 if (pos != POS_NEW)
334 filedb_delfile(fdb, pos);
335 reposition = 1;
336 }
337 }
338 } else {
339 fdbe->_type = TYPE_EXIST; /* Update type */
340 reposition = 1;
341 }
342
343 /* Search for a new home */
344 if (reposition) {
345 filedb_entry *n_fdbe;
346
347 n_fdbe = filedb_findempty(fdb, filedb_tot_dynspace(fdh));
348 fdbe->pos = pos = n_fdbe->pos;
349 /* If we create a new entry (instead of using an existing one),
350 * buf_len is zero
351 */
352 if (n_fdbe->buf_len > 0)
353 /* Note: empty entries have dyn_len set to zero, so we only
354 * need to consider buf_len.
355 */
356 nbuftot = n_fdbe->buf_len - ndyntot;
357 else
358 nbuftot = 0;
359 free_fdbe(&n_fdbe);
360 }
361
362 /* Set length of dynamic data and buffer */
363 fdbe->dyn_len = ndyntot;
364 fdbe->buf_len = fdh.buffer_len = nbuftot;
365
366 /* Write header */
367 fseek(fdb, pos, SEEK_SET);
368 fwrite(&fdh, 1, sizeof(filedb_header), fdb);
369 /* Write dynamic data */
370 if (update == UPDATE_ALL) {
371 if (fdbe->filename)
372 fwrite(fdbe->filename, 1, fdh.filename_len, fdb);
373 if (fdbe->desc)
374 fwrite(fdbe->desc, 1, fdh.desc_len, fdb);
375 if (fdbe->chan)
376 fwrite(fdbe->chan, 1, fdh.chan_len, fdb);
377 if (fdbe->uploader)
378 fwrite(fdbe->uploader, 1, fdh.uploader_len, fdb);
379 if (fdbe->flags_req)
380 fwrite(fdbe->flags_req, 1, fdh.flags_req_len, fdb);
381 if (fdbe->sharelink)
382 fwrite(fdbe->sharelink, 1, fdh.sharelink_len, fdb);
383 } else
384 fseek(fdb, ndyntot, SEEK_CUR); /* Skip over dynamic data */
385 fseek(fdb, nbuftot, SEEK_CUR); /* Skip over buffer */
386 return 0;
387 }
388
389 /* Moves an existing file entry, saved in fdbe to a new position.
390 */
_filedb_movefile(FILE * fdb,long pos,filedb_entry * fdbe,char * file,int line)391 static int _filedb_movefile(FILE *fdb, long pos, filedb_entry *fdbe,
392 char *file, int line)
393 {
394 fdbe->_type = TYPE_EXIST;
395 _filedb_updatefile(fdb, pos, fdbe, UPDATE_ALL, file, line);
396 return 0;
397 }
398
399 /* Adds a completely new file.
400 */
_filedb_addfile(FILE * fdb,filedb_entry * fdbe,char * file,int line)401 static int _filedb_addfile(FILE *fdb, filedb_entry *fdbe, char *file, int line)
402 {
403 fdbe->_type = TYPE_NEW;
404 _filedb_updatefile(fdb, POS_NEW, fdbe, UPDATE_ALL, file, line);
405 return 0;
406 }
407
408 /* Short-cut macro to read an entry from disc to memory. Only
409 * useful for filedb_getfile().
410 */
411 #define filedb_read(fdb, entry, len) \
412 { \
413 if ((len) > 0) { \
414 (entry) = nmalloc((len)); \
415 if (feof((fdb)) || \
416 !fread((entry), (len), 1, (fdb)) || \
417 ferror((fdb))) { \
418 nfree(entry); \
419 return NULL; \
420 } \
421 } \
422 }
423
424 /* Reads an entry from the fildb at the specified position. The
425 * amount of information returned depends on the get flag.
426 * It always positions the file position pointer exactly behind
427 * the entry.
428 */
_filedb_getfile(FILE * fdb,long pos,int get,char * file,int line)429 static filedb_entry *_filedb_getfile(FILE *fdb, long pos, int get,
430 char *file, int line)
431 {
432 filedb_entry *fdbe;
433 filedb_header fdh;
434
435 /* Read header */
436 fseek(fdb, pos, SEEK_SET);
437 if (feof(fdb) || !fread(&fdh, sizeof fdh, 1, fdb) || ferror(fdb))
438 return NULL;
439
440 /* Allocate memory for file db entry */
441 fdbe = _malloc_fdbe(file, line);
442
443 /* Optimisation: maybe use memcpy here? */
444 fdbe->uploaded = fdh.uploaded;
445 fdbe->size = fdh.size;
446 fdbe->stat = fdh.stat;
447 fdbe->gots = fdh.gots;
448
449 fdbe->buf_len = fdh.buffer_len;
450 fdbe->dyn_len = filedb_tot_dynspace(fdh);
451 fdbe->pos = pos; /* Save position */
452 fdbe->_type = TYPE_EXIST; /* Entry exists in DB */
453
454 /* This is useful for cases where we don't read the rest of the
455 * data, but need to know whether the file is a link.
456 */
457 if (fdh.sharelink_len > 0)
458 fdbe->stat |= FILE_ISLINK;
459 else
460 fdbe->stat &= ~FILE_ISLINK;
461
462 /* Read additional data from db */
463 if (get >= GET_FILENAME) {
464 filedb_read(fdb, fdbe->filename, fdh.filename_len);
465 } else
466 fseek(fdb, fdh.filename_len, SEEK_CUR);
467 if (get < GET_FULL || (fdh.stat & FILE_UNUSED))
468 fseek(fdb, filedb_tot_dynspace(fdh) - fdh.filename_len, SEEK_CUR);
469 else if (get == GET_FULL) {
470 filedb_read(fdb, fdbe->desc, fdh.desc_len);
471 filedb_read(fdb, fdbe->chan, fdh.chan_len);
472 filedb_read(fdb, fdbe->uploader, fdh.uploader_len);
473 filedb_read(fdb, fdbe->flags_req, fdh.flags_req_len);
474 filedb_read(fdb, fdbe->sharelink, fdh.sharelink_len);
475 }
476 fseek(fdb, fdh.buffer_len, SEEK_CUR); /* Skip buffer */
477 return fdbe; /* Return the ready structure */
478 }
479
480 /* Searches the filedb for a file matching the specified mask, starting
481 * at position 'pos'. The first matching file is returned.
482 */
_filedb_matchfile(FILE * fdb,long pos,char * match,char * file,int line)483 static filedb_entry *_filedb_matchfile(FILE *fdb, long pos, char *match,
484 char *file, int line)
485 {
486 filedb_entry *fdbe = NULL;
487
488 fseek(fdb, pos, SEEK_SET);
489 while (!feof(fdb)) {
490 pos = ftell(fdb);
491 fdbe = filedb_getfile(fdb, pos, GET_FILENAME);
492 if (fdbe) {
493 if (!(fdbe->stat & FILE_UNUSED) && /* Not unused? */
494 wild_match_file(match, fdbe->filename)) { /* Matches our mask? */
495 free_fdbe(&fdbe);
496 fdbe = _filedb_getfile(fdb, pos, GET_FULL, file, line); /* Save all data now */
497 return fdbe;
498 }
499 free_fdbe(&fdbe);
500 }
501 }
502 return NULL;
503 }
504
505 /* Throws out all entries marked as unused, by walking through the
506 * filedb and moving all good ones towards the top.
507 */
filedb_cleanup(FILE * fdb)508 static void filedb_cleanup(FILE *fdb)
509 {
510 long oldpos, newpos, temppos;
511 filedb_entry *fdbe = NULL;
512
513 filedb_readtop(fdb, NULL); /* Skip DB header */
514 oldpos = ftell(fdb);
515 fseek(fdb, oldpos, SEEK_SET); /* Go to beginning */
516 while (!feof(fdb)) { /* Loop until EOF */
517 fdbe = filedb_getfile(fdb, oldpos, GET_HEADER); /* Read header */
518 if (fdbe) {
519 if (fdbe->stat & FILE_UNUSED) { /* Found dirt! */
520 free_fdbe(&fdbe);
521 while (!feof(fdb)) { /* Loop until EOF */
522 newpos = ftell(fdb);
523 fdbe = filedb_getfile(fdb, newpos, GET_FULL); /* Read next entry */
524 if (!fdbe)
525 break;
526 if (!(fdbe->stat & FILE_UNUSED)) { /* Not unused? */
527 temppos = ftell(fdb);
528 filedb_movefile(fdb, oldpos, fdbe); /* Move to top */
529 oldpos = ftell(fdb);
530 fseek(fdb, temppos, SEEK_SET);
531 }
532 free_fdbe(&fdbe);
533 }
534 } else {
535 free_fdbe(&fdbe);
536 oldpos = ftell(fdb);
537 }
538 }
539 }
540 if (ftruncate(fileno(fdb), oldpos) == -1) { /* Shorten file */
541 putlog(LOG_MISC, "*", "FILESYS: Error truncating file.");
542 }
543 }
544
545 /* Merges empty entries to one big entry, if they directly
546 * follow each other. Does this for the complete DB.
547 * This considerably speeds up several actions performed on
548 * the db.
549 */
filedb_mergeempty(FILE * fdb)550 static void filedb_mergeempty(FILE *fdb)
551 {
552 filedb_entry *fdbe_t, *fdbe_i;
553 int modified;
554
555 filedb_readtop(fdb, NULL);
556 while (!feof(fdb)) {
557 fdbe_t = filedb_getfile(fdb, ftell(fdb), GET_HEADER);
558 if (fdbe_t) {
559 if (fdbe_t->stat & FILE_UNUSED) {
560 modified = 0;
561 fdbe_i = filedb_getfile(fdb, ftell(fdb), GET_HEADER);
562 while (fdbe_i) {
563 /* Is this entry in use? */
564 if (!(fdbe_i->stat & FILE_UNUSED))
565 break; /* It is, exit loop. */
566
567 /* Woohoo, found an empty entry. Append it's space to
568 * our target entry's buffer space.
569 */
570 fdbe_t->buf_len += sizeof(filedb_header) + fdbe_i->buf_len;
571 modified++;
572 free_fdbe(&fdbe_i);
573 /* Get next file entry */
574 fdbe_i = filedb_getfile(fdb, ftell(fdb), GET_HEADER);
575 }
576
577 /* Did we exit the loop because of a used entry? */
578 if (fdbe_i) {
579 free_fdbe(&fdbe_i);
580 /* Did we find any empty entries before? */
581 if (modified)
582 filedb_updatefile(fdb, fdbe_t->pos, fdbe_t, UPDATE_SIZE);
583 /* ... or because we hit EOF? */
584 } else {
585 /* Truncate trailing empty entries and exit. */
586 if (ftruncate(fileno(fdb), fdbe_t->pos) == -1) {
587 putlog(LOG_MISC, "*", "FILESYS: Error truncating file");
588 }
589 free_fdbe(&fdbe_t);
590 return;
591 }
592 }
593 free_fdbe(&fdbe_t);
594 }
595 }
596 }
597
598 /* Returns the filedb entry matching the filename 'fn' in
599 * directory 'dir'.
600 */
filedb_getentry(char * dir,char * fn)601 static filedb_entry *filedb_getentry(char *dir, char *fn)
602 {
603 FILE *fdb;
604 filedb_entry *fdbe = NULL;
605
606 fdb = filedb_open(dir, 0);
607 if (fdb) {
608 filedb_readtop(fdb, NULL);
609 fdbe = filedb_matchfile(fdb, ftell(fdb), fn);
610 filedb_close(fdb);
611 }
612 return fdbe;
613 }
614
615 /* Initialises a new filedb by writing the db header, etc.
616 */
filedb_initdb(FILE * fdb)617 static void filedb_initdb(FILE *fdb)
618 {
619 filedb_top fdbt;
620
621 fdbt.version = FILEDB_NEWEST_VER;
622 fdbt.timestamp = now;
623 filedb_writetop(fdb, &fdbt);
624 }
625
filedb_timestamp(FILE * fdb)626 static void filedb_timestamp(FILE *fdb)
627 {
628 filedb_top fdbt;
629
630 filedb_readtop(fdb, &fdbt);
631 fdbt.timestamp = time(NULL);
632 filedb_writetop(fdb, &fdbt);
633 }
634
635 /* Updates the specified filedb in several ways:
636 *
637 * 1. Adds all new files from the directory to the db.
638 * 2. Removes all stale entries from the db.
639 * 3. Optimises the db.
640 */
filedb_update(char * path,FILE * fdb,int sort)641 static void filedb_update(char *path, FILE *fdb, int sort)
642 {
643 struct dirent *dd = NULL;
644 struct stat st;
645 filedb_entry *fdbe = NULL;
646 DIR *dir = NULL;
647 long where = 0;
648 char *name = NULL, *s = NULL;
649
650 /*
651 * FIRST: make sure every real file is in the database
652 */
653 dir = opendir(path);
654 if (dir == NULL) {
655 putlog(LOG_MISC, "*", FILES_NOUPDATE);
656 return;
657 }
658 dd = readdir(dir);
659 while (dd != NULL) {
660 malloc_strcpy(name, dd->d_name);
661 if (name[0] != '.') {
662 s = nmalloc(strlen(path) + strlen(name) + 2);
663 sprintf(s, "%s/%s", path, name);
664 stat(s, &st);
665 my_free(s);
666 filedb_readtop(fdb, NULL);
667 fdbe = filedb_matchfile(fdb, ftell(fdb), name);
668 if (!fdbe) {
669 /* new file! */
670 fdbe = malloc_fdbe();
671 malloc_strcpy(fdbe->filename, name);
672 malloc_strcpy(fdbe->uploader, botnetnick);
673 fdbe->uploaded = now;
674 fdbe->size = st.st_size;
675 if (S_ISDIR(st.st_mode))
676 fdbe->stat |= FILE_DIR;
677 filedb_addfile(fdb, fdbe);
678 } else if (fdbe->size != st.st_size) {
679 /* update size if needed */
680 fdbe->size = st.st_size;
681 filedb_updatefile(fdb, fdbe->pos, fdbe, UPDATE_HEADER);
682 }
683 free_fdbe(&fdbe);
684 }
685 dd = readdir(dir);
686 }
687 if (name)
688 my_free(name);
689 closedir(dir);
690
691 /*
692 * SECOND: make sure every db file is real
693 */
694 filedb_readtop(fdb, NULL);
695 fdbe = filedb_getfile(fdb, ftell(fdb), GET_FILENAME);
696 while (fdbe) {
697 where = ftell(fdb);
698 if (!(fdbe->stat & FILE_UNUSED) && !(fdbe->stat & FILE_ISLINK) &&
699 fdbe->filename) {
700 s = nmalloc(strlen(path) + 1 + strlen(fdbe->filename) + 1);
701 sprintf(s, "%s/%s", path, fdbe->filename);
702 if (stat(s, &st) != 0)
703 /* gone file */
704 filedb_delfile(fdb, fdbe->pos);
705 my_free(s);
706 }
707 free_fdbe(&fdbe);
708 fdbe = filedb_getfile(fdb, where, GET_FILENAME);
709 }
710
711 /*
712 * THIRD: optimise database
713 *
714 * Instead of sorting, we only clean up the db, because sorting is now
715 * done on-the-fly when we display the file list.
716 */
717 if (sort)
718 filedb_cleanup(fdb); /* Cleanup DB */
719 filedb_timestamp(fdb); /* Write new timestamp */
720 }
721
722 /* Converts all slashes to dots. Returns an allocated buffer, so
723 * do not forget to FREE it after use.
724 */
make_point_path(char * path)725 static char *make_point_path(char *path)
726 {
727 char *s2 = NULL, *p = NULL;
728
729 malloc_strcpy(s2, path);
730 if (s2[strlen(s2) - 1] == '/')
731 s2[strlen(s2) - 1] = 0; /* remove trailing '/' */
732 p = s2;
733 while (*p++)
734 if (*p == '/')
735 *p = '.';
736 return s2;
737 }
738
739 /* Opens the filedb responsible to the specified directory.
740 */
filedb_open(char * path,int sort)741 static FILE *filedb_open(char *path, int sort)
742 {
743 char *s, *npath;
744 FILE *fdb;
745 filedb_top fdbt;
746 struct stat st;
747
748 if (count >= 2)
749 putlog(LOG_MISC, "*", "(@) warning: %d open filedb's", count);
750 npath = nmalloc(strlen(dccdir) + strlen(path) + 1);
751 simple_sprintf(npath, "%s%s", dccdir, path);
752 /* Use alternate filename if requested */
753 if (filedb_path[0]) {
754 char *s2;
755
756 s2 = make_point_path(path);
757 s = nmalloc(strlen(filedb_path) + strlen(s2) + 8);
758 simple_sprintf(s, "%sfiledb.%s", filedb_path, s2);
759 my_free(s2);
760 } else {
761 s = nmalloc(strlen(npath) + 10);
762 simple_sprintf(s, "%s/.filedb", npath);
763 }
764 fdb = fopen(s, "r+b");
765 if (!fdb) {
766 if (convert_old_files(npath, s)) {
767 fdb = fopen(s, "r+b");
768 if (fdb == NULL) {
769 putlog(LOG_MISC, "*", FILES_NOCONVERT, npath);
770 my_free(s);
771 my_free(npath);
772 return NULL;
773 }
774 lockfile(fdb);
775 filedb_update(npath, fdb, sort);
776 count++;
777 my_free(s);
778 my_free(npath);
779 return fdb;
780 } else {
781 filedb_top fdbt;
782
783 /* Create new database and fix it up */
784 fdb = fopen(s, "w+b");
785 if (!fdb) {
786 my_free(s);
787 my_free(npath);
788 return NULL;
789 }
790 lockfile(fdb);
791 fdbt.version = FILEDB_NEWEST_VER;
792 fdbt.timestamp = now;
793 filedb_writetop(fdb, &fdbt);
794 filedb_update(npath, fdb, sort);
795 count++;
796 my_free(s);
797 my_free(npath);
798 return fdb;
799 }
800 }
801
802 lockfile(fdb); /* Lock it from other bots */
803 filedb_readtop(fdb, &fdbt);
804 if (fdbt.version < FILEDB_NEWEST_VER) {
805 if (!convert_old_db(&fdb, s)) {
806 /* Conversion failed. Unlock file again and error out.
807 * (convert_old_db() could have modified fdb, so check
808 * for fdb != NULL.)
809 */
810 if (fdb)
811 unlockfile(fdb);
812 my_free(npath);
813 my_free(s);
814 return NULL;
815 }
816 filedb_update(npath, fdb, sort);
817 }
818 stat(npath, &st);
819 /* Update filedb if:
820 * + it's been 6 hours since it was last updated
821 * + the directory has been visibly modified since then
822 * (6 hours may be a bit often)
823 */
824 if (sort || ((now - fdbt.timestamp) > (6 * 3600)) ||
825 (fdbt.timestamp < st.st_mtime) || (fdbt.timestamp < st.st_ctime))
826 /* File database isn't up-to-date! */
827 filedb_update(npath, fdb, sort & 1);
828 else if ((now - fdbt.timestamp) > 300)
829 filedb_mergeempty(fdb);
830
831 count++;
832 my_free(npath);
833 my_free(s);
834 return fdb;
835 }
836
837 /* Closes the filedb. Also removes the lock and updates the
838 * timestamp.
839 */
filedb_close(FILE * fdb)840 static void filedb_close(FILE *fdb)
841 {
842 filedb_timestamp(fdb);
843 fseeko(fdb, 0, SEEK_END);
844 count--;
845 unlockfile(fdb);
846 fclose(fdb);
847 }
848
849 /* Adds information for a newly added file. Actually the name
850 * is misleading, as the file is added in filedb_open() and we
851 * only add information in here.
852 */
filedb_add(FILE * fdb,char * filename,char * nick)853 static void filedb_add(FILE *fdb, char *filename, char *nick)
854 {
855 filedb_entry *fdbe = NULL;
856
857 filedb_readtop(fdb, NULL);
858 /* When the filedb was opened, a record was already created. */
859 fdbe = filedb_matchfile(fdb, ftell(fdb), filename);
860 if (!fdbe)
861 return;
862 my_free(fdbe->uploader);
863 malloc_strcpy(fdbe->uploader, nick);
864 fdbe->uploaded = now;
865 filedb_updatefile(fdb, fdbe->pos, fdbe, UPDATE_ALL);
866 free_fdbe(&fdbe);
867 }
868
869 /* Outputs a sorted list of files/directories matching the mask,
870 * to idx.
871 */
filedb_ls(FILE * fdb,int idx,char * mask,int showall)872 static void filedb_ls(FILE *fdb, int idx, char *mask, int showall)
873 {
874 int ok = 0, cnt = 0, is = 0;
875 char s1[81], *p = NULL;
876 struct flag_record user = { FR_GLOBAL | FR_CHAN, 0, 0, 0, 0, 0 };
877 filedb_entry *fdbe = NULL;
878 filelist_t *flist = NULL;
879
880 flist = filelist_new();
881 filedb_readtop(fdb, NULL);
882 fdbe = filedb_getfile(fdb, ftell(fdb), GET_FULL);
883 while (fdbe) {
884 ok = 1;
885 if (fdbe->stat & FILE_UNUSED)
886 ok = 0;
887 if (ok && (fdbe->stat & FILE_DIR) && fdbe->flags_req) {
888 /* Check permissions */
889 struct flag_record req = { FR_GLOBAL | FR_CHAN, 0, 0, 0, 0, 0 };
890
891 break_down_flags(fdbe->flags_req, &req, NULL);
892 get_user_flagrec(dcc[idx].user, &user, dcc[idx].u.file->chat->con_chan);
893 if (!flagrec_ok(&req, &user)) {
894 ok = 0;
895 }
896 }
897 if (ok)
898 is = 1;
899 if (ok && !wild_match_file(mask, fdbe->filename))
900 ok = 0;
901 if (ok && (fdbe->stat & FILE_HIDDEN) && !(showall))
902 ok = 0;
903 if (ok) {
904 /* Display it! */
905 if (cnt == 0) {
906 dprintf(idx, FILES_LSHEAD1);
907 dprintf(idx, FILES_LSHEAD2);
908 }
909 filelist_add(flist, fdbe->filename);
910 if (fdbe->stat & FILE_DIR) {
911 char *s2 = NULL, *s3 = NULL;
912
913 /* Too long? */
914 if (strlen(fdbe->filename) > 45) {
915 /* Display the filename on its own line. */
916 s2 = nmalloc(strlen(fdbe->filename) + 3);
917 sprintf(s2, "%s/\n", fdbe->filename);
918 filelist_addout(flist, s2);
919 my_free(s2);
920 } else {
921 s2 = nmalloc(strlen(fdbe->filename) + 2);
922 sprintf(s2, "%s/", fdbe->filename);
923 }
924 /* Note: You have to keep the sprintf and the nmalloc statements
925 * in sync, i.e. always check that you allocate enough
926 * memory.
927 */
928 if ((fdbe->flags_req) && (user.global &(USER_MASTER | USER_JANITOR))) {
929 s3 = nmalloc(42 + strlen(s2 ? s2 : "") + 6 +
930 strlen(FILES_REQUIRES) + strlen(fdbe->flags_req) + 1 +
931 strlen(fdbe->chan ? fdbe->chan : "") + 1);
932 sprintf(s3, "%-30s <DIR%s> (%s %s%s%s)\n", s2,
933 fdbe->stat & FILE_SHARE ?
934 " SHARE" : "", FILES_REQUIRES, fdbe->flags_req,
935 fdbe->chan ? " " : "", fdbe->chan ? fdbe->chan : "");
936 } else {
937 s3 = nmalloc(38 + strlen(s2 ? s2 : ""));
938 sprintf(s3, "%-30s <DIR>\n", s2 ? s2 : "");
939 }
940 if (s2)
941 my_free(s2);
942 filelist_addout(flist, s3);
943 my_free(s3);
944 } else {
945 char s2[41], t[50], *s3 = NULL, *s4;
946
947 s2[0] = 0;
948 if (showall) {
949 if (fdbe->stat & FILE_SHARE)
950 strcat(s2, " (shr)");
951 if (fdbe->stat & FILE_HIDDEN)
952 strcat(s2, " (hid)");
953 }
954 strftime(t, 10, "%d%b%Y", localtime(&fdbe->uploaded));
955 if (fdbe->size < 1024)
956 sprintf(s1, "%5d", fdbe->size);
957 else
958 sprintf(s1, "%4dk", (int) (fdbe->size / 1024));
959 if (fdbe->sharelink)
960 strcpy(s1, " ");
961 /* Too long? */
962 if (strlen(fdbe->filename) > 30) {
963 s3 = nmalloc(strlen(fdbe->filename) + 2);
964 sprintf(s3, "%s\n", fdbe->filename);
965 filelist_addout(flist, s3);
966 my_free(s3);
967 /* Causes filename to be displayed on its own line */
968 } else
969 malloc_strcpy(s3, fdbe->filename);
970 s4 = nmalloc(69 + strlen(s3 ? s3 : "") + strlen(s1) +
971 strlen(fdbe->uploader) + strlen(t) + strlen(s2));
972 sprintf(s4, "%-30s %s %-9s (%s) %6d%s\n", s3 ? s3 : "", s1,
973 fdbe->uploader, t, fdbe->gots, s2);
974 if (s3)
975 my_free(s3);
976 filelist_addout(flist, s4);
977 my_free(s4);
978 if (fdbe->sharelink) {
979 s4 = nmalloc(9 + strlen(fdbe->sharelink));
980 sprintf(s4, " --> %s\n", fdbe->sharelink);
981 filelist_addout(flist, s4);
982 my_free(s4);
983 }
984 }
985 if (fdbe->desc) {
986 p = strchr(fdbe->desc, '\n');
987 while (p != NULL) {
988 *p = 0;
989 if ((fdbe->desc)[0]) {
990 char *sd;
991
992 sd = nmalloc(strlen(fdbe->desc) + 5);
993 sprintf(sd, " %s\n", fdbe->desc);
994 filelist_addout(flist, sd);
995 my_free(sd);
996 }
997 strcpy(fdbe->desc, p + 1);
998 p = strchr(fdbe->desc, '\n');
999 }
1000 if ((fdbe->desc)[0]) {
1001 char *sd;
1002
1003 sd = nmalloc(strlen(fdbe->desc) + 5);
1004 sprintf(sd, " %s\n", fdbe->desc);
1005 filelist_addout(flist, sd);
1006 my_free(sd);
1007 }
1008 }
1009 cnt++;
1010 }
1011 free_fdbe(&fdbe);
1012 fdbe = filedb_getfile(fdb, ftell(fdb), GET_FULL);
1013 }
1014 if (is == 0)
1015 dprintf(idx, FILES_NOFILES);
1016 else if (cnt == 0)
1017 dprintf(idx, FILES_NOMATCH);
1018 else {
1019 filelist_sort(flist);
1020 filelist_idxshow(flist, idx);
1021 dprintf(idx, "--- %d file%s.\n", cnt, cnt != 1 ? "s" : "");
1022 }
1023 filelist_free(flist);
1024 }
1025
remote_filereq(int idx,char * from,char * file)1026 static void remote_filereq(int idx, char *from, char *file)
1027 {
1028 char *p = NULL, *what = NULL, *dir = NULL,
1029 *s1 = NULL, *reject = NULL, *s = NULL;
1030 FILE *fdb = NULL;
1031 int i = 0;
1032 filedb_entry *fdbe = NULL;
1033
1034 malloc_strcpy(what, file);
1035 p = strrchr(what, '/');
1036 if (p) {
1037 *p = 0;
1038 malloc_strcpy(dir, what);
1039 strcpy(what, p + 1);
1040 } else {
1041 malloc_strcpy(dir, "");
1042 }
1043 fdb = filedb_open(dir, 0);
1044 if (!fdb) {
1045 reject = FILES_DIRDNE;
1046 } else {
1047 filedb_readtop(fdb, NULL);
1048 fdbe = filedb_matchfile(fdb, ftell(fdb), what);
1049 filedb_close(fdb);
1050 if (!fdbe) {
1051 reject = FILES_FILEDNE;
1052 } else {
1053 if ((!(fdbe->stat & FILE_SHARE)) ||
1054 (fdbe->stat & (FILE_HIDDEN | FILE_DIR)))
1055 reject = FILES_NOSHARE;
1056 else {
1057 s1 = nmalloc(strlen(dccdir) + strlen(dir) + strlen(what) + 2);
1058 sprintf(s1, "%s%s%s%s", dccdir, dir, dir[0] ? "/" : "", what);
1059 i = raw_dcc_send(s1, "*remote", FILES_REMOTE);
1060 if (i > 0) {
1061 reject = FILES_SENDERR;
1062 }
1063 my_free(s1);
1064 }
1065 free_fdbe(&fdbe);
1066 }
1067 }
1068 s1 = nmalloc(strlen(botnetnick) + strlen(dir) + strlen(what) + 3);
1069 simple_sprintf(s1, "%s:%s/%s", botnetnick, dir, what);
1070 if (reject) {
1071 botnet_send_filereject(idx, s1, from, reject);
1072 my_free(s1);
1073 my_free(what);
1074 my_free(dir);
1075 return;
1076 }
1077 /* Grab info from dcc struct and bounce real request across net */
1078 i = dcc_total - 1;
1079 #ifdef IPV6
1080 s = nmalloc(INET6_ADDRSTRLEN);
1081 getdccaddr(&dcc[i].sockname, s, INET6_ADDRSTRLEN);
1082 #else
1083 s = nmalloc(INET_ADDRSTRLEN);
1084 getdccaddr(&dcc[i].sockname, s, INET_ADDRSTRLEN);
1085 #endif
1086 simple_sprintf(s, "%s %u %d", s, dcc[i].port, dcc[i].u.xfer->length);
1087 botnet_send_filesend(idx, s1, from, s);
1088 putlog(LOG_FILES, "*", FILES_REMOTEREQ, dir, dir[0] ? "/" : "", what);
1089 my_free(s1);
1090 my_free(s);
1091 my_free(what);
1092 my_free(dir);
1093 }
1094
1095
1096 /*
1097 * Tcl functions
1098 */
1099
filedb_getdesc(char * dir,char * fn,char ** desc)1100 static void filedb_getdesc(char *dir, char *fn, char **desc)
1101 {
1102 filedb_entry *fdbe = NULL;
1103
1104 fdbe = filedb_getentry(dir, fn);
1105 if (fdbe) {
1106 if (fdbe->desc) {
1107 *desc = nmalloc(strlen(fdbe->desc) + 1);
1108 strcpy(*desc, fdbe->desc);
1109 }
1110 free_fdbe(&fdbe);
1111 } else
1112 *desc = NULL;
1113 }
1114
filedb_getowner(char * dir,char * fn,char ** owner)1115 static void filedb_getowner(char *dir, char *fn, char **owner)
1116 {
1117 filedb_entry *fdbe = NULL;
1118
1119 fdbe = filedb_getentry(dir, fn);
1120 if (fdbe) {
1121 *owner = nmalloc(strlen(fdbe->uploader) + 1);
1122 strcpy(*owner, fdbe->uploader);
1123 free_fdbe(&fdbe);
1124 } else
1125 *owner = NULL;
1126 }
1127
filedb_getgots(char * dir,char * fn)1128 static int filedb_getgots(char *dir, char *fn)
1129 {
1130 filedb_entry *fdbe = NULL;
1131 int gots = 0;
1132
1133 fdbe = filedb_getentry(dir, fn);
1134 if (fdbe) {
1135 gots = fdbe->gots;
1136 free_fdbe(&fdbe);
1137 }
1138 return gots;
1139 }
1140
filedb_setdesc(char * dir,char * fn,char * desc)1141 static void filedb_setdesc(char *dir, char *fn, char *desc)
1142 {
1143 filedb_entry *fdbe = NULL;
1144 FILE *fdb = NULL;
1145
1146 fdb = filedb_open(dir, 0);
1147 if (!fdb)
1148 return;
1149 filedb_readtop(fdb, NULL);
1150 fdbe = filedb_matchfile(fdb, ftell(fdb), fn);
1151 if (fdbe) {
1152 my_free(fdbe->desc);
1153 malloc_strcpy(fdbe->desc, desc);
1154 filedb_updatefile(fdb, fdbe->pos, fdbe, UPDATE_ALL);
1155 free_fdbe(&fdbe);
1156 }
1157 filedb_close(fdb);
1158 }
1159
filedb_setowner(char * dir,char * fn,char * owner)1160 static void filedb_setowner(char *dir, char *fn, char *owner)
1161 {
1162 filedb_entry *fdbe = NULL;
1163 FILE *fdb = NULL;
1164
1165 fdb = filedb_open(dir, 0);
1166 if (!fdb)
1167 return;
1168 filedb_readtop(fdb, NULL);
1169 fdbe = filedb_matchfile(fdb, ftell(fdb), fn);
1170 if (fdbe) {
1171 my_free(fdbe->uploader);
1172 malloc_strcpy(fdbe->uploader, owner);
1173 filedb_updatefile(fdb, fdbe->pos, fdbe, UPDATE_ALL);
1174 free_fdbe(&fdbe);
1175 }
1176 filedb_close(fdb);
1177 }
1178
filedb_setlink(char * dir,char * fn,char * link)1179 static void filedb_setlink(char *dir, char *fn, char *link)
1180 {
1181 filedb_entry *fdbe = NULL;
1182 FILE *fdb = NULL;
1183
1184 fdb = filedb_open(dir, 0);
1185 if (!fdb)
1186 return;
1187 filedb_readtop(fdb, NULL);
1188 fdbe = filedb_matchfile(fdb, ftell(fdb), fn);
1189 if (fdbe) {
1190 /* Change existing one? */
1191 if ((fdbe->stat & FILE_DIR) || !fdbe->sharelink)
1192 return;
1193 if (!link || !link[0])
1194 filedb_delfile(fdb, fdbe->pos);
1195 else {
1196 my_free(fdbe->sharelink);
1197 malloc_strcpy(fdbe->sharelink, link);
1198 filedb_updatefile(fdb, fdbe->pos, fdbe, UPDATE_ALL);
1199 }
1200 free_fdbe(&fdbe);
1201 return;
1202 }
1203
1204 fdbe = malloc_fdbe();
1205 malloc_strcpy(fdbe->uploader, botnetnick);
1206 malloc_strcpy(fdbe->filename, fn);
1207 malloc_strcpy(fdbe->sharelink, link);
1208 fdbe->uploaded = now;
1209 filedb_addfile(fdb, fdbe);
1210 free_fdbe(&fdbe);
1211 filedb_close(fdb);
1212 }
1213
filedb_getlink(char * dir,char * fn,char ** link)1214 static void filedb_getlink(char *dir, char *fn, char **link)
1215 {
1216 filedb_entry *fdbe = NULL;
1217
1218 fdbe = filedb_getentry(dir, fn);
1219 if (fdbe && (!(fdbe->stat & FILE_DIR))) {
1220 malloc_strcpy(*link, fdbe->sharelink);
1221 } else
1222 *link = NULL;
1223 if (fdbe)
1224 free_fdbe(&fdbe);
1225 return;
1226 }
1227
filedb_getfiles(Tcl_Interp * irp,char * dir)1228 static void filedb_getfiles(Tcl_Interp *irp, char *dir)
1229 {
1230 FILE *fdb;
1231 filedb_entry *fdbe;
1232
1233 fdb = filedb_open(dir, 0);
1234 if (!fdb)
1235 return;
1236 filedb_readtop(fdb, NULL);
1237 while (!feof(fdb)) {
1238 fdbe = filedb_getfile(fdb, ftell(fdb), GET_FILENAME);
1239 if (fdbe) {
1240 if (!(fdbe->stat & (FILE_DIR | FILE_UNUSED)))
1241 Tcl_AppendElement(irp, fdbe->filename);
1242 free_fdbe(&fdbe);
1243 }
1244 }
1245 filedb_close(fdb);
1246 }
1247
filedb_getdirs(Tcl_Interp * irp,char * dir)1248 static void filedb_getdirs(Tcl_Interp *irp, char *dir)
1249 {
1250 FILE *fdb;
1251 filedb_entry *fdbe;
1252
1253 fdb = filedb_open(dir, 0);
1254 if (!fdb)
1255 return;
1256 filedb_readtop(fdb, NULL);
1257 while (!feof(fdb)) {
1258 fdbe = filedb_getfile(fdb, ftell(fdb), GET_FILENAME);
1259 if (fdbe) {
1260 if ((!(fdbe->stat & FILE_UNUSED)) && (fdbe->stat & FILE_DIR))
1261 Tcl_AppendElement(irp, fdbe->filename);
1262 free_fdbe(&fdbe);
1263 }
1264 }
1265 filedb_close(fdb);
1266 }
1267
filedb_change(char * dir,char * fn,int what)1268 static void filedb_change(char *dir, char *fn, int what)
1269 {
1270 FILE *fdb;
1271 filedb_entry *fdbe;
1272 int changed = 0;
1273
1274 fdb = filedb_open(dir, 0);
1275 if (!fdb)
1276 return;
1277 filedb_readtop(fdb, NULL);
1278 fdbe = filedb_matchfile(fdb, ftell(fdb), fn);
1279 if (fdbe) {
1280 if (!(fdbe->stat & FILE_DIR)) {
1281 switch (what) {
1282 case FILEDB_SHARE:
1283 fdbe->stat |= FILE_SHARE;
1284 break;
1285 case FILEDB_UNSHARE:
1286 fdbe->stat &= ~FILE_SHARE;
1287 break;
1288 }
1289 changed = 1;
1290 }
1291 switch (what) {
1292 case FILEDB_HIDE:
1293 fdbe->stat |= FILE_HIDDEN;
1294 changed = 1;
1295 break;
1296 case FILEDB_UNHIDE:
1297 fdbe->stat &= ~FILE_HIDDEN;
1298 changed = 1;
1299 break;
1300 }
1301 if (changed)
1302 filedb_updatefile(fdb, fdbe->pos, fdbe, UPDATE_HEADER);
1303 free_fdbe(&fdbe);
1304 }
1305 filedb_close(fdb);
1306 }
1307