#include "logfsos.h"#include "logfs.h"#include "fcall.h"#include "local.h" char *logfsserverwstat(LogfsServer *server, u32int fid, uchar *stat, ushort nstat){ Fid *f; uchar *p; ushort len; uchar *mep; Qid qid; u32int perm, mtime; uvlong length; char *name, *uname, *gname, *muname; int qiddonttouch, permdonttouch, mtimedonttouch, lengthdonttouch; Entry *e, *parent; LogMessage s; char *cuid, *ngid; Group *eg, *ng; char *cname; char *errmsg; char *nuid; if(server->trace > 1) print("logfsserverwstat(%ud, %ud)\n", fid, nstat); if(nstat < 49) return Eshortstat; p = stat; len = GBIT16(p); p += BIT16SZ; if(len + BIT16SZ != nstat) return Eshortstat; mep = p + len; p += BIT16SZ + BIT32SZ; /* skip type and dev */ qid.type = *p++; qid.vers = GBIT32(p); p += BIT32SZ; qid.path = GBIT64(p); p += BIT64SZ; perm = GBIT32(p); p += BIT32SZ; p += BIT32SZ; /* skip atime */ mtime = GBIT32(p); p += BIT32SZ; length = GBIT64(p); p+= BIT64SZ; if(!logfsgn(&p, mep, &name) || !logfsgn(&p, mep, &uname) || !logfsgn(&p, mep, &gname) || !logfsgn(&p, mep, &muname)) return Eshortstat; if(p != mep) return Eshortstat; qiddonttouch = qid.type == (uchar)~0 && qid.vers == ~0 && qid.path == ~(uvlong)0; permdonttouch = perm == ~0; mtimedonttouch = mtime == ~0; lengthdonttouch = length == ~(uvlong)0; if(server->trace > 1) { int comma = 0; print("logfsserverwstat("); if(!qiddonttouch) { comma = 1; print("qid=0x%.2ux/%lud/%llud", qid.type, qid.vers, qid.path); } if(!permdonttouch) { if(comma) print(", "); print("perm=0%uo", perm); comma = 1; } if(!mtimedonttouch) { if(comma) print(", "); print("mtime=%ud", mtime); comma = 1; } if(!lengthdonttouch) { if(comma) print(", "); print("length=%llud", length); comma = 1; } if(name != nil) { if(comma) print(", "); print("name=%s", name); comma = 1; } if(uname != nil) { if(comma) print(", "); print("uid=%s", uname); comma = 1; } if(gname != nil) { if(comma) print(", "); print("gid=%s", gname); comma = 1; } if(muname != nil) { if(comma) print(", "); print("muname=%s", muname); comma = 1; } USED(comma); print(")\n"); } f = logfsfidmapfindentry(server->fidmap, fid); if(f == nil) return logfsebadfid; e = f->entry; if(e->deadandgone) return Eio; parent = e->parent; if(name) { Entry *oe; if(parent == e) return Eperm; if(!logfsuserpermcheck(server, e->parent, f, DMWRITE)) return Eperm; for(oe = parent->u.dir.list; oe; oe = oe->next) { if(oe == e) continue; if(strcmp(oe->name, name) == 0) return Eexist; } } if(!lengthdonttouch) { if(!logfsuserpermcheck(server, e, f, DMWRITE)) return Eperm; if(e->qid.type & QTDIR) { if(length != 0) return Eperm; }else if(length != e->u.file.length){ /* * TODO - truncate directory * TODO - truncate file */ return "wstat -- can't change length"; } } cuid = logfsisfindidfromname(server->is, f->uname); /* TODO - change entries to have a group pointer */ eg = logfsisfindgroupfromid(server->is, e->uid); if(gname) { gname = logfsisustadd(server->is, gname); if(gname == nil) return Enomem; ngid = logfsisfindidfromname(server->is, gname); if(ngid == nil) return Eunknown; } else ngid = nil; if(uname) { uname = logfsisustadd(server->is, uname); if(uname == nil) return Enomem; nuid = logfsisfindidfromname(server->is, uname); if(nuid == nil) return Eunknown; } else nuid = nil; if(!permdonttouch || !mtimedonttouch) { /* * same permissions rules - change by owner, or by group leader */ if((server->openflags & LogfsOpenFlagWstatAllow) == 0 && e->uid != cuid && (eg == nil || !logfsisgroupuidisleader(server->is, eg, cuid))) return Eperm; } if(!permdonttouch){ if((perm^e->perm) & DMDIR) return "wstat -- attempt to change directory"; if(perm & ~(DMDIR|DMAPPEND|DMEXCL|0777)) return Eperm; } if(gname) { int ok; ng = logfsisfindgroupfromid(server->is, ngid); ok = 0; if(e->uid == cuid && logfsisgroupuidismember(server->is, ng, e->uid)) ok = 1; if(!ok && eg && logfsisgroupuidisleader(server->is, eg, cuid) && logfsisgroupuidisleader(server->is, ng, cuid)) ok = 1; if(!ok && (server->openflags & LogfsOpenFlagWstatAllow) == 0) return Eperm; } if(!qiddonttouch) return Eperm; if(uname){ if((server->openflags & LogfsOpenFlagWstatAllow) == 0) return Eperm; } if(muname) return Eperm; /* * we can do this */ if(mtimedonttouch && permdonttouch && lengthdonttouch && name == nil && uname == nil && gname == nil) { /* * but we aren't doing anything - this is a wstat flush */ return logfsserverflush(server); } if(name) { cname = logfsstrdup(name); if(cname == nil) return Enomem; } else cname = nil; /* * send the log message */ s.type = LogfsLogTwstat; s.path = e->qid.path; s.u.wstat.name = cname; s.u.wstat.perm = perm; s.u.wstat.uid = nuid; s.u.wstat.gid = ngid; s.u.wstat.mtime = mtime; s.u.wstat.muid = cuid; errmsg = logfslog(server, 1, &s); if(errmsg) { logfsfreemem(cname); return errmsg; } if(!mtimedonttouch) e->mtime = mtime; if(!permdonttouch) e->perm = (e->perm & DMDIR) | perm; if(!lengthdonttouch) { /* TODO */ } if(name) { logfsfreemem(e->name); e->name = cname; } if(uname) e->uid = nuid; if(ngid) e->gid = ngid; return nil;}
#include "logfsos.h"
#include "logfs.h"
#include "fcall.h"
#include "local.h"
char *
logfsserverwstat(LogfsServer *server, u32int fid, uchar *stat, ushort nstat)
{
Fid *f;
uchar *p;
ushort len;
uchar *mep;
Qid qid;
u32int perm, mtime;
uvlong length;
char *name, *uname, *gname, *muname;
int qiddonttouch, permdonttouch, mtimedonttouch, lengthdonttouch;
Entry *e, *parent;
LogMessage s;
char *cuid, *ngid;
Group *eg, *ng;
char *cname;
char *errmsg;
char *nuid;
if(server->trace > 1)
print("logfsserverwstat(%ud, %ud)\n", fid, nstat);
if(nstat < 49)
return Eshortstat;
p = stat;
len = GBIT16(p); p += BIT16SZ;
if(len + BIT16SZ != nstat)
return Eshortstat;
mep = p + len;
p += BIT16SZ + BIT32SZ; /* skip type and dev */
qid.type = *p++;
qid.vers = GBIT32(p); p += BIT32SZ;
qid.path = GBIT64(p); p += BIT64SZ;
perm = GBIT32(p); p += BIT32SZ;
p += BIT32SZ; /* skip atime */
mtime = GBIT32(p); p += BIT32SZ;
length = GBIT64(p); p+= BIT64SZ;
if(!logfsgn(&p, mep, &name) || !logfsgn(&p, mep, &uname)
|| !logfsgn(&p, mep, &gname) || !logfsgn(&p, mep, &muname))
return Eshortstat;
if(p != mep)
return Eshortstat;
qiddonttouch = qid.type == (uchar)~0 && qid.vers == ~0 && qid.path == ~(uvlong)0;
permdonttouch = perm == ~0;
mtimedonttouch = mtime == ~0;
lengthdonttouch = length == ~(uvlong)0;
if(server->trace > 1) {
int comma = 0;
print("logfsserverwstat(");
if(!qiddonttouch) {
comma = 1;
print("qid=0x%.2ux/%lud/%llud", qid.type, qid.vers, qid.path);
}
if(!permdonttouch) {
if(comma)
print(", ");
print("perm=0%uo", perm);
comma = 1;
}
if(!mtimedonttouch) {
if(comma)
print(", ");
print("mtime=%ud", mtime);
comma = 1;
}
if(!lengthdonttouch) {
if(comma)
print(", ");
print("length=%llud", length);
comma = 1;
}
if(name != nil) {
if(comma)
print(", ");
print("name=%s", name);
comma = 1;
}
if(uname != nil) {
if(comma)
print(", ");
print("uid=%s", uname);
comma = 1;
}
if(gname != nil) {
if(comma)
print(", ");
print("gid=%s", gname);
comma = 1;
}
if(muname != nil) {
if(comma)
print(", ");
print("muname=%s", muname);
comma = 1;
}
USED(comma);
print(")\n");
}
f = logfsfidmapfindentry(server->fidmap, fid);
if(f == nil)
return logfsebadfid;
e = f->entry;
if(e->deadandgone)
return Eio;
parent = e->parent;
if(name) {
Entry *oe;
if(parent == e)
return Eperm;
if(!logfsuserpermcheck(server, e->parent, f, DMWRITE))
return Eperm;
for(oe = parent->u.dir.list; oe; oe = oe->next) {
if(oe == e)
continue;
if(strcmp(oe->name, name) == 0)
return Eexist;
}
}
if(!lengthdonttouch) {
if(!logfsuserpermcheck(server, e, f, DMWRITE))
return Eperm;
if(e->qid.type & QTDIR) {
if(length != 0)
return Eperm;
}else if(length != e->u.file.length){
/*
* TODO - truncate directory
* TODO - truncate file
*/
return "wstat -- can't change length";
}
}
cuid = logfsisfindidfromname(server->is, f->uname);
/* TODO - change entries to have a group pointer */
eg = logfsisfindgroupfromid(server->is, e->uid);
if(gname) {
gname = logfsisustadd(server->is, gname);
if(gname == nil)
return Enomem;
ngid = logfsisfindidfromname(server->is, gname);
if(ngid == nil)
return Eunknown;
}
else
ngid = nil;
if(uname) {
uname = logfsisustadd(server->is, uname);
if(uname == nil)
return Enomem;
nuid = logfsisfindidfromname(server->is, uname);
if(nuid == nil)
return Eunknown;
}
else
nuid = nil;
if(!permdonttouch || !mtimedonttouch) {
/*
* same permissions rules - change by owner, or by group leader
*/
if((server->openflags & LogfsOpenFlagWstatAllow) == 0 &&
e->uid != cuid && (eg == nil || !logfsisgroupuidisleader(server->is, eg, cuid)))
return Eperm;
}
if(!permdonttouch){
if((perm^e->perm) & DMDIR)
return "wstat -- attempt to change directory";
if(perm & ~(DMDIR|DMAPPEND|DMEXCL|0777))
return Eperm;
}
if(gname) {
int ok;
ng = logfsisfindgroupfromid(server->is, ngid);
ok = 0;
if(e->uid == cuid && logfsisgroupuidismember(server->is, ng, e->uid))
ok = 1;
if(!ok && eg && logfsisgroupuidisleader(server->is, eg, cuid)
&& logfsisgroupuidisleader(server->is, ng, cuid))
ok = 1;
if(!ok && (server->openflags & LogfsOpenFlagWstatAllow) == 0)
return Eperm;
}
if(!qiddonttouch)
return Eperm;
if(uname){
if((server->openflags & LogfsOpenFlagWstatAllow) == 0)
return Eperm;
}
if(muname)
return Eperm;
/*
* we can do this
*/
if(mtimedonttouch && permdonttouch && lengthdonttouch
&& name == nil && uname == nil && gname == nil) {
/*
* but we aren't doing anything - this is a wstat flush
*/
return logfsserverflush(server);
}
if(name) {
cname = logfsstrdup(name);
if(cname == nil)
return Enomem;
}
else
cname = nil;
/*
* send the log message
*/
s.type = LogfsLogTwstat;
s.path = e->qid.path;
s.u.wstat.name = cname;
s.u.wstat.perm = perm;
s.u.wstat.uid = nuid;
s.u.wstat.gid = ngid;
s.u.wstat.mtime = mtime;
s.u.wstat.muid = cuid;
errmsg = logfslog(server, 1, &s);
if(errmsg) {
logfsfreemem(cname);
return errmsg;
}
if(!mtimedonttouch)
e->mtime = mtime;
if(!permdonttouch)
e->perm = (e->perm & DMDIR) | perm;
if(!lengthdonttouch) {
/* TODO */
}
if(name) {
logfsfreemem(e->name);
e->name = cname;
}
if(uname)
e->uid = nuid;
if(ngid)
e->gid = ngid;
return nil;
}