/* Throws out all entries marked as unused, by walking through the * filedb and moving all good ones towards the top. */ static void filedb_cleanup(FILE *fdb) { long oldpos, newpos, temppos; filedb_entry *fdbe = NULL; filedb_readtop(fdb, NULL); /* Skip DB header */ newpos = temppos = oldpos = ftell(fdb); fseek(fdb, oldpos, SEEK_SET); /* Go to beginning */ while (!feof(fdb)) { /* Loop until EOF */ fdbe = filedb_getfile(fdb, oldpos, GET_HEADER); /* Read header */ if (fdbe) { if (fdbe->stat & FILE_UNUSED) { /* Found dirt! */ free_fdbe(&fdbe); while (!feof(fdb)) { /* Loop until EOF */ newpos = ftell(fdb); fdbe = filedb_getfile(fdb, newpos, GET_FULL); /* Read next entry */ if (!fdbe) break; if (!(fdbe->stat & FILE_UNUSED)) { /* Not unused? */ temppos = ftell(fdb); filedb_movefile(fdb, oldpos, fdbe); /* Move to top */ oldpos = ftell(fdb); fseek(fdb, temppos, SEEK_SET); } free_fdbe(&fdbe); } } else { free_fdbe(&fdbe); oldpos = ftell(fdb); } } } ftruncate(fileno(fdb), oldpos); /* Shorten file */ }
/* Merges empty entries to one big entry, if they directly * follow each other. Does this for the complete DB. * This considerably speeds up several actions performed on * the db. */ static void filedb_mergeempty(FILE *fdb) { filedb_entry *fdbe_t, *fdbe_i; int modified; filedb_readtop(fdb, NULL); while (!feof(fdb)) { fdbe_t = filedb_getfile(fdb, ftell(fdb), GET_HEADER); if (fdbe_t) { if (fdbe_t->stat & FILE_UNUSED) { modified = 0; fdbe_i = filedb_getfile(fdb, ftell(fdb), GET_HEADER); while (fdbe_i) { /* Is this entry in use? */ if (!(fdbe_i->stat & FILE_UNUSED)) break; /* It is, exit loop. */ /* Woohoo, found an empty entry. Append it's space to * our target entry's buffer space. */ fdbe_t->buf_len += sizeof(filedb_header) + fdbe_i->buf_len; modified++; free_fdbe(&fdbe_i); /* Get next file entry */ fdbe_i = filedb_getfile(fdb, ftell(fdb), GET_HEADER); } /* Did we exit the loop because of a used entry? */ if (fdbe_i) { free_fdbe(&fdbe_i); /* Did we find any empty entries before? */ if (modified) filedb_updatefile(fdb, fdbe_t->pos, fdbe_t, UPDATE_SIZE); /* ... or because we hit EOF? */ } else { /* Truncate trailing empty entries and exit. */ ftruncate(fileno(fdb), fdbe_t->pos); free_fdbe(&fdbe_t); return; } } free_fdbe(&fdbe_t); } } }
/* Searches for a suitable place to write an entry which uses tot * bytes for dynamic data. * * * If there is no such existing entry, it just points to the * end of the DB. * * If it finds an empty entry and it has enough space to fit * in another entry, we split it up and only use the space we * really need. * * Note: We can assume that empty entries' dyn_lengths are zero. * Therefore we only need to check buf_len. */ static filedb_entry *filedb_findempty(FILE *fdb, int tot) { filedb_entry *fdbe; filedb_readtop(fdb, NULL); fdbe = filedb_getfile(fdb, ftell(fdb), GET_HEADER); while (fdbe) { /* Found an existing, empty entry? */ if ((fdbe->stat & FILE_UNUSED) && (fdbe->buf_len >= tot)) { /* Do we have enough space to split up the entry to form * another empty entry? That way we would use the space * more efficiently. */ if (fdbe->buf_len > (tot + sizeof(filedb_header) + FILEDB_ESTDYN)) { filedb_entry *fdbe_oe; /* Create new entry containing the additional space */ fdbe_oe = malloc_fdbe(); fdbe_oe->stat = FILE_UNUSED; fdbe_oe->pos = fdbe->pos + sizeof(filedb_header) + tot; fdbe_oe->buf_len = fdbe->buf_len - tot - sizeof(filedb_header); filedb_movefile(fdb, fdbe_oe->pos, fdbe_oe); free_fdbe(&fdbe_oe); /* Cut down buf_len of entry as the rest is now used in the new * entry. */ fdbe->buf_len = tot; } return fdbe; } free_fdbe(&fdbe); fdbe = filedb_getfile(fdb, ftell(fdb), GET_HEADER); } /* No existing entries, so create new entry at end of DB instead. */ fdbe = malloc_fdbe(); fseek(fdb, 0L, SEEK_END); fdbe->pos = ftell(fdb); return fdbe; }
static void filedb_getdirs(Tcl_Interp *irp, char *dir) { FILE *fdb; filedb_entry *fdbe; fdb = filedb_open(dir, 0); if (!fdb) return; filedb_readtop(fdb, NULL); while (!feof(fdb)) { fdbe = filedb_getfile(fdb, ftell(fdb), GET_FILENAME); if (fdbe) { if ((!(fdbe->stat & FILE_UNUSED)) && (fdbe->stat & FILE_DIR)) Tcl_AppendElement(irp, fdbe->filename); free_fdbe(&fdbe); } } filedb_close(fdb); }
/* Searches the filedb for a file matching the specified mask, starting * at position 'pos'. The first matching file is returned. */ static filedb_entry *_filedb_matchfile(FILE *fdb, long pos, char *match, char *file, int line) { filedb_entry *fdbe = NULL; fseek(fdb, pos, SEEK_SET); while (!feof(fdb)) { pos = ftell(fdb); fdbe = filedb_getfile(fdb, pos, GET_FILENAME); if (fdbe) { if (!(fdbe->stat & FILE_UNUSED) && /* Not unused? */ wild_match_file(match, fdbe->filename)) { /* Matches our mask? */ free_fdbe(&fdbe); fdbe = _filedb_getfile(fdb, pos, GET_FULL, file, line); /* Save all data now */ return fdbe; } free_fdbe(&fdbe); } } return NULL; }
/* Outputs a sorted list of files/directories matching the mask, * to idx. */ static void filedb_ls(FILE *fdb, int idx, char *mask, int showall) { int ok = 0, cnt = 0, is = 0; char s1[81], *p = NULL; struct flag_record user = { FR_GLOBAL | FR_CHAN, 0, 0, 0, 0, 0 }; filedb_entry *fdbe = NULL; filelist_t *flist = NULL; flist = filelist_new(); filedb_readtop(fdb, NULL); fdbe = filedb_getfile(fdb, ftell(fdb), GET_FULL); while (fdbe) { ok = 1; if (fdbe->stat & FILE_UNUSED) ok = 0; if (ok && (fdbe->stat & FILE_DIR) && fdbe->flags_req) { /* Check permissions */ struct flag_record req = { FR_GLOBAL | FR_CHAN, 0, 0, 0, 0, 0 }; break_down_flags(fdbe->flags_req, &req, NULL); get_user_flagrec(dcc[idx].user, &user, dcc[idx].u.file->chat->con_chan); if (!flagrec_ok(&req, &user)) { ok = 0; } } if (ok) is = 1; if (ok && !wild_match_file(mask, fdbe->filename)) ok = 0; if (ok && (fdbe->stat & FILE_HIDDEN) && !(showall)) ok = 0; if (ok) { /* Display it! */ if (cnt == 0) { dprintf(idx, FILES_LSHEAD1); dprintf(idx, FILES_LSHEAD2); } filelist_add(flist, fdbe->filename); if (fdbe->stat & FILE_DIR) { char *s2 = NULL, *s3 = NULL; /* Too long? */ if (strlen(fdbe->filename) > 45) { /* Display the filename on its own line. */ s2 = nmalloc(strlen(fdbe->filename) + 3); sprintf(s2, "%s/\n", fdbe->filename); filelist_addout(flist, s2); my_free(s2); } else { s2 = nmalloc(strlen(fdbe->filename) + 2); sprintf(s2, "%s/", fdbe->filename); } /* Note: You have to keep the sprintf and the nmalloc statements * in sync, i.e. always check that you allocate enough * memory. */ if ((fdbe->flags_req) && (user.global &(USER_MASTER | USER_JANITOR))) { s3 = nmalloc(42 + strlen(s2 ? s2 : "") + 6 + strlen(FILES_REQUIRES) + strlen(fdbe->flags_req) + 1 + strlen(fdbe->chan ? fdbe->chan : "") + 1); sprintf(s3, "%-30s <DIR%s> (%s %s%s%s)\n", s2, fdbe->stat & FILE_SHARE ? " SHARE" : "", FILES_REQUIRES, fdbe->flags_req, fdbe->chan ? " " : "", fdbe->chan ? fdbe->chan : ""); } else { s3 = nmalloc(38 + strlen(s2 ? s2 : "")); sprintf(s3, "%-30s <DIR>\n", s2 ? s2 : ""); } if (s2) my_free(s2); filelist_addout(flist, s3); my_free(s3); } else { char s2[41], t[50], *s3 = NULL, *s4; s2[0] = 0; if (showall) { if (fdbe->stat & FILE_SHARE) strcat(s2, " (shr)"); if (fdbe->stat & FILE_HIDDEN) strcat(s2, " (hid)"); } egg_strftime(t, 10, "%d%b%Y", localtime(&fdbe->uploaded)); if (fdbe->size < 1024) sprintf(s1, "%5d", fdbe->size); else sprintf(s1, "%4dk", (int) (fdbe->size / 1024)); if (fdbe->sharelink) strcpy(s1, " "); /* Too long? */ if (strlen(fdbe->filename) > 30) { s3 = nmalloc(strlen(fdbe->filename) + 2); sprintf(s3, "%s\n", fdbe->filename); filelist_addout(flist, s3); my_free(s3); /* Causes filename to be displayed on its own line */ } else malloc_strcpy(s3, fdbe->filename); s4 = nmalloc(69 + strlen(s3 ? s3 : "") + strlen(s1) + strlen(fdbe->uploader) + strlen(t) + strlen(s2)); sprintf(s4, "%-30s %s %-9s (%s) %6d%s\n", s3 ? s3 : "", s1, fdbe->uploader, t, fdbe->gots, s2); if (s3) my_free(s3); filelist_addout(flist, s4); my_free(s4); if (fdbe->sharelink) { s4 = nmalloc(9 + strlen(fdbe->sharelink)); sprintf(s4, " --> %s\n", fdbe->sharelink); filelist_addout(flist, s4); my_free(s4); } } if (fdbe->desc) { p = strchr(fdbe->desc, '\n'); while (p != NULL) { *p = 0; if ((fdbe->desc)[0]) { char *sd; sd = nmalloc(strlen(fdbe->desc) + 5); sprintf(sd, " %s\n", fdbe->desc); filelist_addout(flist, sd); my_free(sd); } strcpy(fdbe->desc, p + 1); p = strchr(fdbe->desc, '\n'); } if ((fdbe->desc)[0]) { char *sd; sd = nmalloc(strlen(fdbe->desc) + 5); sprintf(sd, " %s\n", fdbe->desc); filelist_addout(flist, sd); my_free(sd); } } cnt++; } free_fdbe(&fdbe); fdbe = filedb_getfile(fdb, ftell(fdb), GET_FULL); } if (is == 0) dprintf(idx, FILES_NOFILES); else if (cnt == 0) dprintf(idx, FILES_NOMATCH); else { filelist_sort(flist); filelist_idxshow(flist, idx); dprintf(idx, "--- %d file%s.\n", cnt, cnt != 1 ? "s" : ""); } filelist_free(flist); }
/* Updates the specified filedb in several ways: * * 1. Adds all new files from the directory to the db. * 2. Removes all stale entries from the db. * 3. Optimises the db. */ static void filedb_update(char *path, FILE *fdb, int sort) { struct dirent *dd = NULL; struct stat st; filedb_entry *fdbe = NULL; DIR *dir = NULL; long where = 0; char *name = NULL, *s = NULL; /* * FIRST: make sure every real file is in the database */ dir = opendir(path); if (dir == NULL) { putlog(LOG_MISC, "*", FILES_NOUPDATE); return; } dd = readdir(dir); while (dd != NULL) { malloc_strcpy(name, dd->d_name); if (name[0] != '.') { s = nmalloc(strlen(path) + strlen(name) + 2); sprintf(s, "%s/%s", path, name); stat(s, &st); my_free(s); filedb_readtop(fdb, NULL); fdbe = filedb_matchfile(fdb, ftell(fdb), name); if (!fdbe) { /* new file! */ fdbe = malloc_fdbe(); malloc_strcpy(fdbe->filename, name); malloc_strcpy(fdbe->uploader, botnetnick); fdbe->uploaded = now; fdbe->size = st.st_size; if (S_ISDIR(st.st_mode)) fdbe->stat |= FILE_DIR; filedb_addfile(fdb, fdbe); } else if (fdbe->size != st.st_size) { /* update size if needed */ fdbe->size = st.st_size; filedb_updatefile(fdb, fdbe->pos, fdbe, UPDATE_HEADER); } free_fdbe(&fdbe); } dd = readdir(dir); } if (name) my_free(name); closedir(dir); /* * SECOND: make sure every db file is real */ filedb_readtop(fdb, NULL); fdbe = filedb_getfile(fdb, ftell(fdb), GET_FILENAME); while (fdbe) { where = ftell(fdb); if (!(fdbe->stat & FILE_UNUSED) && !(fdbe->stat & FILE_ISLINK) && fdbe->filename) { s = nmalloc(strlen(path) + 1 + strlen(fdbe->filename) + 1); sprintf(s, "%s/%s", path, fdbe->filename); if (stat(s, &st) != 0) /* gone file */ filedb_delfile(fdb, fdbe->pos); my_free(s); } free_fdbe(&fdbe); fdbe = filedb_getfile(fdb, where, GET_FILENAME); } /* * THIRD: optimise database * * Instead of sorting, we only clean up the db, because sorting is now * done on-the-fly when we display the file list. */ if (sort) filedb_cleanup(fdb); /* Cleanup DB */ filedb_timestamp(fdb); /* Write new timestamp */ }