# HG changeset patch # User Francisco J Ballesteros # Date 1316098961 -7200 # Node ID f2b04317d2acb1288f0e15ed399604b6e500a7f7 # Parent 226dd3948144feec113bfca1107104df58637256 trfs. sent again... R=nix-dev CC=nix-dev http://codereview.appspot.com/5004051 diff -r 226dd3948144 -r f2b04317d2ac sys/man/4/trfs --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/sys/man/4/trfs Thu Sep 15 17:02:41 2011 +0200 @@ -0,0 +1,90 @@ +.TH TRFS 4 +.SH NAME +trfs \- translate spaces and other runes in file names file system +.SH SYNOPSIS +.B trfs +[ +.B -Dv +] +[ +.B -\fIRune\fP +] +[ +.B -s +.I srvfile +] +[ +.B -n +.I addr +] +.I servename +.SH DESCRIPTION +.I Trfs +translates 9P requests exchanged between a client and a file +server. The translation replaces spaces seen in remote file names +to a different rune. The same is done with left and right parenthesis, +ampersands and single quotes. +.PP +The rune used instead of space within file names +can be selected by specifying it as an option. +.PP +.I Trfs +reaches the server through the +.I servename +file or through a network connection to the +.I addr +address given to the +.B -n +option. +The client mounts the file posted by +.I trfs +in +.IR srv (4). +The file posted is named +.B /srv/trfs +by default, but it can be +.B /srv/\fIsrvfile\fP +when the option +.B -s +is used, and also +.B /srv/\fIaddr\fP when the option +.B -n +is given. +.PP +Option +.B -v +instructs +.I trfs +to print to standard error a human readable copy of messages exchanged. This +makes +.I trfs +useful to debug file servers. The +.B -D +option enables debug messages. +.SH EXAMPLE +These examples dial a foreign system, start +.IR trfs , +and mount it at +.B /n/remote. +Names like +.B "/n/remote/music/alan parsons" +that contaning blanks +would be renamed to names like +.B "/n/remote/music/alan␣parsons." +.PP +.EX + srv tcp!foreign + trfs -s foreign.tr /srv/tcp!foreign + mount -c /srv/foreign.tr /n/remote + + trfs -n tcp!foreign + mount -c /srv/tcp!foreign /n/remote +.EE +.SH SOURCE +.B /sys/src/cmd/trfs.c +.SH BUGS +Spaces in remote file names are not caught when found inside file +names or other media. The rune used instead of space might be used +in remote file names leading to confussion. The same happens with remaining +runes subject to translation. + diff -r 226dd3948144 -r f2b04317d2ac sys/src/cmd/trfs.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/sys/src/cmd/trfs.c Thu Sep 15 17:02:41 2011 +0200 @@ -0,0 +1,432 @@ +#include +#include +#include +#include +#include <9p.h> + + +enum { + Maxmsglen = IOHDRSZ + 8 * 1024, // Max message size + Nnames = 128, // Max # of names in a message +}; + +#define dprint if(debug)print + +Rune altspc = L'·'; +Rune altlparen = L'«'; +Rune altrparen = L'»'; +Rune altquote = L'´'; +Rune altamp = L'­'; + +int debug; +int verbose; + +uchar rxbuf[Maxmsglen]; +uchar txbuf[Maxmsglen]; +char statbuf[Maxmsglen]; +char dirbuf[2*Maxmsglen]; + +Fidpool*fidpool; + + +static void +usage(void) +{ + fprint(2, "usage: %s [-Dv] [-RUNE] [-s srvfile] [-n addr] servename\n", argv0); + exits("usage"); +} + + +static ulong +getaux(Fid* fp) +{ + assert(sizeof(ulong) <= sizeof(fp->aux)); + return (ulong)fp->aux; +} + +static void +setaux(Fid* fp, ulong aux) +{ + assert(sizeof(ulong) <= sizeof(fp->aux)); + fp->aux = (void*) aux; +} + +static char* +emalloc(int l) +{ + char* r; + r = malloc(l); + if (r == nil) + sysfatal("not enough memory"); + return r; +} + +int nnames; +char* names[Nnames]; + +void +cleannames(void) +{ + int i; + + for (i = 0; i < nnames; i++) + free(names[i]); + nnames = 0; +} + +/* From Plan 9 to Unix */ +char* +exportname(char* name) +{ + Rune r; + int nr; + char *uxname; + char *up; + + if (name == 0 || + (utfrune(name, altspc) == 0 && + utfrune(name,altlparen) == 0 && + utfrune(name,altrparen) == 0 && + utfrune(name,altamp) == 0 && + utfrune(name,altquote) == 0)) + return name; + up = uxname = emalloc(strlen(name) + 1); + names[nnames++] = uxname; + while(*name != 0){ + nr = chartorune(&r, name); + if (r == altspc) + r = ' '; + if (r == altlparen) + r = '('; + if (r == altrparen) + r = ')'; + if (r == altamp) + r = '&'; + if (r == altquote) + r = '\''; + up += runetochar(up, &r); + name += nr; + } + *up = 0; + return uxname; +} + +/* From Unix to Plan 9 */ +char* +importname(char* name) +{ + Rune r; + int nr; + char *up; + char *p9name; + + if (name == 0 || + (strchr(name, ' ') == 0 && strchr(name, '(') == 0 && + strchr(name, ')') == 0 && strchr(name, '&') == 0 && + strchr(name, '\'')== 0)) + return name; + p9name = emalloc(strlen(name) * 3 + 1); // worst case: all blanks + 0 + up = p9name; + names[nnames++] = p9name; + while (*name != 0){ + nr = chartorune(&r, name); + if (r == ' ') + r = altspc; + if (r == '(') + r = altlparen; + if (r == ')') + r = altrparen; + if (r == '&') + r = altamp; + if (r == '\'') + r = altquote; + up += runetochar(up, &r); + name += nr; + } + *up = 0; + return p9name; +} + +static int +isfdir(Fcall* f, Fid **fpp) +{ + Fid* fp; + int r; + fp = lookupfid(fidpool, f->fid); + if (fp == nil) + return 0; + r = (fp->qid.type&QTDIR); + if (r) + *fpp = fp; + else + closefid(fp); + return r; +} + +static int +getfcall(int fd, Fcall* f) +{ + int r; + + r = read9pmsg(fd, rxbuf, sizeof(rxbuf)); + if (r <= 0) + return 0; + if (convM2S(rxbuf, sizeof(rxbuf), f) == 0) + return -1; + return 1; +} + +static int +putfcall(int fd, Fcall* f) +{ + int n; + + n = convS2M(f, txbuf, sizeof(txbuf)); + if (n == 0) + return -1; + if (write(fd, txbuf, n) != n) + return -1; + return n; +} + +static void +twalk(Fcall* f) +{ + int i; + + cleannames(); + for (i = 0; i < f->nwname; i++) + f->wname[i] = exportname(f->wname[i]); +} + +static void +tcreate(Fcall* f) +{ + cleannames(); + f->name = exportname(f->name); +} + + +// Dir read is tricky. +// We have to change the user supplied offset to match the sizes +// seen by the server. Sizes seen by client are greater than those +// seen by server since the change from ' ' to '␣' adds 2 bytes. +static void +tread(Fcall* f) +{ + Fid* fp; + + fp = nil; + if (!isfdir(f, &fp)) + return; + f->count /= 3; // sizes will grow upon return. + if (fp == nil) + sysfatal("can't find fid\n"); + if (f->offset == 0) + setaux(fp, 0); + f->offset -= getaux(fp); // cumulative size delta + closefid(fp); +} + +static void +rread(Fcall* f) +{ + ulong n, rn, nn, delta; + Dir d; + Fid* fp; + + if (!isfdir(f, &fp)) + return; + if (f->count == 0) + goto done; + cleannames(); + for (n = nn = 0; n < f->count; n += rn){ + rn = convM2D((uchar*)f->data + n, f->count - n, &d, statbuf); + if (rn <= BIT16SZ) + break; + d.name = importname(d.name); + //dprint("⇒ %D\n", &d); + nn += convD2M(&d, (uchar*)dirbuf + nn, sizeof(dirbuf) - nn); + } + delta = nn - n; + setaux(fp, getaux(fp) + delta); + f->count = nn; + f->data = dirbuf; +done: + closefid(fp); +} + +static void +twstat(Fcall* f) +{ + Dir d; + + cleannames(); + if (convM2D(f->stat, f->nstat, &d, statbuf) <= BIT16SZ) + return; + d.name = exportname(d.name); + f->nstat = convD2M(&d, (uchar*)dirbuf, sizeof(dirbuf)); + f->stat = (uchar*)dirbuf; + if (statcheck(f->stat, f->nstat) < 0) + dprint("stat fails\n"); +} + +static void +rstat(Fcall* f) +{ + Dir d; + + cleannames(); + convM2D(f->stat, f->nstat, &d, statbuf); + d.name = importname(d.name); + f->nstat = convD2M(&d, (uchar*)dirbuf, sizeof(dirbuf)); + f->stat = (uchar*)dirbuf; + if (statcheck(f->stat, f->nstat) < 0) + dprint("stat fails\n"); +} + +void +nop(Fid*) +{ +} + + +static void +service(int cfd, int sfd, int dfd) +{ + Fcall f; + int r; + Fid* fp; + + fidpool = allocfidpool(nop); + for(;;){ + fp = nil; + r = getfcall(cfd, &f); + if (r <= 0){ + fprint(dfd, "trfs: getfcall %r\n"); + break; + } + if(verbose) + fprint(dfd , "c→s %F\n", &f); + switch(f.type){ + case Tclunk: + case Tremove: + // BUG in lib9p? removefid leaks fid. + // is that what it should do? + fp = lookupfid(fidpool, f.fid); + if (fp != nil){ + removefid(fidpool, f.fid); + closefid(fp); + closefid(fp); + fp = nil; + } + break; + case Tcreate: + tcreate(&f); + // and also... + case Topen: + fp = allocfid(fidpool, f.fid); + fp->aux = 0; + break; + case Tread: + tread(&f); + break; + case Twalk: + twalk(&f); + break; + case Twstat: + twstat(&f); + break; + } + if(verbose && debug) + fprint(dfd , "c→s %F\n", &f); + if (putfcall(sfd, &f) < 0) + fprint(dfd , "can't putfcall: %r\n"); + + + r = getfcall(sfd, &f); + if (r <= 0){ + fprint(dfd, "trfs: 2nd getfcall %r\n"); + break; + } + if (verbose) + fprint(dfd, "c←s %F\n", &f); + switch(f.type){ + case Ropen: + case Rcreate: + fp->qid = f.qid; + break; + case Rread: + rread(&f); + break; + case Rstat: + rstat(&f); + break; + } + if(verbose && debug) + fprint(dfd , "c←s %F\n", &f); + if (putfcall(cfd, &f) < 0) + fprint(dfd , "can't 2n dputfcall: %r\n"); + if (fp != nil) + closefid(fp); + } +} + +void +main(int argc, char* argv[]) +{ + char* srv = nil; + char* sname = nil; + char* addr = nil; + int fd; + int p[2]; + + ARGBEGIN{ + case 'D': + debug = 1; + break; + case 'n': + addr = EARGF(usage()); + break; + case 'v': + verbose = 1; + break; + case 's': + sname = EARGF(usage()); + break; + default: + altspc = ARGC(); + }ARGEND; + if (addr == nil){ + if (argc < 1) + usage(); + srv = *argv; + argc--; + } + if (argc > 0) + usage(); + if (sname == nil) + sname = (addr != nil) ? addr : "trfs"; + fmtinstall('D', dirfmt); + fmtinstall('M', dirmodefmt); + fmtinstall('F', fcallfmt); + + if (addr == nil) + fd = open(srv, ORDWR); + else + fd = dial(netmkaddr(addr, "net", "9fs"), 0, 0, 0); + if (fd < 0 || pipe(p) < 0) + sysfatal("can't connect to server %s: %r\n", (addr?addr:srv)); + if (postfd(sname, p[0]) < 0) + sysfatal("can't post srv: %r\n"); + rfork(RFNOTEG); + switch(rfork(RFPROC|RFNOTEG)){ + case 0: + service(p[1], fd, 2); + break; + case -1: + sysfatal("can't fork server: %r\n"); + break; + } + exits(nil); +}