#include <u.h>
#include <libc.h>
#include <auth.h>
#include <fcall.h>
#include <thread.h>
#include <9p.h>
#include "ncp.h"
#define min(a,b) (((a)<(b))?(a):(b))
char Host[OBJNAMLEN]; // host connect to
int Numvol; // number of volumes
int Debug; // Packet hex-dump
int Maxio; // Maximum IO chunk size
int Slip; // time slip in secs between server and us
int Fsver; // server software version * 1000
int Bigfiles; // flag: 64 bit files possible on this server
Qid Root; // root of remote system
Session *Sess; // netware session
ulong Myuid; // my UID
enum {
RIMstat = RIMname|RIMattr|RIMsize|RIMarch|RIMmodif|
RIMcreate|RIMns|RIMdir|RIMrights,
DARnewdir = DARreadexisting|DARwriteexisting|DARoldopenexisting|
DARcreatenewentry|DARdeleteexisting|DARchangeacl|DARsearch|
DARmodify|DARsuporvisor,
DARnewfile = DARreadonly|DARwriteonly,
RIMqid = RIMattr|RIMmodif|RIMdir,
RIMnone = 0,
};
typedef struct NWdir NWdir;
typedef struct Aux Aux;
struct Aux {
char *path; // full path fo file
long expire; // expiration time of cache
long off; // file pos of start of cache
long end; // file pos of end of cache
char *cache;
union {
Srch srch; // NCP dir search state
Fh fh; // file handle
};
};
static void
responderrstr(Req *r)
{
char e[ERRMAX];
rerrstr(e, sizeof e);
respond(r, e);
}
/*
* used only for root dir and volumes
* to which we have no rights
*/
static void
V2D(Dir *d, ulong vol, char *name)
{
memset(d, 0, sizeof(Dir));
d->type = 'N';
d->dev = vol;
d->name = strlwr(estrdup9p(name));
d->uid = estrdup9p("supervisor");
d->muid = estrdup9p("supervisor");
d->gid = estrdup9p("none");
d->mode = DMDIR; // zero mode, no rights
d->qid.vers = Vvol;
d->qid.path = vol;
d->qid.type = QTDIR;
}
static Qid
I2Qid( FInfo *i)
{
Qid q;
q.path = i->dirent;
q.vers = i->modified;
q.type = (i->attr & FAdirectory)? QTDIR: QTFILE;
return q;
}
static void
I2D(Dir *d, FInfo *i)
{
char *u, *g;
memset(d, 0, sizeof(Dir));
d->type = 'N';
d->dev = i->vol;
d->name = estrdup9p(i->name);
if (i->creatns == NSdos)
strlwr(d->name);
uid2names(Sess, i->creator, &u, &g);
d->uid = estrdup9p(u);
d->gid = estrdup9p(g);
uid2names(Sess, i->modifier, &u, &g);
d->muid = estrdup9p(u);
d->atime = i->accessed;
d->mtime = i->modified;
d->qid = I2Qid(i);
if (i->attr & FAdirectory){
d->length = 0;
d->mode = (i->rights & ERall)? (DMDIR|0777): (DMDIR|0555);
}
else{ d->length = i->size;
d->mode = (i->attr & FAreadonly)? 0444: 0666;
}
}
static char *
newpath(char *path, char *name)
{
char *p, *q;
assert((p = strrchr(path, '/')) != nil);
if (strcmp(name, "..") == 0){
if (p == path)
return estrdup9p("/");
q = emalloc9p((p-path)+1);
strecpy(q, q+(p-path)+1, path);
return q;
}
if (strcmp(path, "/") == 0)
return smprint("/%s", name);
return smprint("%s/%s", path, name);
}
static int
dodelete(char *s)
{
FInfo i;
Mfi mfi;
char e[ERRMAX];
/*
* Netware won't delete readonly files
*/
memset(&i, 0, sizeof i);
if (ObtainFileOrDirInfo(Sess, NSlong, NSlong, RIMattr, s, &i) == -1){
return -1;
}
memset(&mfi, 0, sizeof mfi);
mfi.attr = FAnormal | (i.attr & FAdirectory);
ModifyFileOrDirInfo(Sess, NSlong, SAall, MFIattr, &mfi, s);
if (DeteleFileOrDir(Sess, NSlong, SAall, s) == -1){
errstr(e, sizeof e);
mfi.attr = i.attr;
ModifyFileOrDirInfo(Sess, NSlong, SAall, MFIattr, &mfi, s);
errstr(e, sizeof e);
return -1;
}
return 0;
}
static int
dorename(char *src, char *dst)
{
int sa;
FInfo i;
Mfi mfi;
char e[ERRMAX];
/*
* Netware won't rename of readonly files
*/
memset(&i, 0, sizeof i);
if (ObtainFileOrDirInfo(Sess, NSlong, NSlong, RIMattr, src, &i) == -1)
return -1;
memset(&mfi, 0, sizeof mfi);
mfi.attr = FAnormal | (i.attr & FAdirectory);
if (ModifyFileOrDirInfo(Sess, NSlong, SAall, MFIattr, &mfi, src) == -1)
return -1;
/*
* I don't understand why this is nescessary, but rename fails
* on subdirs if we simply pass SAall as the search attribute.
*/
sa = (i.attr & FAdirectory)? SAsubdironly: SAsubdirfiles;
if (RenameOrMoveFileOrDir(Sess, NSlong, 1, sa, src, dst) == -1){
errstr(e, sizeof e);
memset(&mfi, 0, sizeof mfi);
mfi.attr = i.attr;
ModifyFileOrDirInfo(Sess, NSlong, SAall, MFIattr, &mfi, src);
errstr(e, sizeof e);
return -1;
}
memset(&mfi, 0, sizeof mfi);
mfi.attr = i.attr;
ModifyFileOrDirInfo(Sess, NSlong, SAall, MFIattr, &mfi, dst);
return 0;
}
int
doreopen(Fid *f) /* as we must close open files during rename */
{
FInfo i;
int oca;
Aux *a = f->aux;
int dar = DARcompatibility;
switch (f->omode & OMASK){
case OREAD:
dar |= DARreadonly;
break;
case OWRITE:
dar |= DARwriteonly;
break;
case ORDWR:
dar |= DARreadonly|DARwriteonly;
break;
case OEXEC:
dar |= DARreadonly;
break;
}
if (OpenCreateFileOrSubdir(Sess, NSlong, OCMopen, SAall, RIMnone, dar,
FAnormal, a->path, &oca, a->fh, &i) == -1)
return -1;
return 0;
}
int
dotruncate(char *path, ulong len)
{
Fh fh;
FInfo i;
int err, oca;
char buf[1];
if (OpenCreateFileOrSubdir(Sess, NSlong, OCMopen, SAall, RIMnone,
DARreadonly|DARwriteonly, FAnormal, path, &oca, fh, &i) == -1){
return -1;
}
err = WriteToAFile(Sess, fh, buf, 0, len);
err += CloseFile(Sess, fh);
return (err == 0)? 0: -1;
}
static int
dirgen(int slot, Dir *d, void *aux)
{
FInfo i;
Aux *a = aux;
int nent, more;
char *npath, volnam[VOLNAMLEN];
long off;
int numinf = numinfo();
if (strcmp(a->path, "/") == 0){
if (slot < numinf){
dirgeninfo(slot, d);
return 0;
}
else
slot -= numinf;
*volnam = 0;
if (slot >= Numvol)
return -1;
if (GetVolumeName(Sess, slot, volnam) == -1)
return -1;
if (*volnam == 0)
return -1;
npath = newpath(a->path, volnam);
memset(&i, 0, sizeof i);
if (ObtainFileOrDirInfo(Sess, NSlong, NSlong, RIMstat, npath, &i) == 0){
/*
* ObtainFileOrDirInfo() on volumes returns mostly correct
* metadata (NW 5.0) but the filename is missing, so we add
* that by hand, and also tweek the permissions. It may fails
* on earlier server versions, but we just fall back to V2D().
*/
I2D(d, &i);
free(d->name);
d->name = estrdup9p(strlwr(volnam));
d->mode = 0555|DMDIR;
}
else
V2D(d, slot, volnam);
free(npath);
return 0;
}
off = slot * sizeof(FInfo);
if (off >= a->off && off < a->end && time(nil) < a->expire){
I2D(d, (FInfo *)(a->cache + (off - a->off)));
return 0;
}
if (off == 0){
if (InitializeSearch(Sess, NSlong, a->path, a->srch) == -1)
return -1;
a->off = 0;
a->end = 0;
}
/*
* we try to read dirs in 64 entry chunks - the
* server will round this down to an integer number of
* entrys to fit in the negiotated packet size;
*
* Disappointingly we only seem to get 4 to 6
* directory entries per read.
*/
more = 1;
do {
nent = 64;
a->off = a->end;
if (SearchFileOrSubdirectorySet(Sess, a->srch, NSlong, DSdos, SAall,
"\377*", RIMstat, (FInfo *)a->cache, &nent, &more) == -1)
return -1;
a->end = a->off + nent * sizeof(FInfo);
}while (a->off < off && more);
a->expire = time(nil) + CACHETIME;
if (off >= a->end)
return -1;
I2D(d, (FInfo *)(a->cache + (off - a->off)));
return 0;
}
static void
fsattach(Req *r)
{
Aux *a;
char *spec = r->ifcall.aname;
if(spec && spec[0]){
respond(r, "invalid attach specifier");
return;
}
r->ofcall.qid = (Qid){Proot, Vvol, QTDIR};
r->fid->qid = r->ofcall.qid;
a = r->fid->aux = emalloc9p(sizeof(Aux));
memset(a, 0, sizeof(Aux));
a->path = estrdup9p("/");
respond(r, nil);
}
static char*
fsclone(Fid *ofid, Fid *fid)
{
Aux *a = emalloc9p(sizeof(Aux));
*a = *(Aux*)ofid->aux;
if (a->path)
a->path = estrdup9p(a->path);
fid->aux = a;
return nil;
}
static char*
fswalk1(Fid *fid, char *name, Qid *qid)
{
FInfo i;
int n, vol;
char *npath;
static char e[ERRMAX];
Aux *a = fid->aux;
npath = newpath(a->path, name);
if (strcmp(npath, "/") == 0)
*qid = (Qid){Proot, Vvol, QTDIR};
else
if (strrchr(npath, '/') == npath){
if ((n = walkinfo(name)) != -1){
*qid = (Qid){n, Vinfo, 0};
}
else {
if (GetVolumeNumber(Sess, npath +1, &vol) == -1){
free(npath);
rerrstr(e, sizeof(e));
return e;
}
*qid = (Qid){vol, Vvol, QTDIR};
}
}
else{
memset(&i, 0, sizeof i);
if (ObtainFileOrDirInfo(Sess, NSlong, NSlong, RIMqid, npath, &i) == -1){
free(npath);
rerrstr(e, sizeof(e));
if (strcmp(e, "failed") == 0)
return "file does not exist";
return e;
}
*qid = I2Qid(&i);
}
free(a->path);
a->path = npath;
fid->qid = *qid;
return nil;
}
static void
fsstat(Req *r)
{
FInfo i;
Aux *a = r->fid->aux;
if (r->fid->qid.path == Proot)
V2D(&r->d, Proot, "");
else
if (r->fid->qid.vers == Vinfo)
dirgeninfo(r->fid->qid.path, &r->d);
else
if (r->fid->qid.vers == Vvol){
memset(&i, 0, sizeof i);
if (ObtainFileOrDirInfo(Sess, NSlong, NSlong, RIMstat, a->path, &i) == 0)
I2D(&r->d, &i);
else
V2D(&r->d, r->fid->qid.path, a->path +1);
}
else{
memset(&i, 0, sizeof i);
if (ObtainFileOrDirInfo(Sess, NSlong, NSlong, RIMstat, a->path, &i) == -1){
responderrstr(r);
return;
}
I2D(&r->d, &i);
}
respond(r, nil);
}
/*
* All files are created in compatibility mode because of one marginal case:
* If you use mk to build an ANSI C library (using pcc) then the object files
* are opened for reading whilst 8c is still writing them. This is illegal in
* NCP unless the compatability mode is set.
*/
static void
fscreate(Req *r)
{
FInfo i;
char *npath;
int oca, ocm, dar, attr;
Aux *a = r->fid->aux;
a->cache = emalloc9p(Sess->mtu);
npath = smprint("%s/%s", a->path, r->ifcall.name);
if (r->ifcall.perm & DMDIR){
attr = FAdirectory;
ocm = OCMcreate;
dar = DARnewdir;
}
else {
attr = FAnormal;
dar = DARnewfile|DARcompatibility;
ocm = OCMcreate|OCMopen|OCMtruncate;
}
memset(&i, 0, sizeof i);
if (OpenCreateFileOrSubdir(Sess, NSlong, ocm, SAall, RIMqid, dar,
attr, npath, &oca, a->fh, &i) == -1 || oca != OCAcreated){
char e[ERRMAX];
rerrstr(e, sizeof(e));
if (oca != OCAcreated && strcmp(e, "lock fail") != 0){
free(npath);
responderrstr(r);
return;
}
}
free(a->path);
a->path = npath;
r->ofcall.qid = I2Qid(&i);
r->fid->qid = r->ofcall.qid;
respond(r, nil);
}
static void
fsopen(Req *r)
{
FInfo i;
Aux *a = r->fid->aux;
int oca, ocm = OCMopen;
int dar = DARcompatibility;
if (r->fid->qid.vers == Vinfo){
if (makeinfo(r->fid->qid.path) != -1)
respond(r, nil);
else
respond(r, "cannot renerate info");
return;
}
a->cache = emalloc9p(Sess->mtu);
if (r->ofcall.qid.type & QTDIR){
respond(r, nil);
return;
}
if (r->ifcall.mode & OTRUNC)
ocm |= OCMtruncate;
switch (r->ifcall.mode & OMASK){
case OREAD:
dar |= DARreadonly;
break;
case OWRITE:
dar |= DARwriteonly;
break;
case ORDWR:
dar |= DARreadonly|DARwriteonly;
break;
case OEXEC:
dar |= DARreadonly;
break;
}
if (OpenCreateFileOrSubdir(Sess, NSlong, ocm, SAall, RIMnone, dar,
FAnormal, a->path, &oca, a->fh, &i) == -1){
responderrstr(r);
return;
}
respond(r, 0);
}
static void
fswrite(Req *r)
{
long n, m;
int err;
long got = 0;
Aux *a = r->fid->aux;
char *buf = r->ifcall.data;
long len = r->ifcall.count;
long off = r->ifcall.offset;
if (r->fid->qid.vers == Vinfo){
respond(r, "illegal operation");
return;
}
if (r->ifcall.offset > ~0U ||
r->ifcall.count + r->ifcall.offset > ~0U){
respond(r, "64 bit file offsets not supported");
return;
}
/* Writes must not cross a 4096 byte boundry */
do {
m = n = min(Maxio-(off % IOCHUNK), len-got);
err = WriteToAFile(Sess, a->fh, buf, n, off);
off += n;
got += n;
buf += n;
} while (got < len && n == m && !err);
r->ofcall.count = got;
if (err == -1)
responderrstr(r);
else
respond(r, nil);
}
static void
fsread(Req *r)
{
long n, m;
int err;
long got = 0;
Aux *a = r->fid->aux;
char *buf = r->ofcall.data;
long len = r->ifcall.count;
long off = r->ifcall.offset;
if (r->fid->qid.vers == Vinfo){
r->ofcall.count = readinfo(r->fid->qid.path, buf, len, off);
respond(r, nil);
return;
}
if (r->ifcall.offset > ~0U ||
r->ifcall.count + r->ifcall.offset > ~0U){
respond(r, "64 bit file offsets not supported");
return;
}
if (r->fid->qid.type & QTDIR){
dirread9p(r, dirgen, a);
respond(r, nil);
return;
}
if (off >= a->off && (off + len) < a->end && time(nil) < a->expire){
memcpy(buf, a->cache + (off - a->off), len);
r->ofcall.count = len;
respond(r, nil);
return;
}
a->off = off - (off % IOCHUNK);
do {
m = n = Maxio;
err = ReadFromAFile(Sess, a->fh, a->cache + got, &n, a->off + got);
got += n;
} while (got < Maxio && n == m && !err);
a->end = a->off + got;
a->expire = time(nil) + CACHETIME;
if ((a->end - a->off) > (off - a->off))
r->ofcall.count = min(len, (a->end - a->off) - (off - a->off));
else
r->ofcall.count = 0;
if (err == -1){
responderrstr(r);
return;
}
memcpy(buf, a->cache + (off - a->off), r->ofcall.count);
respond(r, nil);
}
static void
fsdestroyfid(Fid *f)
{
Aux *a = f->aux;
if (f->qid.vers == Vinfo)
freeinfo(f->qid.path);
if (f->omode != -1 && (f->qid.type & DMDIR) == 0)
CloseFile(Sess, a->fh);
if ((f->omode != -1) && (f->omode & ORCLOSE) == ORCLOSE)
dodelete(a->path);
if (a && a->cache)
free(a->cache);
if (a && a->path)
free(a->path);
if (a)
free(a);
f->omode = -1;
}
static void
fsremove(Req *r)
{
Aux *a = r->fid->aux;
if (r->fid->qid.vers == Vinfo){
respond(r, "illegal operation");
return;
}
if (r->fid->omode != -1 && (r->fid->qid.type & DMDIR) == 0)
CloseFile(Sess, a->fh);
r->fid->omode = -1;
if (dodelete(a->path) == -1){
responderrstr(r);
return;
}
respond(r, nil);
}
static void
fswstat(Req *r)
{
Mfi mfi;
char *p, *npath;
int mask = 0;
Aux *a = r->fid->aux;
if (r->fid->qid.vers == Vinfo){
respond(r, "illegal operation");
return;
}
memset(&mfi, 0, sizeof mfi);
if((r->d.uid && r->d.uid[0]) || (r->d.gid && r->d.gid[0])){
respond(r, nil);
// this change makes replica happier
// respond(r, "cannot change ownership");
return;
}
if(~r->d.mode){
if(((r->d.mode & ~(0777 | DMDIR)) != 0)){
respond(r, "mode not supported");
return;
}
mfi.attr = (r->d.mode & 0222)? 0: FAreadonly;
mfi.attr |= (r->d.mode & DMDIR)? FAdirectory: FAnormal;
mask |= MFIattr;
}
if(~r->d.length){
if (r->d.length > ~0U){
respond(r, "64 bit file offsets not supported");
return;
}
if (dotruncate(a->path, (ulong)r->d.length) == -1)
responderrstr(r);
return;
}
if(~r->d.mtime){
mfi.modified = r->d.mtime;
mfi.modifier = Myuid;
mask |= MFImodified;
}
if(~r->d.atime){
mfi.accessed = r->d.atime;
mask |= MFIaccessed;
}
if(r->d.name && r->d.name[0]){
if ((p = strrchr(a->path, '/')) == nil){
respond(r, "illegal path");
return;
}
npath = emalloc9p((p-a->path)+strlen(r->d.name)+2);
strecpy(npath, npath+(p- a->path)+2, a->path);
strcat(npath, r->d.name);
if (r->fid->omode != -1 && (r->fid->qid.type & DMDIR) == 0)
CloseFile(Sess, a->fh);
if (dorename(a->path, npath) == -1){
free(npath);
responderrstr(r);
return;
}
if (r->fid->omode != -1 && (r->fid->qid.type & DMDIR) == 0)
if (doreopen(r->fid) == -1){
respond(r, "reopen after rename - failed");
return;
}
free(a->path);
a->path = npath;
}
if(mask && ModifyFileOrDirInfo(Sess, NSlong, SAall, mask, &mfi, a->path)){
responderrstr(r);
return;
}
/*
* This only works for open files under Netware,
* but it might be usefull...
*/
if (!mask && (!r->d.name || !r->d.name[0]) && r->fid->omode != -1){
if (CommitFile(Sess, a->fh) == -1){
responderrstr(r);
return;
}
}
respond(r, nil);
}
static void
fsend(Srv *srv)
{
USED(srv);
Logout(Sess);
Detach(Sess);
}
Srv fs =
{
.destroyfid = fsdestroyfid,
.attach= fsattach,
.open= fsopen,
.create= fscreate,
.read= fsread,
.write= fswrite,
.remove= fsremove,
.stat= fsstat,
.wstat= fswstat,
.clone= fsclone,
.walk1= fswalk1,
.end= fsend,
};
void
usage(void)
{
fprint(2, "usage: %s [-dD] [-s service] [-m mtpt] [-k keyparam] host\n", argv0);
exits("usage");
}
void
main(int argc, char **argv)
{
FSInfo fsi;
long fstime;
UserPasswd *up;
ulong nds_uid;
int fd, maxpkt;
uchar uid[sizeof(nds_uid)], *p;
char *addr, *keyp, defmnt[64], *mtpt, *svs, errbuf[64];
uchar chal[NWKEYLEN], tmp[NWKEYLEN *2], key[NWKEYLEN];
svs = nil;
keyp = "";
mtpt = defmnt;
ARGBEGIN{
case 'd':
Debug = 1;
break;
case 'D':
chatty9p++;
break;
case 'k':
keyp = EARGF(usage());
break;
case 's':
svs = EARGF(usage());
break;
case 'm':
mtpt = EARGF(usage());
break;
default:
usage();
}ARGEND
if(argc != 1)
usage();
strncpy(Host, *argv, sizeof(Host));
fmtinstall('N', ncpfmt);
addr = netmkaddr(Host, "tcp", "ncp");
if ((fd = dial(addr, 0, 0, 0)) == -1)
sysfatal("cannot dial %s", addr);
if ((up = auth_getuserpasswd(auth_getkey, "proto=pass service=ncp %s", keyp)) == nil) {
fprint(2, "cannot authenticate\n");
exits("auth");
}
if ((Sess = Attach(fd)) == nil){
memset(up->passwd, 0, strlen(up->passwd));
fprint(2, "%s - attach failed, %r\n", Host);
exits("attach");
}
strupr(up->passwd);
if (GetLoginKey(Sess, 1, chal) != 0){
/*
* As we cannot get the random challange from the server
* then we must use the depricated plain-text login
*/
if (LoginObject(Sess, up->user, OTuser, up->passwd) == -1){
memset(up->passwd, 0, strlen(up->passwd));
rerrstr(errbuf, sizeof(errbuf));
if (strcmp(errbuf, "no q job rights") == 0)
fprint(2, "%s:%s - encrypted login only\n", Host, up->user);
else
fprint(2, "%s:%s login failed - %r\n", Host, up->user);
exits("user");
}
memset(up->passwd, 0, strlen(up->passwd));
}
else{
/*
* NB: We ask for the ObjectID _after_ requesting a
* our LoginKey and before doing a KeyedObjectLogin.
* This hints to the server to give us the NDS ObjectID
* (If it is running NDS), nescessary for authentication.
* The server specific ObjectID for the user is returned by
* GetBinderyObjectId() called at "any other time".
*/
if (GetBinderyObjectId(Sess, up->user, OTuser, &nds_uid) != 0){
memset(up->passwd, 0, strlen(up->passwd));
rerrstr(errbuf, sizeof(errbuf));
if (strcmp(errbuf, "no such object") == 0)
fprint(2, "%s:%s - unknown username\n", Host, up->user);
else
fprint(2, "%s:%s get userid failed - %r\n", Host, up->user);
exits("user");
}
p = uid;
*(p++) = (nds_uid >> 24) & 0xff;
*(p++) = (nds_uid >> 16) & 0xff;
*(p++) = (nds_uid >> 8) & 0xff;
*(p++) = nds_uid & 0xff;
USED(p);
shuffle(uid, (uchar *)up->passwd, strlen(up->passwd), tmp);
memset(up->passwd, 0, strlen(up->passwd));
nw_encrypt(chal, tmp, key);
/*
* the "old password" error looks apropriate, and is taken
* from the Linux ncpfs code, however my NW 5.0 server returns
* "unable to bind" on password expiration (grace logins still apply)
*/
if (KeyedObjectLogin(Sess, OTuser, (char *)up->user, key) != 0){
rerrstr(errbuf, sizeof(errbuf));
if (strcmp(errbuf, "old password") == 0 ||
strcmp(errbuf, "unable to bind") == 0){
fprint(2, "%s: warning %s:%s - password expiration imminent\n",
argv0, up->user, Host);
}
else{
fprint(2, "%s: %s:%s - login failed, %r\n", argv0, Host, up->user);
exits("login");
}
}
}
maxpkt = MTU;
if (NegotiateBufferSize(Sess, &maxpkt) == -1)
sysfatal("negotiate MTU - %r");
free(Sess->buf);
Sess->mtu = maxpkt;
Sess->buf = emalloc9p(Sess->mtu);
Maxio = maxpkt - 32; // leave space for packet headers
if (GetFileServerDateAndTime(Sess, &fstime) == -1)
sysfatal("get FS time - %r");
Slip = fstime - time(nil);
if (GetFileServerInfo(Sess, &fsi) == -1)
sysfatal("get FS info - %r");
Fsver = fsi.fsvermaj * 1000 + fsi.fsvermin;
Bigfiles = fsi.bigfiles;
Numvol = fsi.numvol;
/*
* by doing this now we get the users
* bindery user ID rather than their NDS user ID
*/
if (GetBinderyObjectId(Sess, up->user, OTuser, &Myuid) != 0){
fprint(2, "%s:%s get userid failed - %r\n", Host, up->user);
exits("user");
}
strcpy(defmnt, "/n/");
strcat(defmnt, *argv);
postmountsrv(&fs, svs, mtpt, MREPL);
exits(nil);
}
|