/* * filedb.c -- handles: low-level manipulation of the filesystem database * files reaction to remote requests for files * dprintf'ized, 25feb1996 english, 5mar1996 */ /* * This file is part of the eggdrop source code copyright (c) 1997 Robey * Pointer and is distributed according to the GNU general public license. * For full details, read the top of 'main.c' or the file called COPYING * that was distributed with this code. */ /* lock the file, using fcntl */ static void lockfile(FILE * f) { struct flock fl; fl.l_type = F_WRLCK; fl.l_start = 0; fl.l_whence = SEEK_SET; fl.l_len = 0; /* block on lock: */ fcntl(fileno(f), F_SETLKW, &fl); } /* unlock the file */ static void unlockfile(FILE * f) { struct flock fl; fl.l_type = F_UNLCK; fl.l_start = 0; fl.l_whence = SEEK_SET; fl.l_len = 0; fcntl(fileno(f), F_SETLKW, &fl); } /* use a where of 0 to start out, then increment 1 space for each next */ static int findmatch(FILE * f, char *lookfor, long *where, filedb * fdb) { char match[256]; int l; strncpy(match, lookfor, 255); match[255] = 0; /* clip any trailing / */ l = strlen(match) - 1; if (match[l] == '/') match[l] = 0; fseek(f, *where, SEEK_SET); while (!feof(f)) { *where = ftell(f); fread(fdb, sizeof(filedb), 1, f); if (!feof(f)) { if (!(fdb->stat & FILE_UNUSED) && wild_match_file(match, fdb->filename)) return 1; } } return 0; } static long findempty(FILE * f) { long where = 0L; filedb fdb; rewind(f); while (!feof(f)) { where = ftell(f); fread(&fdb, sizeof(filedb), 1, f); if (!feof(f)) { if (fdb.stat & FILE_UNUSED) return where; } } fseek(f, 0L, SEEK_END); where = ftell(f); return where; } static void filedb_timestamp(FILE * f) { filedb fdb; int x; /* read 1st filedb entry if it's there */ rewind(f); x = fread(&fdb, sizeof(filedb), 1, f); if (x < 1) { /* MAKE 1st filedb entry then! */ fdb.version = FILEVERSION; fdb.stat = FILE_UNUSED; } fdb.timestamp = now; rewind(f); fwrite(&fdb, sizeof(filedb), 1, f); } /* return 1 if i find a '.files' and convert it */ static int convert_old_db(char *path, char *newfiledb) { FILE *f, *g; char s[256], *fn, *nick, *tm, *s1 = s; filedb fdb; int in_file = 0, i; long where; struct stat st; sprintf(s, "%s/.files", path); f = fopen(s, "r"); if (f == NULL) return 0; g = fopen(newfiledb, "w+b"); if (g == NULL) { putlog(LOG_MISC, "(!) Can't create filedb in %s", newfiledb); fclose(f); return 0; } putlog(LOG_FILES, "*", FILES_CONVERT, path); where = ftell(g); /* scan contents of .files and painstakingly create .filedb entries */ while (!feof(f)) { fgets(s, 120, f); if (s[strlen(s) - 1] == '\n') s[strlen(s) - 1] = 0; if (!feof(f)) { fn = newsplit(&s1); rmspace(fn); if ((fn[0]) && (fn[0] != ';') && (fn[0] != '#')) { /* not comment */ if (fn[0] == '-') { /* adjust comment for current file */ if (in_file) { rmspace(s); if (strlen(s) + strlen(fdb.desc) <= 600) { strcat(fdb.desc, "\n"); strcat(fdb.desc, s); fseek(g, where, SEEK_SET); fwrite(&fdb, sizeof(filedb), 1, g); } } } else { in_file = 1; where = ftell(g); nick = newsplit(&s1); rmspace(nick); tm = newsplit(&s1); rmspace(tm); rmspace(s1); i = strlen(fn) - 1; if (fn[i] == '/') fn[i] = 0; fdb.version = FILEVERSION; fdb.stat = 0; fdb.desc[0] = 0; fdb.chname[0] = 0; fdb.flags_req[0] = 0; strcpy(fdb.filename, fn); strcpy(fdb.uploader, nick); fdb.gots = atoi(s1); fdb.sharelink[0] = 0; fdb.uploaded = atoi(tm); sprintf(s, "%s/%s", path, fn); if (stat(s, &st) == 0) { /* file is okay */ if (S_ISDIR(st.st_mode)) { fdb.stat |= FILE_DIR; if (nick[0] == '+') { char x[100]; /* only do global flags, it's an old one */ struct flag_record fr = {FR_GLOBAL, 0, 0, 0, 0, 0}; break_down_flags(nick + 1, &fr, NULL); build_flags(x, &fr, NULL); /* we only want valid flags */ strncpy(fdb.flags_req, x, 21); fdb.flags_req[21] = 0; } } fdb.size = st.st_size; fwrite(&fdb, sizeof(filedb), 1, g); } else in_file = 0; /* skip */ } } } } fseek(g, 0, SEEK_END); fclose(g); fclose(f); return 1; } static void filedb_update(char *path, FILE * f, int sort) { struct dirent *dd; DIR *dir; filedb fdb[2]; char name[61]; long where, oldwhere; struct stat st; char s[512]; int ret; /* 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) { strncpy(name, dd->d_name, 60); name[60] = 0; if (NAMLEN(dd) <= 60) name[NAMLEN(dd)] = 0; else { /* truncate name on disk */ char s1[512], s2[256]; strcpy(s1, path); strcat(s1, "/"); strncat(s1, dd->d_name, NAMLEN(dd)); s1[strlen(path) + NAMLEN(dd) + 1] = 0; sprintf(s2, "%s/%s", path, name); movefile(s1, s2); } if (name[0] != '.') { sprintf(s, "%s/%s", path, name); stat(s, &st); where = 0; ret = findmatch(f, name, &where, &fdb[0]); if (!ret) { /* new file! */ where = findempty(f); fseek(f, where, SEEK_SET); fdb[0].version = FILEVERSION; fdb[0].stat = 0; /* by default, visible regular file */ strcpy(fdb[0].filename, name); fdb[0].desc[0] = 0; strcpy(fdb[0].uploader, botnetnick); fdb[0].gots = 0; fdb[0].flags_req[0] = 0; fdb[0].uploaded = now; fdb[0].size = st.st_size; fdb[0].sharelink[0] = 0; if (S_ISDIR(st.st_mode)) fdb[0].stat |= FILE_DIR; fwrite(&fdb[0], sizeof(filedb), 1, f); } else if (fdb[0].version < FILEVERSION) { /* old version filedb, do the dirty */ if (fdb[0].version == FILEVERSION_OLD) { filedb_old *fdbo = (filedb_old *) & fdb[0]; fdb[0].desc[185] = 0; /* truncate it */ fdb[0].chname[0] = 0; /* new entry */ strcpy(fdb[0].uploader, fdbo->uploader); /* moved forward * a few bytes */ strcpy(fdb[0].flags_req, fdbo->flags_req); /* and again */ fdb[0].version = FILEVERSION; fdb[0].size = st.st_size; fseek(f, where, SEEK_SET); fwrite(&fdb[0], sizeof(filedb), 1, f); } else { putlog(LOG_MISC, "*", "!!! Unknown filedb type !"); } } else { /* update size if needed */ fdb[0].size = st.st_size; fseek(f, where, SEEK_SET); fwrite(&fdb[0], sizeof(filedb), 1, f); } } dd = readdir(dir); } closedir(dir); /* SECOND: make sure every db file is real, and sort as we go, * if we're sorting */ rewind(f); while (!feof(f)) { where = ftell(f); fread(&fdb[0], sizeof(filedb), 1, f); if (!feof(f)) { if (!(fdb[0].stat & FILE_UNUSED) && !fdb[0].sharelink[0]) { sprintf(s, "%s/%s", path, fdb[0].filename); if (stat(s, &st) != 0) { /* gone file */ fseek(f, where, SEEK_SET); fdb[0].stat |= FILE_UNUSED; fwrite(&fdb[0], sizeof(filedb), 1, f); /* sunos and others will puke bloody chunks if you write the * last record in a file and then attempt to read to EOF: */ fseek(f, where, SEEK_SET); continue; /* cycle to next one */ } } if (sort && !(fdb[0].stat & FILE_UNUSED)) { rewind(f); oldwhere = ftell(f); ret = 0; while (!feof(f) && (oldwhere < where)) { fread(&fdb[1 - ret], sizeof(filedb), 1, f); if (!feof(f)) { if ((fdb[0].stat & FILE_UNUSED) || (strcasecmp(fdb[ret].filename, fdb[1 - ret].filename) < 0)) { /* our current is < the checked one, insert here */ fseek(f, oldwhere, SEEK_SET); fwrite(&fdb[ret], sizeof(filedb), 1, f); ret = 1 - ret; /* and fall out */ } /* otherwise read next entry */ oldwhere = ftell(f); } } /* here, either we've found a place to insert, or got to * the end of the list */ /* if we've got to the end of the current list oldwhere == where * so we fall through ret will point to the current valid one */ while (!feof(f) && (oldwhere < where)) { /* need to move this entry up 1 .. */ fread(&fdb[1 - ret], sizeof(filedb), 1, f); oldwhere = ftell(f); /* write lower record here */ fseek(f, oldwhere, SEEK_SET); fwrite(&fdb[ret], sizeof(filedb), 1, f); ret = 1 - ret; } /* when we get here fdb[ret] holds the last record, * which needs to be written where we first grabbed the * record from */ fseek(f, where, SEEK_SET); fwrite(&fdb[ret], sizeof(filedb), 1, f); } } } /* write new timestamp */ filedb_timestamp(f); } static int count = 0; static FILE *filedb_open(char *path, int sort) { char s[DIRLEN], npath[DIRLEN]; FILE *f; filedb fdb; struct stat st; if (count >= 2) putlog(LOG_MISC, "*", "(@) warning: %d open filedb's", count); simple_sprintf(npath, "%s%s", dccdir, path); /* use alternate filename if requested */ if (filedb_path[0]) { char s2[DIRLEN], *p; strcpy(s2, path); p = s2; while (*p++) if (*p == '/') *p = '.'; simple_sprintf(s, "%sfiledb.%s", filedb_path, s2); if (s[strlen(s) - 1] == '.') s[strlen(s) - 1] = 0; } else simple_sprintf(s, "%s/.filedb", npath); f = fopen(s, "r+b"); if (!f) { /* attempt to convert */ if (convert_old_db(npath, s)) { f = fopen(s, "r+b"); if (f == NULL) { putlog(LOG_MISC, FILES_NOCONVERT, npath); return NULL; } lockfile(f); filedb_update(npath, f, sort); /* make it correct */ count++; return f; } /* create new database and fix it up */ f = fopen(s, "w+b"); if (!f) return NULL; lockfile(f); filedb_update(npath, f, sort); count++; return f; } /* lock it from other bots: */ lockfile(f); /* check the timestamp... */ fread(&fdb, sizeof(filedb), 1, f); stat(npath, &st); /* update filedb if: * + it's been 6 hours since it was last updated * + the directory has been visibly modified since then * (6 hours may be a bit often) */ if (sort || ((now - fdb.timestamp) > (6 * 3600)) || (fdb.timestamp < st.st_mtime) || (fdb.timestamp < st.st_ctime) || (fdb.version <= FILEVERSION_OLD)) /* file database isn't up-to-date! */ filedb_update(npath, f, sort & 1); count++; return f; } static void filedb_close(FILE * f) { filedb_timestamp(f); fseek(f, 0L, SEEK_END); count--; unlockfile(f); fclose(f); } static void filedb_add(FILE * f, char *filename, char *nick) { long where; filedb fdb; /* when the filedb was opened, a record was already created */ where = 0; if (!findmatch(f, filename, &where, &fdb)) return; strncpy(fdb.uploader, nick, HANDLEN); fdb.uploader[HANDLEN] = 0; fdb.uploaded = now; fseek(f, where, SEEK_SET); fwrite(&fdb, sizeof(filedb), 1, f); } static void filedb_ls(FILE * f, int idx, char *mask, int showall) { filedb fdb; int ok = 0, cnt = 0, is = 0; char s[81], s1[81], *p; struct flag_record user = {FR_GLOBAL | FR_CHAN, 0, 0, 0, 0, 0}; rewind(f); while (!feof(f)) { fread(&fdb, sizeof(filedb), 1, f); if (!feof(f)) { ok = 1; if (fdb.stat & FILE_UNUSED) ok = 0; if (fdb.stat & FILE_DIR) { /* check permissions */ struct flag_record req = {FR_GLOBAL | FR_CHAN, 0, 0, 0, 0, 0}; break_down_flags(fdb.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 (!wild_match_file(mask, fdb.filename)) ok = 0; if ((fdb.stat & FILE_HIDDEN) && !(showall)) ok = 0; if (ok) { /* display it! */ if (cnt == 0) { dprintf(idx, FILES_LSHEAD1); dprintf(idx, FILES_LSHEAD2); } if (fdb.stat & FILE_DIR) { char s2[50]; /* too long? */ if (strlen(fdb.filename) > 45) { dprintf(idx, "%s/\n", fdb.filename); s2[0] = 0; /* causes filename to be displayed on its own line */ } else sprintf(s2, "%s/", fdb.filename); if ((fdb.flags_req[0]) && (user.global &(USER_MASTER | USER_JANITOR))) { dprintf(idx, "%-30s