summaryrefslogtreecommitdiff
path: root/liblogfs/wstat.c
diff options
context:
space:
mode:
Diffstat (limited to 'liblogfs/wstat.c')
-rw-r--r--liblogfs/wstat.c245
1 files changed, 245 insertions, 0 deletions
diff --git a/liblogfs/wstat.c b/liblogfs/wstat.c
new file mode 100644
index 00000000..85f112da
--- /dev/null
+++ b/liblogfs/wstat.c
@@ -0,0 +1,245 @@
+#include "lib9.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 Emsgsize;
+ p = stat;
+ len = GBIT16(p); p += BIT16SZ;
+ if(len + BIT16SZ != nstat)
+ return Emsgsize;
+ 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 Emsgsize;
+ if(p != mep)
+ return Emsgsize;
+ 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;
+}
+