diff mbox

[v2,2/5] mountd: talk to kernel using file descriptors instead of FILE

Message ID 1412257321-5855-3-git-send-email-timo.teras@iki.fi (mailing list archive)
State New, archived
Headers show

Commit Message

Timo Teräs Oct. 2, 2014, 1:41 p.m. UTC
Signed-off-by: Timo Teräs <timo.teras@iki.fi>
---
 utils/mountd/cache.c | 343 +++++++++++++++++++++++++++------------------------
 1 file changed, 183 insertions(+), 160 deletions(-)
diff mbox

Patch

diff --git a/utils/mountd/cache.c b/utils/mountd/cache.c
index 663a52a..c23d384 100644
--- a/utils/mountd/cache.c
+++ b/utils/mountd/cache.c
@@ -61,15 +61,13 @@  enum nfsd_fsid {
  * Record is terminated with newline.
  *
  */
-static int cache_export_ent(char *domain, struct exportent *exp, char *p);
+static int cache_export_ent(char *buf, int buflen, char *domain, struct exportent *exp, char *path);
 
 #define INITIAL_MANAGED_GROUPS 100
 
-char *lbuf  = NULL;
-int lbuflen = 0;
 extern int use_ipaddr;
 
-static void auth_unix_ip(FILE *f)
+static void auth_unix_ip(int f)
 {
 	/* requests are
 	 *  class IP-ADDR
@@ -78,23 +76,26 @@  static void auth_unix_ip(FILE *f)
 	 *
 	 *  "nfsd" IP-ADDR expiry domainname
 	 */
-	char *cp;
 	char class[20];
 	char ipaddr[INET6_ADDRSTRLEN + 1];
 	char *client = NULL;
 	struct addrinfo *tmp = NULL;
-	if (readline(fileno(f), &lbuf, &lbuflen) != 1)
-		return;
+	char buf[RPC_CHAN_BUF_SIZE], *bp;
+	int blen;
+
+	blen = read(f, buf, sizeof(buf));
+	if (blen <= 0 || buf[blen-1] != '\n') return;
+	buf[blen-1] = 0;
 
-	xlog(D_CALL, "auth_unix_ip: inbuf '%s'", lbuf);
+	xlog(D_CALL, "auth_unix_ip: inbuf '%s'", buf);
 
-	cp = lbuf;
+	bp = buf;
 
-	if (qword_get(&cp, class, 20) <= 0 ||
+	if (qword_get(&bp, class, 20) <= 0 ||
 	    strcmp(class, "nfsd") != 0)
 		return;
 
-	if (qword_get(&cp, ipaddr, sizeof(ipaddr) - 1) <= 0)
+	if (qword_get(&bp, ipaddr, sizeof(ipaddr) - 1) <= 0)
 		return;
 
 	tmp = host_pton(ipaddr);
@@ -113,16 +114,20 @@  static void auth_unix_ip(FILE *f)
 			freeaddrinfo(ai);
 		}
 	}
-	qword_print(f, "nfsd");
-	qword_print(f, ipaddr);
-	qword_printtimefrom(f, DEFAULT_TTL);
+	bp = buf; blen = sizeof(buf);
+	qword_add(&bp, &blen, "nfsd");
+	qword_add(&bp, &blen, ipaddr);
+	qword_adduint(&bp, &blen, time(0) + DEFAULT_TTL);
 	if (use_ipaddr) {
 		memmove(ipaddr + 1, ipaddr, strlen(ipaddr) + 1);
 		ipaddr[0] = '$';
-		qword_print(f, ipaddr);
+		qword_add(&bp, &blen, ipaddr);
 	} else if (client)
-		qword_print(f, *client?client:"DEFAULT");
-	qword_eol(f);
+		qword_add(&bp, &blen, *client?client:"DEFAULT");
+	qword_addeol(&bp, &blen);
+	if (blen <= 0 || write(f, buf, bp - buf) != bp - buf)
+		xlog(L_ERROR, "auth_unix_ip: error writing reply");
+
 	xlog(D_CALL, "auth_unix_ip: client %p '%s'", client, client?client: "DEFAULT");
 
 	free(client);
@@ -130,7 +135,7 @@  static void auth_unix_ip(FILE *f)
 
 }
 
-static void auth_unix_gid(FILE *f)
+static void auth_unix_gid(int f)
 {
 	/* Request are
 	 *  uid
@@ -144,7 +149,8 @@  static void auth_unix_gid(FILE *f)
 	gid_t *more_groups;
 	int ngroups;
 	int rv, i;
-	char *cp;
+	char buf[RPC_CHAN_BUF_SIZE], *bp;
+	int blen;
 
 	if (groups_len == 0) {
 		groups = malloc(sizeof(gid_t) * INITIAL_MANAGED_GROUPS);
@@ -156,11 +162,12 @@  static void auth_unix_gid(FILE *f)
 
 	ngroups = groups_len;
 
-	if (readline(fileno(f), &lbuf, &lbuflen) != 1)
-		return;
+	blen = read(f, buf, sizeof(buf));
+	if (blen <= 0 || buf[blen-1] != '\n') return;
+	buf[blen-1] = 0;
 
-	cp = lbuf;
-	if (qword_get_uint(&cp, &uid) != 0)
+	bp = buf;
+	if (qword_get_uint(&bp, &uid) != 0)
 		return;
 
 	pw = getpwuid(uid);
@@ -180,15 +187,19 @@  static void auth_unix_gid(FILE *f)
 			}
 		}
 	}
-	qword_printuint(f, uid);
-	qword_printtimefrom(f, DEFAULT_TTL);
+
+	bp = buf; blen = sizeof(buf);
+	qword_adduint(&bp, &blen, uid);
+	qword_adduint(&bp, &blen, time(0) + DEFAULT_TTL);
 	if (rv >= 0) {
-		qword_printuint(f, ngroups);
+		qword_adduint(&bp, &blen, ngroups);
 		for (i=0; i<ngroups; i++)
-			qword_printuint(f, groups[i]);
+			qword_adduint(&bp, &blen, groups[i]);
 	} else
-		qword_printuint(f, 0);
-	qword_eol(f);
+		qword_adduint(&bp, &blen, 0);
+	qword_addeol(&bp, &blen);
+	if (blen <= 0 || write(f, buf, bp - buf) != bp - buf)
+		xlog(L_ERROR, "auth_unix_gid: error writing reply");
 }
 
 #if USE_BLKID
@@ -659,14 +670,13 @@  static struct addrinfo *lookup_client_addr(char *dom)
 	return ret;
 }
 
-static void nfsd_fh(FILE *f)
+static void nfsd_fh(int f)
 {
 	/* request are:
 	 *  domain fsidtype fsid
 	 * interpret fsid, find export point and options, and write:
 	 *  domain fsidtype fsid expiry path
 	 */
-	char *cp;
 	char *dom;
 	int fsidtype;
 	int fsidlen;
@@ -678,24 +688,27 @@  static void nfsd_fh(FILE *f)
 	nfs_export *exp;
 	int i;
 	int dev_missing = 0;
+	char buf[RPC_CHAN_BUF_SIZE], *bp;
+	int blen;
 
-	if (readline(fileno(f), &lbuf, &lbuflen) != 1)
-		return;
+	blen = read(f, buf, sizeof(buf));
+	if (blen <= 0 || buf[blen-1] != '\n') return;
+	buf[blen-1] = 0;
 
-	xlog(D_CALL, "nfsd_fh: inbuf '%s'", lbuf);
+	xlog(D_CALL, "nfsd_fh: inbuf '%s'", buf);
 
-	cp = lbuf;
+	bp = buf;
 
-	dom = malloc(strlen(cp));
+	dom = malloc(blen);
 	if (dom == NULL)
 		return;
-	if (qword_get(&cp, dom, strlen(cp)) <= 0)
+	if (qword_get(&bp, dom, blen) <= 0)
 		goto out;
-	if (qword_get_int(&cp, &fsidtype) != 0)
+	if (qword_get_int(&bp, &fsidtype) != 0)
 		goto out;
 	if (fsidtype < 0 || fsidtype > 7)
 		goto out; /* unknown type */
-	if ((fsidlen = qword_get(&cp, fsid, 32)) <= 0)
+	if ((fsidlen = qword_get(&bp, fsid, 32)) <= 0)
 		goto out;
 	if (parse_fsid(fsidtype, fsidlen, fsid, &parsed))
 		goto out;
@@ -796,12 +809,13 @@  static void nfsd_fh(FILE *f)
 	}
 
 	if (found)
-		if (cache_export_ent(dom, found, found_path) < 0)
+		if (cache_export_ent(buf, sizeof(buf), dom, found, found_path) < 0)
 			found = 0;
 
-	qword_print(f, dom);
-	qword_printint(f, fsidtype);
-	qword_printhex(f, fsid, fsidlen);
+	bp = buf; blen = sizeof(buf);
+	qword_add(&bp, &blen, dom);
+	qword_addint(&bp, &blen, fsidtype);
+	qword_addhex(&bp, &blen, fsid, fsidlen);
 	/* The fsid -> path lookup can be quite expensive as it
 	 * potentially stats and reads lots of devices, and some of those
 	 * might have spun-down.  The Answer is not likely to
@@ -810,20 +824,21 @@  static void nfsd_fh(FILE *f)
 	 * timeout.  Maybe this should be configurable on the command
 	 * line.
 	 */
-	qword_printint(f, 0x7fffffff);
+	qword_addint(&bp, &blen, 0x7fffffff);
 	if (found)
-		qword_print(f, found_path);
-	qword_eol(f);
- out:
+		qword_add(&bp, &blen, found_path);
+	qword_addeol(&bp, &blen);
+	if (blen <= 0 || write(f, buf, bp - buf) != bp - buf)
+		xlog(L_ERROR, "nfsd_fh: error writing reply");
+out:
 	if (found_path)
 		free(found_path);
 	freeaddrinfo(ai);
 	free(dom);
 	xlog(D_CALL, "nfsd_fh: found %p path %s", found, found ? found->e_path : NULL);
-	return;		
 }
 
-static void write_fsloc(FILE *f, struct exportent *ep)
+static void write_fsloc(char **bp, int *blen, struct exportent *ep)
 {
 	struct servers *servers;
 
@@ -833,20 +848,20 @@  static void write_fsloc(FILE *f, struct exportent *ep)
 	servers = replicas_lookup(ep->e_fslocmethod, ep->e_fslocdata);
 	if (!servers)
 		return;
-	qword_print(f, "fsloc");
-	qword_printint(f, servers->h_num);
+	qword_add(bp, blen, "fsloc");
+	qword_addint(bp, blen, servers->h_num);
 	if (servers->h_num >= 0) {
 		int i;
 		for (i=0; i<servers->h_num; i++) {
-			qword_print(f, servers->h_mp[i]->h_host);
-			qword_print(f, servers->h_mp[i]->h_path);
+			qword_add(bp, blen, servers->h_mp[i]->h_host);
+			qword_add(bp, blen, servers->h_mp[i]->h_path);
 		}
 	}
-	qword_printint(f, servers->h_referral);
+	qword_addint(bp, blen, servers->h_referral);
 	release_replicas(servers);
 }
 
-static void write_secinfo(FILE *f, struct exportent *ep, int flag_mask)
+static void write_secinfo(char **bp, int *blen, struct exportent *ep, int flag_mask)
 {
 	struct sec_entry *p;
 
@@ -857,45 +872,52 @@  static void write_secinfo(FILE *f, struct exportent *ep, int flag_mask)
 		return;
 	}
 	fix_pseudoflavor_flags(ep);
-	qword_print(f, "secinfo");
-	qword_printint(f, p - ep->e_secinfo);
+	qword_add(bp, blen, "secinfo");
+	qword_addint(bp, blen, p - ep->e_secinfo);
 	for (p = ep->e_secinfo; p->flav; p++) {
-		qword_printint(f, p->flav->fnum);
-		qword_printint(f, p->flags & flag_mask);
+		qword_addint(bp, blen, p->flav->fnum);
+		qword_addint(bp, blen, p->flags & flag_mask);
 	}
 
 }
 
-static int dump_to_cache(FILE *f, char *domain, char *path, struct exportent *exp)
+static int dump_to_cache(int f, char *buf, int buflen, char *domain, char *path, struct exportent *exp)
 {
-	qword_print(f, domain);
-	qword_print(f, path);
+	char *bp = buf;
+	int blen = buflen;
+	time_t now = time(0);
+
+	qword_add(&bp, &blen, domain);
+	qword_add(&bp, &blen, path);
 	if (exp) {
 		int different_fs = strcmp(path, exp->e_path) != 0;
 		int flag_mask = different_fs ? ~NFSEXP_FSID : ~0;
 
-		qword_printtimefrom(f, exp->e_ttl);
-		qword_printint(f, exp->e_flags & flag_mask);
-		qword_printint(f, exp->e_anonuid);
-		qword_printint(f, exp->e_anongid);
-		qword_printint(f, exp->e_fsid);
-		write_fsloc(f, exp);
-		write_secinfo(f, exp, flag_mask);
- 		if (exp->e_uuid == NULL || different_fs) {
- 			char u[16];
- 			if (uuid_by_path(path, 0, 16, u)) {
- 				qword_print(f, "uuid");
- 				qword_printhex(f, u, 16);
- 			}
- 		} else {
- 			char u[16];
- 			get_uuid(exp->e_uuid, 16, u);
- 			qword_print(f, "uuid");
- 			qword_printhex(f, u, 16);
- 		}
+		qword_adduint(&bp, &blen, now + exp->e_ttl);
+		qword_addint(&bp, &blen, exp->e_flags & flag_mask);
+		qword_addint(&bp, &blen, exp->e_anonuid);
+		qword_addint(&bp, &blen, exp->e_anongid);
+		qword_addint(&bp, &blen, exp->e_fsid);
+		write_fsloc(&bp, &blen, exp);
+		write_secinfo(&bp, &blen, exp, flag_mask);
+		if (exp->e_uuid == NULL || different_fs) {
+			char u[16];
+			if (uuid_by_path(path, 0, 16, u)) {
+				qword_add(&bp, &blen, "uuid");
+				qword_addhex(&bp, &blen, u, 16);
+			}
+		} else {
+			char u[16];
+			get_uuid(exp->e_uuid, 16, u);
+			qword_add(&bp, &blen, "uuid");
+			qword_addhex(&bp, &blen, u, 16);
+		}
 	} else
-		qword_printtimefrom(f, DEFAULT_TTL);
-	return qword_eol(f);
+		qword_adduint(&bp, &blen, now + DEFAULT_TTL);
+	qword_addeol(&bp, &blen);
+	if (blen <= 0) return -1;
+	if (write(f, buf, bp - buf) != bp - buf) return -1;
+	return 0;
 }
 
 static nfs_export *
@@ -1245,27 +1267,27 @@  static struct exportent *lookup_junction(char *dom, const char *pathname,
 	return exp;
 }
 
-static void lookup_nonexport(FILE *f, char *dom, char *path,
+static void lookup_nonexport(int f, char *buf, int buflen, char *dom, char *path,
 		struct addrinfo *ai)
 {
 	struct exportent *eep;
 
 	eep = lookup_junction(dom, path, ai);
-	dump_to_cache(f, dom, path, eep);
+	dump_to_cache(f, buf, buflen, dom, path, eep);
 	if (eep == NULL)
 		return;
 	exportent_release(eep);
 	free(eep);
 }
 #else	/* !HAVE_NFS_PLUGIN_H */
-static void lookup_nonexport(FILE *f, char *dom, char *path,
+static void lookup_nonexport(int f, char *buf, int buflen, char *dom, char *path,
 		struct addrinfo *UNUSED(ai))
 {
-	dump_to_cache(f, dom, path, NULL);
+	dump_to_cache(f, buf, buflen, dom, path, NULL);
 }
 #endif	/* !HAVE_NFS_PLUGIN_H */
 
-static void nfsd_export(FILE *f)
+static void nfsd_export(int f)
 {
 	/* requests are:
 	 *  domain path
@@ -1273,26 +1295,28 @@  static void nfsd_export(FILE *f)
 	 *  domain path expiry flags anonuid anongid fsid
 	 */
 
-	char *cp;
 	char *dom, *path;
 	nfs_export *found = NULL;
 	struct addrinfo *ai = NULL;
+	char buf[RPC_CHAN_BUF_SIZE], *bp;
+	int blen;
 
-	if (readline(fileno(f), &lbuf, &lbuflen) != 1)
-		return;
+	blen = read(f, buf, sizeof(buf));
+	if (blen <= 0 || buf[blen-1] != '\n') return;
+	buf[blen-1] = 0;
 
-	xlog(D_CALL, "nfsd_export: inbuf '%s'", lbuf);
+	xlog(D_CALL, "nfsd_export: inbuf '%s'", buf);
 
-	cp = lbuf;
-	dom = malloc(strlen(cp));
-	path = malloc(strlen(cp));
+	bp = buf;
+	dom = malloc(blen);
+	path = malloc(blen);
 
 	if (!dom || !path)
 		goto out;
 
-	if (qword_get(&cp, dom, strlen(lbuf)) <= 0)
+	if (qword_get(&bp, dom, blen) <= 0)
 		goto out;
-	if (qword_get(&cp, path, strlen(lbuf)) <= 0)
+	if (qword_get(&bp, path, blen) <= 0)
 		goto out;
 
 	auth_reload();
@@ -1306,14 +1330,14 @@  static void nfsd_export(FILE *f)
 	found = lookup_export(dom, path, ai);
 
 	if (found) {
-		if (dump_to_cache(f, dom, path, &found->m_export) < 0) {
+		if (dump_to_cache(f, buf, sizeof(buf), dom, path, &found->m_export) < 0) {
 			xlog(L_WARNING,
 			     "Cannot export %s, possibly unsupported filesystem"
 			     " or fsid= required", path);
-			dump_to_cache(f, dom, path, NULL);
+			dump_to_cache(f, buf, sizeof(buf), dom, path, NULL);
 		}
 	} else
-		lookup_nonexport(f, dom, path, ai);
+		lookup_nonexport(f, buf, sizeof(buf), dom, path, ai);
 
  out:
 	xlog(D_CALL, "nfsd_export: found %p path %s", found, path ? path : NULL);
@@ -1325,15 +1349,14 @@  static void nfsd_export(FILE *f)
 
 struct {
 	char *cache_name;
-	void (*cache_handle)(FILE *f);
-	FILE *f;
-	char vbuf[RPC_CHAN_BUF_SIZE];
+	void (*cache_handle)(int f);
+	int f;
 } cachelist[] = {
-	{ "auth.unix.ip", auth_unix_ip, NULL, ""},
-	{ "auth.unix.gid", auth_unix_gid, NULL, ""},
-	{ "nfsd.export", nfsd_export, NULL, ""},
-	{ "nfsd.fh", nfsd_fh, NULL, ""},
-	{ NULL, NULL, NULL, ""}
+	{ "auth.unix.ip", auth_unix_ip, -1 },
+	{ "auth.unix.gid", auth_unix_gid, -1 },
+	{ "nfsd.export", nfsd_export, -1 },
+	{ "nfsd.fh", nfsd_fh, -1 },
+	{ NULL, NULL, -1 }
 };
 
 extern int manage_gids;
@@ -1350,11 +1373,7 @@  void cache_open(void)
 		if (!manage_gids && cachelist[i].cache_handle == auth_unix_gid)
 			continue;
 		sprintf(path, "/proc/net/rpc/%s/channel", cachelist[i].cache_name);
-		cachelist[i].f = fopen(path, "r+");
-		if (cachelist[i].f != NULL) {
-			setvbuf(cachelist[i].f, cachelist[i].vbuf, _IOLBF, 
-				RPC_CHAN_BUF_SIZE);
-		}
+		cachelist[i].f = open(path, O_RDWR);
 	}
 }
 
@@ -1366,8 +1385,8 @@  void cache_set_fds(fd_set *fdset)
 {
 	int i;
 	for (i=0; cachelist[i].cache_name; i++) {
-		if (cachelist[i].f)
-			FD_SET(fileno(cachelist[i].f), fdset);
+		if (cachelist[i].f >= 0)
+			FD_SET(cachelist[i].f, fdset);
 	}
 }
 
@@ -1380,11 +1399,11 @@  int cache_process_req(fd_set *readfds)
 	int i;
 	int cnt = 0;
 	for (i=0; cachelist[i].cache_name; i++) {
-		if (cachelist[i].f != NULL &&
-		    FD_ISSET(fileno(cachelist[i].f), readfds)) {
+		if (cachelist[i].f >= 0 &&
+		    FD_ISSET(cachelist[i].f, readfds)) {
 			cnt++;
 			cachelist[i].cache_handle(cachelist[i].f);
-			FD_CLR(fileno(cachelist[i].f), readfds);
+			FD_CLR(cachelist[i].f, readfds);
 		}
 	}
 	return cnt;
@@ -1397,14 +1416,14 @@  int cache_process_req(fd_set *readfds)
  * % echo $domain $path $[now+DEFAULT_TTL] $options $anonuid $anongid $fsid > /proc/net/rpc/nfsd.export/channel
  */
 
-static int cache_export_ent(char *domain, struct exportent *exp, char *path)
+static int cache_export_ent(char *buf, int buflen, char *domain, struct exportent *exp, char *path)
 {
-	int err;
-	FILE *f = fopen("/proc/net/rpc/nfsd.export/channel", "w");
-	if (!f)
-		return -1;
+	int f, err;
+
+	f = open("/proc/net/rpc/nfsd.export/channel", O_WRONLY);
+	if (f < 0) return -1;
 
-	err = dump_to_cache(f, domain, exp->e_path, exp);
+	err = dump_to_cache(f, buf, buflen, domain, exp->e_path, exp);
 	if (err) {
 		xlog(L_WARNING,
 		     "Cannot export %s, possibly unsupported filesystem or"
@@ -1445,13 +1464,13 @@  static int cache_export_ent(char *domain, struct exportent *exp, char *path)
 				continue;
 			dev = stb.st_dev;
 			path[l] = 0;
-			dump_to_cache(f, domain, path, exp);
+			dump_to_cache(f, buf, buflen, domain, path, exp);
 			path[l] = c;
 		}
 		break;
 	}
 
-	fclose(f);
+	close(f);
 	return err;
 }
 
@@ -1462,27 +1481,25 @@  static int cache_export_ent(char *domain, struct exportent *exp, char *path)
  */
 int cache_export(nfs_export *exp, char *path)
 {
-	char buf[INET6_ADDRSTRLEN];
-	int err;
-	FILE *f;
+	char ip[INET6_ADDRSTRLEN];
+	char buf[RPC_CHAN_BUF_SIZE], *bp;
+	int blen, f;
 
-	f = fopen("/proc/net/rpc/auth.unix.ip/channel", "w");
-	if (!f)
+	f = open("/proc/net/rpc/auth.unix.ip/channel", O_WRONLY);
+	if (f < 0)
 		return -1;
 
-
-	qword_print(f, "nfsd");
-	qword_print(f,
-		host_ntop(get_addrlist(exp->m_client, 0), buf, sizeof(buf)));
-	qword_printtimefrom(f, exp->m_export.e_ttl);
-	qword_print(f, exp->m_client->m_hostname);
-	err = qword_eol(f);
-	
-	fclose(f);
-
-	err = cache_export_ent(exp->m_client->m_hostname, &exp->m_export, path)
-		|| err;
-	return err;
+	bp = buf, blen = sizeof(buf);
+	qword_add(&bp, &blen, "nfsd");
+	qword_add(&bp, &blen, host_ntop(get_addrlist(exp->m_client, 0), ip, sizeof(ip)));
+	qword_adduint(&bp, &blen, time(0) + exp->m_export.e_ttl);
+	qword_add(&bp, &blen, exp->m_client->m_hostname);
+	qword_addeol(&bp, &blen);
+	if (blen <= 0 || write(f, buf, bp - buf) != bp - buf) blen = -1;
+	close(f);
+	if (blen < 0) return -1;
+
+	return cache_export_ent(buf, sizeof(buf), exp->m_client->m_hostname, &exp->m_export, path);
 }
 
 /**
@@ -1501,27 +1518,33 @@  int cache_export(nfs_export *exp, char *path)
 struct nfs_fh_len *
 cache_get_filehandle(nfs_export *exp, int len, char *p)
 {
-	FILE *f = fopen("/proc/fs/nfsd/filehandle", "r+");
-	char buf[200];
-	char *bp = buf;
-	int failed;
 	static struct nfs_fh_len fh;
+	char buf[RPC_CHAN_BUF_SIZE], *bp;
+	int blen, f;
+
+	f = open("/proc/fs/nfsd/filehandle", O_RDWR);
+	if (f < 0) {
+		f = open("/proc/fs/nfs/filehandle", O_RDWR);
+		if (f < 0) return NULL;
+	}
 
-	if (!f)
-		f = fopen("/proc/fs/nfs/filehandle", "r+");
-	if (!f)
+	bp = buf, blen = sizeof(buf);
+	qword_add(&bp, &blen, exp->m_client->m_hostname);
+	qword_add(&bp, &blen, p);
+	qword_addint(&bp, &blen, len);
+	qword_addeol(&bp, &blen);
+	if (blen <= 0 || write(f, buf, bp - buf) != bp - buf) {
+		close(f);
 		return NULL;
+	}
+	bp = buf;
+	blen = read(f, buf, sizeof(buf));
+	close(f);
 
-	qword_print(f, exp->m_client->m_hostname);
-	qword_print(f, p);
-	qword_printint(f, len);	
-	failed = qword_eol(f);
-	
-	if (!failed)
-		failed = (fgets(buf, sizeof(buf), f) == NULL);
-	fclose(f);
-	if (failed)
+	if (blen <= 0 || buf[blen-1] != '\n')
 		return NULL;
+	buf[blen-1] = 0;
+
 	memset(fh.fh_handle, 0, sizeof(fh.fh_handle));
 	fh.fh_size = qword_get(&bp, (char *)fh.fh_handle, NFS3_FHSIZE);
 	return &fh;