/** * jogger_checkoutfile() * * Tries to open given file (check), and reads it, if expected (checkout). * It is designed to be proof to special file problems (especially named pipe ones). * * @param file - filename to open. * @param data - pointer to store file contents or NULL, if don't want to read it. * @param len - pointer to store filelength or NULL, if not needed. * @param hash - pointer to store filehash or NULL, if not needed. * @param maxlen - maximum filesize to accept (not counting additional NUL) or 0, if n/a. * @param quiet - if set, don't output anything to __status. * * @return 0 on success, errno on failure. */ static int jogger_checkoutfile(const char *file, char **data, int *len, char **hash, const int maxlen, const int quiet) { static char jogger_hash[sizeof(int)*2+3]; int mylen, fs, fd; const char *fn = prepare_path_user(file); if (!fn) return EINVAL; if ((fd = open(fn, O_RDONLY|O_NONBLOCK)) == -1) { /* we use O_NONBLOCK to get rid of FIFO problems */ const int err = errno; if (err == ENXIO) printq("io_nonfile", file); else printq("io_cantopen", file, strerror(err)); return err; } { struct stat st; if ((fstat(fd, &st) == -1) || !S_ISREG(st.st_mode)) { close(fd); printq("io_nonfile", file); return EISDIR; /* nearest, I think */ } fs = st.st_size; } int bufsize = (fs ? (maxlen && fs > maxlen ? maxlen+1 : fs+1) : 0x4000); /* we leave 1 byte for additional NUL */ char *out = xmalloc(bufsize); void *p = out; int _read = 0, res; { int cf = fcntl(fd, F_GETFL); if (cf == -1) /* evil thing */ cf = 0; else cf &= ~O_NONBLOCK; fcntl(fd, F_SETFL, cf); } while ((res = read(fd, p, bufsize-_read))) { if (res == -1) { const int err = errno; if (err != EINTR && err != EAGAIN) { close(fd); printq("io_cantread", file, strerror(errno)); return err; } } else { _read += res; if (maxlen && _read > maxlen) { xfree(out); printq("io_toobig", file, ekg_itoa(_read > fs ? _read : fs), ekg_itoa(maxlen)); return EFBIG; } else if (_read == bufsize) { /* fs sucks? */ bufsize += 0x4000; out = xrealloc(out, bufsize); p = out+_read; } else p += res; } } close(fd); if (_read == 0) { xfree(out); printq("io_emptyfile", file); return EINVAL; /* like mmap() */ } else if (_read+1 != bufsize) { out = xrealloc(out, _read+1); out[_read] = 0; /* add NUL */ } mylen = xstrlen(out); if (fs && _read > fs) printq("io_expanded", file, ekg_itoa(_read), ekg_itoa(fs)); else if (_read < fs) printq("io_truncated", file, ekg_itoa(_read), ekg_itoa(fs)); if (_read > mylen) printq("io_binaryfile", file, ekg_itoa(mylen), ekg_itoa(_read)); if (len) *len = _read; /* I don't want to write my own hashing function, so using EKG2 one * it will fail to hash data after any \0 in file, if there're any * but we also aren't prepared to handle them */ if (hash) { char sizecont[8]; snprintf(sizecont, 8, "0x%%0%lux", sizeof(int)*2); snprintf(jogger_hash, sizeof(int)*2+3, sizecont, ekg_hash(out)); *hash = jogger_hash; } if (data) *data = out; else xfree(out); return 0; }
/* like that in mine 'jogger' plugin, but slightly modified for xmsg * - quiet always 1, so removed all prints, * - hash not needed, giving timestamp instead. */ static int xmsg_checkoutfile(const char *file, char **data, int *len, time_t *ts, const int maxlen) { int fs, fd; const char *fn = prepare_path_user(file); if (!fn) return EINVAL; if ((fd = open(fn, O_RDONLY|O_NONBLOCK)) == -1) /* we use O_NONBLOCK to get rid of FIFO problems */ return errno; { struct stat st; if ((fstat(fd, &st) == -1) || !S_ISREG(st.st_mode)) { close(fd); return EISDIR; /* nearest, I think */ } fs = st.st_size; /* mtime > ctime > atime > time(NULL) */ #define X(x,y) (x ? x : y) if (ts) *ts = X(st.st_mtime, X(st.st_ctime, X(st.st_atime, time(NULL)))); #undef X } int bufsize = (fs ? (maxlen && fs > maxlen ? maxlen+1 : fs+1) : 0x4000); /* we leave 1 byte for additional NUL */ char *out = xmalloc(bufsize); void *p = out; int _read = 0, res; { int cf = fcntl(fd, F_GETFL); if (cf == -1) /* evil thing */ cf = 0; else cf &= ~O_NONBLOCK; fcntl(fd, F_SETFL, cf); } while ((res = read(fd, p, bufsize-_read))) { if (res == -1) { const int err = errno; if (err != EINTR && err != EAGAIN) { close(fd); return err; } } else { _read += res; if (maxlen && _read > maxlen) { xfree(out); return EFBIG; } else if (_read == bufsize) { /* fs sucks? */ bufsize += 0x4000; out = xrealloc(out, bufsize); p = out+_read; } else p += res; } } close(fd); if (_read == 0) { xfree(out); return EINVAL; /* like mmap() */ } else if (_read+1 != bufsize) { out = xrealloc(out, _read+1); out[_read] = 0; /* add NUL */ } if (len) *len = _read; if (data) *data = out; else xfree(out); return 0; }