]> git.decadent.org.uk Git - nfs-utils.git/blob - support/nfs/cacheio.c
mountd: fix checking for errors when exporting filesystems
[nfs-utils.git] / support / nfs / cacheio.c
1 /*
2  * support/nfs/cacheio.c
3  * support IO on the cache channel files in 2.5 and beyond.
4  * These use 'qwords' which are like words, but with a little quoting.
5  *
6  */
7
8
9 /*
10  * Support routines for text-based upcalls.
11  * Fields are separated by spaces.
12  * Fields are either mangled to quote space tab newline slosh with slosh
13  * or a hexified with a leading \x
14  * Record is terminated with newline.
15  *
16  */
17
18 #include <nfslib.h>
19 #include <stdio.h>
20 #include <stdio_ext.h>
21 #include <ctype.h>
22 #include <unistd.h>
23 #include <sys/types.h>
24 #include <sys/stat.h>
25 #include <fcntl.h>
26 #include <time.h>
27 #include <errno.h>
28
29 void qword_add(char **bpp, int *lp, char *str)
30 {
31         char *bp = *bpp;
32         int len = *lp;
33         char c;
34
35         if (len < 0) return;
36
37         while ((c=*str++) && len)
38                 switch(c) {
39                 case ' ':
40                 case '\t':
41                 case '\n':
42                 case '\\':
43                         if (len >= 4) {
44                                 *bp++ = '\\';
45                                 *bp++ = '0' + ((c & 0300)>>6);
46                                 *bp++ = '0' + ((c & 0070)>>3);
47                                 *bp++ = '0' + ((c & 0007)>>0);
48                         }
49                         len -= 4;
50                         break;
51                 default:
52                         *bp++ = c;
53                         len--;
54                 }
55         if (c || len <1) len = -1;
56         else {
57                 *bp++ = ' ';
58                 len--;
59         }
60         *bpp = bp;
61         *lp = len;
62 }
63
64 void qword_addhex(char **bpp, int *lp, char *buf, int blen)
65 {
66         char *bp = *bpp;
67         int len = *lp;
68
69         if (len < 0) return;
70
71         if (len > 2) {
72                 *bp++ = '\\';
73                 *bp++ = 'x';
74                 len -= 2;
75                 while (blen && len >= 2) {
76                         unsigned char c = *buf++;
77                         *bp++ = '0' + ((c&0xf0)>>4) + (c>=0xa0)*('a'-'9'-1);
78                         *bp++ = '0' + (c&0x0f) + ((c&0x0f)>=0x0a)*('a'-'9'-1);
79                         len -= 2;
80                         blen--;
81                 }
82         }
83         if (blen || len<1) len = -1;
84         else {
85                 *bp++ = ' ';
86                 len--;
87         }
88         *bpp = bp;
89         *lp = len;
90 }
91
92 void qword_addint(char **bpp, int *lp, int n)
93 {
94         int len;
95
96         len = snprintf(*bpp, *lp, "%d ", n);
97         if (len > *lp)
98                 len = *lp;
99         *bpp += len;
100         *lp -= len;
101 }
102
103 void qword_adduint(char **bpp, int *lp, unsigned int n)
104 {
105         int len;
106
107         len = snprintf(*bpp, *lp, "%u ", n);
108         if (len > *lp)
109                 len = *lp;
110         *bpp += len;
111         *lp -= len;
112 }
113
114 void qword_addeol(char **bpp, int *lp)
115 {
116         if (*lp <= 0)
117                 return;
118         **bpp = '\n';
119         (*bpp)++;
120         (*lp)--;
121 }
122
123 static char qword_buf[8192];
124 void qword_print(FILE *f, char *str)
125 {
126         char *bp = qword_buf;
127         int len = sizeof(qword_buf);
128         qword_add(&bp, &len, str);
129         if (fwrite(qword_buf, bp-qword_buf, 1, f) != 1) {
130                 xlog_warn("qword_print: fwrite failed: errno %d (%s)",
131                         errno, strerror(errno));
132         }
133 }
134
135 void qword_printhex(FILE *f, char *str, int slen)
136 {
137         char *bp = qword_buf;
138         int len = sizeof(qword_buf);
139         qword_addhex(&bp, &len, str, slen);
140         if (fwrite(qword_buf, bp-qword_buf, 1, f) != 1) {
141                 xlog_warn("qword_printhex: fwrite failed: errno %d (%s)",
142                         errno, strerror(errno));
143         }
144 }
145
146 void qword_printint(FILE *f, int num)
147 {
148         fprintf(f, "%d ", num);
149 }
150
151 void qword_printuint(FILE *f, unsigned int num)
152 {
153         fprintf(f, "%u ", num);
154 }
155
156 void qword_printtimefrom(FILE *f, unsigned int num)
157 {
158         fprintf(f, "%lu ", time(0) + num);
159 }
160
161 int qword_eol(FILE *f)
162 {
163         int err;
164
165         err = fprintf(f,"\n");
166         if (err < 0) {
167                 xlog_warn("qword_eol: fprintf failed: errno %d (%s)",
168                             errno, strerror(errno));
169         } else {
170                 err = fflush(f);
171                 if (err) {
172                         xlog_warn("qword_eol: fflush failed: errno %d (%s)",
173                                   errno, strerror(errno));
174                 }
175         }
176         /*
177          * We must send one line (and one line only) in a single write
178          * call.  In case of a write error, libc may accumulate the
179          * unwritten data and try to write it again later, resulting in a
180          * multi-line write.  So we must explicitly ask it to throw away
181          * any such cached data.  But we return any original error
182          * indication to the caller.
183          */
184         __fpurge(f);
185         fflush(f);
186         return err;
187 }
188
189
190
191 #define isodigit(c) (isdigit(c) && c <= '7')
192 int qword_get(char **bpp, char *dest, int bufsize)
193 {
194         /* return bytes copied, or -1 on error */
195         char *bp = *bpp;
196         int len = 0;
197
198         while (*bp == ' ') bp++;
199
200         if (bp[0] == '\\' && bp[1] == 'x') {
201                 /* HEX STRING */
202                 bp += 2;
203                 while (isxdigit(bp[0]) && isxdigit(bp[1]) && len < bufsize) {
204                         int byte = isdigit(*bp) ? *bp-'0' : toupper(*bp)-'A'+10;
205                         bp++;
206                         byte <<= 4;
207                         byte |= isdigit(*bp) ? *bp-'0' : toupper(*bp)-'A'+10;
208                         *dest++ = byte;
209                         bp++;
210                         len++;
211                 }
212         } else {
213                 /* text with \nnn octal quoting */
214                 while (*bp != ' ' && *bp != '\n' && *bp && len < bufsize-1) {
215                         if (*bp == '\\' &&
216                             isodigit(bp[1]) && (bp[1] <= '3') &&
217                             isodigit(bp[2]) &&
218                             isodigit(bp[3])) {
219                                 int byte = (*++bp -'0');
220                                 bp++;
221                                 byte = (byte << 3) | (*bp++ - '0');
222                                 byte = (byte << 3) | (*bp++ - '0');
223                                 *dest++ = byte;
224                                 len++;
225                         } else {
226                                 *dest++ = *bp++;
227                                 len++;
228                         }
229                 }
230         }
231
232         if (*bp != ' ' && *bp != '\n' && *bp != '\0')
233                 return -1;
234         while (*bp == ' ') bp++;
235         *bpp = bp;
236         *dest = '\0';
237         return len;
238 }
239
240 int qword_get_int(char **bpp, int *anint)
241 {
242         char buf[50];
243         char *ep;
244         int rv;
245         int len = qword_get(bpp, buf, 50);
246         if (len < 0) return -1;
247         if (len ==0) return -1;
248         rv = strtol(buf, &ep, 0);
249         if (*ep) return -1;
250         *anint = rv;
251         return 0;
252 }
253
254 int qword_get_uint(char **bpp, unsigned int *anint)
255 {
256         char buf[50];
257         char *ep;
258         unsigned int rv;
259         int len = qword_get(bpp, buf, 50);
260         if (len < 0) return -1;
261         if (len ==0) return -1;
262         rv = strtoul(buf, &ep, 0);
263         if (*ep) return -1;
264         *anint = rv;
265         return 0;
266 }
267
268 #define READLINE_BUFFER_INCREMENT 2048
269
270 int readline(int fd, char **buf, int *lenp)
271 {
272         /* read a line into *buf, which is malloced *len long
273          * realloc if needed until we find a \n
274          * nul out the \n and return
275          * 0 on eof, 1 on success
276          */
277         int len;
278
279         if (*lenp == 0) {
280                 char *b = malloc(READLINE_BUFFER_INCREMENT);
281                 if (b == NULL)
282                         return 0;
283                 *buf = b;
284                 *lenp = READLINE_BUFFER_INCREMENT;
285         }
286         len = read(fd, *buf, *lenp);
287         if (len <= 0)
288                 return 0;
289         while ((*buf)[len-1] != '\n') {
290         /* now the less common case.  There was no newline,
291          * so we have to keep reading after re-alloc
292          */
293                 char *new;
294                 int nl;
295                 *lenp += READLINE_BUFFER_INCREMENT;
296                 new = realloc(*buf, *lenp);
297                 if (new == NULL)
298                         return 0;
299                 *buf = new;
300                 nl = read(fd, *buf + len, *lenp - len);
301                 if (nl <= 0)
302                         return 0;
303                 len += nl;
304         }
305         (*buf)[len-1] = '\0';
306         return 1;
307 }
308
309
310 /* Check if we should use the new caching interface
311  * This succeeds iff the "nfsd" filesystem is mounted on
312  * /proc/fs/nfs
313  */
314 int
315 check_new_cache(void)
316 {
317         return  (access("/proc/fs/nfs/filehandle", F_OK) == 0) ||
318                 (access("/proc/fs/nfsd/filehandle", F_OK) == 0);
319 }       
320
321
322 /* flush the kNFSd caches.
323  * Set the flush time to the mtime of _PATH_ETAB or
324  * if force, to now.
325  * the caches to flush are:
326  *  auth.unix.ip nfsd.export nfsd.fh
327  */
328
329 void
330 cache_flush(int force)
331 {
332         struct stat stb;
333         int c;
334         char stime[20];
335         char path[200];
336         time_t now;
337         /* Note: the order of these caches is important.
338          * They need to be flushed in dependancy order. So
339          * a cache that references items in another cache,
340          * as nfsd.fh entries reference items in nfsd.export,
341          * must be flushed before the cache that it references.
342          */
343         static char *cachelist[] = {
344                 "auth.unix.ip",
345                 "auth.unix.gid",
346                 "nfsd.fh",
347                 "nfsd.export",
348                 NULL
349         };
350         now = time(0);
351         if (force ||
352             stat(_PATH_ETAB, &stb) != 0 ||
353             stb.st_mtime > now)
354                 stb.st_mtime = time(0);
355         
356         sprintf(stime, "%ld\n", stb.st_mtime);
357         for (c=0; cachelist[c]; c++) {
358                 int fd;
359                 sprintf(path, "/proc/net/rpc/%s/flush", cachelist[c]);
360                 fd = open(path, O_RDWR);
361                 if (fd >= 0) {
362                         if (write(fd, stime, strlen(stime)) != (ssize_t)strlen(stime)) {
363                                 xlog_warn("Writing to '%s' failed: errno %d (%s)",
364                                 path, errno, strerror(errno));
365                         }
366                         close(fd);
367                 }
368         }
369 }