1 #include "logfsos.h" 2 #include "logfs.h" 3 #include "fcall.h" 4 #include "local.h" 5 6 char * 7 logfsserverwstat(LogfsServer *server, u32int fid, uchar *stat, ushort nstat) 8 { 9 Fid *f; 10 uchar *p; 11 ushort len; 12 uchar *mep; 13 Qid qid; 14 u32int perm, mtime; 15 uvlong length; 16 char *name, *uname, *gname, *muname; 17 int qiddonttouch, permdonttouch, mtimedonttouch, lengthdonttouch; 18 Entry *e, *parent; 19 LogMessage s; 20 char *cuid, *ngid; 21 Group *eg, *ng; 22 char *cname; 23 char *errmsg; 24 char *nuid; 25 26 if(server->trace > 1) 27 print("logfsserverwstat(%ud, %ud)\n", fid, nstat); 28 if(nstat < 49) 29 return Eshortstat; 30 p = stat; 31 len = GBIT16(p); p += BIT16SZ; 32 if(len + BIT16SZ != nstat) 33 return Eshortstat; 34 mep = p + len; 35 p += BIT16SZ + BIT32SZ; /* skip type and dev */ 36 qid.type = *p++; 37 qid.vers = GBIT32(p); p += BIT32SZ; 38 qid.path = GBIT64(p); p += BIT64SZ; 39 perm = GBIT32(p); p += BIT32SZ; 40 p += BIT32SZ; /* skip atime */ 41 mtime = GBIT32(p); p += BIT32SZ; 42 length = GBIT64(p); p+= BIT64SZ; 43 if(!logfsgn(&p, mep, &name) || !logfsgn(&p, mep, &uname) 44 || !logfsgn(&p, mep, &gname) || !logfsgn(&p, mep, &muname)) 45 return Eshortstat; 46 if(p != mep) 47 return Eshortstat; 48 qiddonttouch = qid.type == (uchar)~0 && qid.vers == ~0 && qid.path == ~(uvlong)0; 49 permdonttouch = perm == ~0; 50 mtimedonttouch = mtime == ~0; 51 lengthdonttouch = length == ~(uvlong)0; 52 if(server->trace > 1) { 53 int comma = 0; 54 print("logfsserverwstat("); 55 if(!qiddonttouch) { 56 comma = 1; 57 print("qid=0x%.2ux/%lud/%llud", qid.type, qid.vers, qid.path); 58 } 59 if(!permdonttouch) { 60 if(comma) 61 print(", "); 62 print("perm=0%uo", perm); 63 comma = 1; 64 } 65 if(!mtimedonttouch) { 66 if(comma) 67 print(", "); 68 print("mtime=%ud", mtime); 69 comma = 1; 70 } 71 if(!lengthdonttouch) { 72 if(comma) 73 print(", "); 74 print("length=%llud", length); 75 comma = 1; 76 } 77 if(name != nil) { 78 if(comma) 79 print(", "); 80 print("name=%s", name); 81 comma = 1; 82 } 83 if(uname != nil) { 84 if(comma) 85 print(", "); 86 print("uid=%s", uname); 87 comma = 1; 88 } 89 if(gname != nil) { 90 if(comma) 91 print(", "); 92 print("gid=%s", gname); 93 comma = 1; 94 } 95 if(muname != nil) { 96 if(comma) 97 print(", "); 98 print("muname=%s", muname); 99 comma = 1; 100 } 101 USED(comma); 102 print(")\n"); 103 } 104 f = logfsfidmapfindentry(server->fidmap, fid); 105 if(f == nil) 106 return logfsebadfid; 107 e = f->entry; 108 if(e->deadandgone) 109 return Eio; 110 parent = e->parent; 111 if(name) { 112 Entry *oe; 113 if(parent == e) 114 return Eperm; 115 if(!logfsuserpermcheck(server, e->parent, f, DMWRITE)) 116 return Eperm; 117 for(oe = parent->u.dir.list; oe; oe = oe->next) { 118 if(oe == e) 119 continue; 120 if(strcmp(oe->name, name) == 0) 121 return Eexist; 122 } 123 } 124 if(!lengthdonttouch) { 125 if(!logfsuserpermcheck(server, e, f, DMWRITE)) 126 return Eperm; 127 if(e->qid.type & QTDIR) { 128 if(length != 0) 129 return Eperm; 130 }else if(length != e->u.file.length){ 131 /* 132 * TODO - truncate directory 133 * TODO - truncate file 134 */ 135 return "wstat -- can't change length"; 136 } 137 } 138 cuid = logfsisfindidfromname(server->is, f->uname); 139 /* TODO - change entries to have a group pointer */ 140 eg = logfsisfindgroupfromid(server->is, e->uid); 141 if(gname) { 142 gname = logfsisustadd(server->is, gname); 143 if(gname == nil) 144 return Enomem; 145 ngid = logfsisfindidfromname(server->is, gname); 146 if(ngid == nil) 147 return Eunknown; 148 } 149 else 150 ngid = nil; 151 if(uname) { 152 uname = logfsisustadd(server->is, uname); 153 if(uname == nil) 154 return Enomem; 155 nuid = logfsisfindidfromname(server->is, uname); 156 if(nuid == nil) 157 return Eunknown; 158 } 159 else 160 nuid = nil; 161 if(!permdonttouch || !mtimedonttouch) { 162 /* 163 * same permissions rules - change by owner, or by group leader 164 */ 165 if((server->openflags & LogfsOpenFlagWstatAllow) == 0 && 166 e->uid != cuid && (eg == nil || !logfsisgroupuidisleader(server->is, eg, cuid))) 167 return Eperm; 168 } 169 if(!permdonttouch){ 170 if((perm^e->perm) & DMDIR) 171 return "wstat -- attempt to change directory"; 172 if(perm & ~(DMDIR|DMAPPEND|DMEXCL|0777)) 173 return Eperm; 174 } 175 if(gname) { 176 int ok; 177 ng = logfsisfindgroupfromid(server->is, ngid); 178 ok = 0; 179 if(e->uid == cuid && logfsisgroupuidismember(server->is, ng, e->uid)) 180 ok = 1; 181 if(!ok && eg && logfsisgroupuidisleader(server->is, eg, cuid) 182 && logfsisgroupuidisleader(server->is, ng, cuid)) 183 ok = 1; 184 if(!ok && (server->openflags & LogfsOpenFlagWstatAllow) == 0) 185 return Eperm; 186 } 187 if(!qiddonttouch) 188 return Eperm; 189 if(uname){ 190 if((server->openflags & LogfsOpenFlagWstatAllow) == 0) 191 return Eperm; 192 } 193 if(muname) 194 return Eperm; 195 /* 196 * we can do this 197 */ 198 if(mtimedonttouch && permdonttouch && lengthdonttouch 199 && name == nil && uname == nil && gname == nil) { 200 /* 201 * but we aren't doing anything - this is a wstat flush 202 */ 203 return logfsserverflush(server); 204 } 205 if(name) { 206 cname = logfsstrdup(name); 207 if(cname == nil) 208 return Enomem; 209 } 210 else 211 cname = nil; 212 /* 213 * send the log message 214 */ 215 s.type = LogfsLogTwstat; 216 s.path = e->qid.path; 217 s.u.wstat.name = cname; 218 s.u.wstat.perm = perm; 219 s.u.wstat.uid = nuid; 220 s.u.wstat.gid = ngid; 221 s.u.wstat.mtime = mtime; 222 s.u.wstat.muid = cuid; 223 errmsg = logfslog(server, 1, &s); 224 if(errmsg) { 225 logfsfreemem(cname); 226 return errmsg; 227 } 228 if(!mtimedonttouch) 229 e->mtime = mtime; 230 if(!permdonttouch) 231 e->perm = (e->perm & DMDIR) | perm; 232 if(!lengthdonttouch) { 233 /* TODO */ 234 } 235 if(name) { 236 logfsfreemem(e->name); 237 e->name = cname; 238 } 239 if(uname) 240 e->uid = nuid; 241 if(ngid) 242 e->gid = ngid; 243 return nil; 244 } 245 246