xref: /inferno-os/liblogfs/wstat.c (revision 1981fff245dfce579ef416fa767eb69d462039e9)
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