/* * support/nfs/cacheio.c * support IO on the cache channel files in 2.5 and beyond. * These use 'qwords' which are like words, but with a little quoting. * */ /* * Support routines for text-based upcalls. * Fields are separated by spaces. * Fields are either mangled to quote space tab newline slosh with slosh * or a hexified with a leading \x * Record is terminated with newline. * */ #include #include #include #include #include #include #include #include #include #include void qword_add(char **bpp, int *lp, char *str) { char *bp = *bpp; int len = *lp; char c; if (len < 0) return; while ((c=*str++) && len) switch(c) { case ' ': case '\t': case '\n': case '\\': if (len >= 4) { *bp++ = '\\'; *bp++ = '0' + ((c & 0300)>>6); *bp++ = '0' + ((c & 0070)>>3); *bp++ = '0' + ((c & 0007)>>0); } len -= 4; break; default: *bp++ = c; len--; } if (c || len <1) len = -1; else { *bp++ = ' '; len--; } *bpp = bp; *lp = len; } void qword_addhex(char **bpp, int *lp, char *buf, int blen) { char *bp = *bpp; int len = *lp; if (len < 0) return; if (len > 2) { *bp++ = '\\'; *bp++ = 'x'; len -= 2; while (blen && len >= 2) { unsigned char c = *buf++; *bp++ = '0' + ((c&0xf0)>>4) + (c>=0xa0)*('a'-'9'-1); *bp++ = '0' + (c&0x0f) + ((c&0x0f)>=0x0a)*('a'-'9'-1); len -= 2; blen--; } } if (blen || len<1) len = -1; else { *bp++ = ' '; len--; } *bpp = bp; *lp = len; } void qword_addint(char **bpp, int *lp, int n) { int len; len = snprintf(*bpp, *lp, "%d ", n); if (len > *lp) len = *lp; *bpp += len; *lp -= len; } void qword_adduint(char **bpp, int *lp, unsigned int n) { int len; len = snprintf(*bpp, *lp, "%u ", n); if (len > *lp) len = *lp; *bpp += len; *lp -= len; } void qword_addeol(char **bpp, int *lp) { if (*lp <= 0) return; **bpp = '\n'; (*bpp)++; (*lp)--; } static char qword_buf[8192]; void qword_print(FILE *f, char *str) { char *bp = qword_buf; int len = sizeof(qword_buf); qword_add(&bp, &len, str); if (fwrite(qword_buf, bp-qword_buf, 1, f) != 1) { xlog_warn("qword_print: fwrite failed: errno %d (%s)", errno, strerror(errno)); } } void qword_printhex(FILE *f, char *str, int slen) { char *bp = qword_buf; int len = sizeof(qword_buf); qword_addhex(&bp, &len, str, slen); if (fwrite(qword_buf, bp-qword_buf, 1, f) != 1) { xlog_warn("qword_printhex: fwrite failed: errno %d (%s)", errno, strerror(errno)); } } void qword_printint(FILE *f, int num) { fprintf(f, "%d ", num); } void qword_printuint(FILE *f, unsigned int num) { fprintf(f, "%u ", num); } void qword_printtimefrom(FILE *f, unsigned int num) { fprintf(f, "%lu ", time(0) + num); } int qword_eol(FILE *f) { int err; err = fprintf(f,"\n"); if (err < 0) { xlog_warn("qword_eol: fprintf failed: errno %d (%s)", errno, strerror(errno)); } else { err = fflush(f); if (err) { xlog_warn("qword_eol: fflush failed: errno %d (%s)", errno, strerror(errno)); } } /* * We must send one line (and one line only) in a single write * call. In case of a write error, libc may accumulate the * unwritten data and try to write it again later, resulting in a * multi-line write. So we must explicitly ask it to throw away * any such cached data. But we return any original error * indication to the caller. */ __fpurge(f); fflush(f); return err; } #define isodigit(c) (isdigit(c) && c <= '7') int qword_get(char **bpp, char *dest, int bufsize) { /* return bytes copied, or -1 on error */ char *bp = *bpp; int len = 0; while (*bp == ' ') bp++; if (bp[0] == '\\' && bp[1] == 'x') { /* HEX STRING */ bp += 2; while (isxdigit(bp[0]) && isxdigit(bp[1]) && len < bufsize) { int byte = isdigit(*bp) ? *bp-'0' : toupper(*bp)-'A'+10; bp++; byte <<= 4; byte |= isdigit(*bp) ? *bp-'0' : toupper(*bp)-'A'+10; *dest++ = byte; bp++; len++; } } else { /* text with \nnn octal quoting */ while (*bp != ' ' && *bp != '\n' && *bp && len < bufsize-1) { if (*bp == '\\' && isodigit(bp[1]) && (bp[1] <= '3') && isodigit(bp[2]) && isodigit(bp[3])) { int byte = (*++bp -'0'); bp++; byte = (byte << 3) | (*bp++ - '0'); byte = (byte << 3) | (*bp++ - '0'); *dest++ = byte; len++; } else { *dest++ = *bp++; len++; } } } if (*bp != ' ' && *bp != '\n' && *bp != '\0') return -1; while (*bp == ' ') bp++; *bpp = bp; *dest = '\0'; return len; } int qword_get_int(char **bpp, int *anint) { char buf[50]; char *ep; int rv; int len = qword_get(bpp, buf, 50); if (len < 0) return -1; if (len ==0) return -1; rv = strtol(buf, &ep, 0); if (*ep) return -1; *anint = rv; return 0; } int qword_get_uint(char **bpp, unsigned int *anint) { char buf[50]; char *ep; unsigned int rv; int len = qword_get(bpp, buf, 50); if (len < 0) return -1; if (len ==0) return -1; rv = strtoul(buf, &ep, 0); if (*ep) return -1; *anint = rv; return 0; } #define READLINE_BUFFER_INCREMENT 2048 int readline(int fd, char **buf, int *lenp) { /* read a line into *buf, which is malloced *len long * realloc if needed until we find a \n * nul out the \n and return * 0 on eof, 1 on success */ int len; if (*lenp == 0) { char *b = malloc(READLINE_BUFFER_INCREMENT); if (b == NULL) return 0; *buf = b; *lenp = READLINE_BUFFER_INCREMENT; } len = read(fd, *buf, *lenp); if (len <= 0) return 0; while ((*buf)[len-1] != '\n') { /* now the less common case. There was no newline, * so we have to keep reading after re-alloc */ char *new; int nl; *lenp += READLINE_BUFFER_INCREMENT; new = realloc(*buf, *lenp); if (new == NULL) return 0; *buf = new; nl = read(fd, *buf + len, *lenp - len); if (nl <= 0) return 0; len += nl; } (*buf)[len-1] = '\0'; return 1; } /* Check if we should use the new caching interface * This succeeds iff the "nfsd" filesystem is mounted on * /proc/fs/nfs */ int check_new_cache(void) { return (access("/proc/fs/nfs/filehandle", F_OK) == 0) || (access("/proc/fs/nfsd/filehandle", F_OK) == 0); } /* flush the kNFSd caches. * Set the flush time to the mtime of _PATH_ETAB or * if force, to now. * the caches to flush are: * auth.unix.ip nfsd.export nfsd.fh */ void cache_flush(int force) { struct stat stb; int c; char stime[20]; char path[200]; time_t now; /* Note: the order of these caches is important. * They need to be flushed in dependancy order. So * a cache that references items in another cache, * as nfsd.fh entries reference items in nfsd.export, * must be flushed before the cache that it references. */ static char *cachelist[] = { "auth.unix.ip", "auth.unix.gid", "nfsd.fh", "nfsd.export", NULL }; now = time(0); if (force || stat(_PATH_ETAB, &stb) != 0 || stb.st_mtime > now) stb.st_mtime = time(0); sprintf(stime, "%ld\n", stb.st_mtime); for (c=0; cachelist[c]; c++) { int fd; sprintf(path, "/proc/net/rpc/%s/flush", cachelist[c]); fd = open(path, O_RDWR); if (fd >= 0) { if (write(fd, stime, strlen(stime)) != (ssize_t)strlen(stime)) { xlog_warn("Writing to '%s' failed: errno %d (%s)", path, errno, strerror(errno)); } close(fd); } } }