9p.c 23 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181
  1. #include "stdinc.h"
  2. #include "9.h"
  3. enum {
  4. OMODE = 0x7, /* Topen/Tcreate mode */
  5. };
  6. enum {
  7. PermX = 1,
  8. PermW = 2,
  9. PermR = 4,
  10. };
  11. static char EPermission[] = "permission denied";
  12. static int
  13. permFile(File* file, Fid* fid, int perm)
  14. {
  15. char *u;
  16. DirEntry de;
  17. if(!fileGetDir(file, &de))
  18. return -1;
  19. /*
  20. * User none only gets other permissions.
  21. */
  22. if(strcmp(fid->uname, unamenone) != 0){
  23. /*
  24. * There is only one uid<->uname mapping
  25. * and it's already cached in the Fid, but
  26. * it might have changed during the lifetime
  27. * if this Fid.
  28. */
  29. if((u = unameByUid(de.uid)) != nil){
  30. if(strcmp(fid->uname, u) == 0 && ((perm<<6) & de.mode)){
  31. vtMemFree(u);
  32. deCleanup(&de);
  33. return 1;
  34. }
  35. vtMemFree(u);
  36. }
  37. if(groupMember(de.gid, fid->uname) && ((perm<<3) & de.mode)){
  38. deCleanup(&de);
  39. return 1;
  40. }
  41. }
  42. if(perm & de.mode){
  43. if(perm == PermX && (de.mode & ModeDir)){
  44. deCleanup(&de);
  45. return 1;
  46. }
  47. if(!groupMember(uidnoworld, fid->uname)){
  48. deCleanup(&de);
  49. return 1;
  50. }
  51. }
  52. if(fsysNoPermCheck(fid->fsys) || (fid->con->flags&ConNoPermCheck)){
  53. deCleanup(&de);
  54. return 1;
  55. }
  56. vtSetError(EPermission);
  57. deCleanup(&de);
  58. return 0;
  59. }
  60. static int
  61. permFid(Fid* fid, int p)
  62. {
  63. return permFile(fid->file, fid, p);
  64. }
  65. static int
  66. permParent(Fid* fid, int p)
  67. {
  68. int r;
  69. File *parent;
  70. parent = fileGetParent(fid->file);
  71. r = permFile(parent, fid, p);
  72. fileDecRef(parent);
  73. return r;
  74. }
  75. int
  76. validFileName(char* name)
  77. {
  78. char *p;
  79. if(name == nil || name[0] == '\0'){
  80. vtSetError("no file name");
  81. return 0;
  82. }
  83. if(name[0] == '.'){
  84. if(name[1] == '\0' || (name[1] == '.' && name[2] == '\0')){
  85. vtSetError(". and .. illegal as file name");
  86. return 0;
  87. }
  88. }
  89. for(p = name; *p != '\0'; p++){
  90. if((*p & 0xFF) < 040){
  91. vtSetError("bad character in file name");
  92. return 0;
  93. }
  94. }
  95. return 1;
  96. }
  97. static int
  98. rTwstat(Msg* m)
  99. {
  100. Dir dir;
  101. Fid *fid;
  102. ulong mode, oldmode;
  103. DirEntry de;
  104. char *gid, *strs, *uid;
  105. int gl, op, retval, tsync, wstatallow;
  106. if((fid = fidGet(m->con, m->t.fid, FidFWlock)) == nil)
  107. return 0;
  108. gid = uid = nil;
  109. retval = 0;
  110. if(strcmp(fid->uname, unamenone) == 0 || (fid->qid.type & QTAUTH)){
  111. vtSetError(EPermission);
  112. goto error0;
  113. }
  114. if(fileIsRoFs(fid->file) || !groupWriteMember(fid->uname)){
  115. vtSetError("read-only filesystem");
  116. goto error0;
  117. }
  118. if(!fileGetDir(fid->file, &de))
  119. goto error0;
  120. strs = vtMemAlloc(m->t.nstat);
  121. if(convM2D(m->t.stat, m->t.nstat, &dir, strs) == 0){
  122. vtSetError("wstat -- protocol botch");
  123. goto error;
  124. }
  125. /*
  126. * Run through each of the (sub-)fields in the provided Dir
  127. * checking for validity and whether it's a default:
  128. * .type, .dev and .atime are completely ignored and not checked;
  129. * .qid.path, .qid.vers and .muid are checked for validity but
  130. * any attempt to change them is an error.
  131. * .qid.type/.mode, .mtime, .name, .length, .uid and .gid can
  132. * possibly be changed.
  133. *
  134. * 'Op' flags there are changed fields, i.e. it's not a no-op.
  135. * 'Tsync' flags all fields are defaulted.
  136. */
  137. tsync = 1;
  138. if(dir.qid.path != ~0){
  139. if(dir.qid.path != de.qid){
  140. vtSetError("wstat -- attempt to change qid.path");
  141. goto error;
  142. }
  143. tsync = 0;
  144. }
  145. if(dir.qid.vers != ~0){
  146. if(dir.qid.vers != de.mcount){
  147. vtSetError("wstat -- attempt to change qid.vers");
  148. goto error;
  149. }
  150. tsync = 0;
  151. }
  152. if(dir.muid != nil && *dir.muid != '\0'){
  153. if((uid = uidByUname(dir.muid)) == nil){
  154. vtSetError("wstat -- unknown muid");
  155. goto error;
  156. }
  157. if(strcmp(uid, de.mid) != 0){
  158. vtSetError("wstat -- attempt to change muid");
  159. goto error;
  160. }
  161. vtMemFree(uid);
  162. uid = nil;
  163. tsync = 0;
  164. }
  165. /*
  166. * Check .qid.type and .mode agree if neither is defaulted.
  167. */
  168. if(dir.qid.type != (uchar)~0 && dir.mode != ~0){
  169. if(dir.qid.type != ((dir.mode>>24) & 0xFF)){
  170. vtSetError("wstat -- qid.type/mode mismatch");
  171. goto error;
  172. }
  173. }
  174. op = 0;
  175. oldmode = de.mode;
  176. if(dir.qid.type != (uchar)~0 || dir.mode != ~0){
  177. /*
  178. * .qid.type or .mode isn't defaulted, check for unknown bits.
  179. */
  180. if(dir.mode == ~0)
  181. dir.mode = (dir.qid.type<<24)|(de.mode & 0777);
  182. if(dir.mode & ~(DMDIR|DMAPPEND|DMEXCL|DMTMP|0777)){
  183. vtSetError("wstat -- unknown bits in qid.type/mode");
  184. goto error;
  185. }
  186. /*
  187. * Synthesise a mode to check against the current settings.
  188. */
  189. mode = dir.mode & 0777;
  190. if(dir.mode & DMEXCL)
  191. mode |= ModeExclusive;
  192. if(dir.mode & DMAPPEND)
  193. mode |= ModeAppend;
  194. if(dir.mode & DMDIR)
  195. mode |= ModeDir;
  196. if(dir.mode & DMTMP)
  197. mode |= ModeTemporary;
  198. if((de.mode^mode) & ModeDir){
  199. vtSetError("wstat -- attempt to change directory bit");
  200. goto error;
  201. }
  202. if((de.mode & (ModeAppend|ModeExclusive|ModeTemporary|0777)) != mode){
  203. de.mode &= ~(ModeAppend|ModeExclusive|ModeTemporary|0777);
  204. de.mode |= mode;
  205. op = 1;
  206. }
  207. tsync = 0;
  208. }
  209. if(dir.mtime != ~0){
  210. if(dir.mtime != de.mtime){
  211. de.mtime = dir.mtime;
  212. op = 1;
  213. }
  214. tsync = 0;
  215. }
  216. if(dir.length != ~0){
  217. if(dir.length != de.size){
  218. /*
  219. * Cannot change length on append-only files.
  220. * If we're changing the append bit, it's okay.
  221. */
  222. if(de.mode & oldmode & ModeAppend){
  223. vtSetError("wstat -- attempt to change length of append-only file");
  224. goto error;
  225. }
  226. if(de.mode & ModeDir){
  227. vtSetError("wstat -- attempt to change length of directory");
  228. goto error;
  229. }
  230. de.size = dir.length;
  231. op = 1;
  232. }
  233. tsync = 0;
  234. }
  235. /*
  236. * Check for permission to change .mode, .mtime or .length,
  237. * must be owner or leader of either group, for which test gid
  238. * is needed; permission checks on gid will be done later.
  239. */
  240. if(dir.gid != nil && *dir.gid != '\0'){
  241. if((gid = uidByUname(dir.gid)) == nil){
  242. vtSetError("wstat -- unknown gid");
  243. goto error;
  244. }
  245. tsync = 0;
  246. }
  247. else
  248. gid = vtStrDup(de.gid);
  249. wstatallow = (fsysWstatAllow(fid->fsys) || (m->con->flags&ConWstatAllow));
  250. /*
  251. * 'Gl' counts whether neither, one or both groups are led.
  252. */
  253. gl = groupLeader(gid, fid->uname) != 0;
  254. gl += groupLeader(de.gid, fid->uname) != 0;
  255. if(op && !wstatallow){
  256. if(strcmp(fid->uid, de.uid) != 0 && !gl){
  257. vtSetError("wstat -- not owner or group leader");
  258. goto error;
  259. }
  260. }
  261. /*
  262. * Check for permission to change group, must be
  263. * either owner and in new group or leader of both groups.
  264. * If gid is nil here then
  265. */
  266. if(strcmp(gid, de.gid) != 0){
  267. if(!wstatallow
  268. && !(strcmp(fid->uid, de.uid) == 0 && groupMember(gid, fid->uname))
  269. && !(gl == 2)){
  270. vtSetError("wstat -- not owner and not group leaders");
  271. goto error;
  272. }
  273. vtMemFree(de.gid);
  274. de.gid = gid;
  275. gid = nil;
  276. op = 1;
  277. tsync = 0;
  278. }
  279. /*
  280. * Rename.
  281. * Check .name is valid and different to the current.
  282. * If so, check write permission in parent.
  283. */
  284. if(dir.name != nil && *dir.name != '\0'){
  285. if(!validFileName(dir.name))
  286. goto error;
  287. if(strcmp(dir.name, de.elem) != 0){
  288. if(permParent(fid, PermW) <= 0)
  289. goto error;
  290. vtMemFree(de.elem);
  291. de.elem = vtStrDup(dir.name);
  292. op = 1;
  293. }
  294. tsync = 0;
  295. }
  296. /*
  297. * Check for permission to change owner - must be god.
  298. */
  299. if(dir.uid != nil && *dir.uid != '\0'){
  300. if((uid = uidByUname(dir.uid)) == nil){
  301. vtSetError("wstat -- unknown uid");
  302. goto error;
  303. }
  304. if(strcmp(uid, de.uid) != 0){
  305. if(!wstatallow){
  306. vtSetError("wstat -- not owner");
  307. goto error;
  308. }
  309. if(strcmp(uid, uidnoworld) == 0){
  310. vtSetError(EPermission);
  311. goto error;
  312. }
  313. vtMemFree(de.uid);
  314. de.uid = uid;
  315. uid = nil;
  316. op = 1;
  317. }
  318. tsync = 0;
  319. }
  320. if(op)
  321. retval = fileSetDir(fid->file, &de, fid->uid);
  322. else
  323. retval = 1;
  324. if(tsync){
  325. /*
  326. * All values were defaulted,
  327. * make the state of the file exactly what it
  328. * claims to be before returning...
  329. */
  330. USED(tsync);
  331. }
  332. error:
  333. deCleanup(&de);
  334. vtMemFree(strs);
  335. if(gid != nil)
  336. vtMemFree(gid);
  337. if(uid != nil)
  338. vtMemFree(uid);
  339. error0:
  340. fidPut(fid);
  341. return retval;
  342. };
  343. static int
  344. rTstat(Msg* m)
  345. {
  346. Dir dir;
  347. Fid *fid;
  348. DirEntry de;
  349. if((fid = fidGet(m->con, m->t.fid, 0)) == nil)
  350. return 0;
  351. if(fid->qid.type & QTAUTH){
  352. memset(&dir, 0, sizeof(Dir));
  353. dir.qid = fid->qid;
  354. dir.mode = DMAUTH;
  355. dir.atime = time(0L);
  356. dir.mtime = dir.atime;
  357. dir.length = 0;
  358. dir.name = "#¿";
  359. dir.uid = fid->uname;
  360. dir.gid = fid->uname;
  361. dir.muid = fid->uname;
  362. if((m->r.nstat = convD2M(&dir, m->data, m->con->msize)) == 0){
  363. vtSetError("stat QTAUTH botch");
  364. fidPut(fid);
  365. return 0;
  366. }
  367. m->r.stat = m->data;
  368. fidPut(fid);
  369. return 1;
  370. }
  371. if(!fileGetDir(fid->file, &de)){
  372. fidPut(fid);
  373. return 0;
  374. }
  375. fidPut(fid);
  376. /*
  377. * TODO: optimise this copy (in convS2M) away somehow.
  378. * This pettifoggery with m->data will do for the moment.
  379. */
  380. m->r.nstat = dirDe2M(&de, m->data, m->con->msize);
  381. m->r.stat = m->data;
  382. deCleanup(&de);
  383. return 1;
  384. }
  385. static int
  386. _rTclunk(Fid* fid, int remove)
  387. {
  388. int rok;
  389. if(fid->excl)
  390. exclFree(fid);
  391. rok = 1;
  392. if(remove && !(fid->qid.type & QTAUTH)){
  393. if((rok = permParent(fid, PermW)) > 0)
  394. rok = fileRemove(fid->file, fid->uid);
  395. }
  396. fidClunk(fid);
  397. return rok;
  398. }
  399. static int
  400. rTremove(Msg* m)
  401. {
  402. Fid *fid;
  403. if((fid = fidGet(m->con, m->t.fid, FidFWlock)) == nil)
  404. return 0;
  405. return _rTclunk(fid, 1);
  406. }
  407. static int
  408. rTclunk(Msg* m)
  409. {
  410. Fid *fid;
  411. if((fid = fidGet(m->con, m->t.fid, FidFWlock)) == nil)
  412. return 0;
  413. _rTclunk(fid, (fid->open & FidORclose));
  414. return 1;
  415. }
  416. static int
  417. rTwrite(Msg* m)
  418. {
  419. Fid *fid;
  420. int count, n;
  421. if((fid = fidGet(m->con, m->t.fid, 0)) == nil)
  422. return 0;
  423. if(!(fid->open & FidOWrite)){
  424. vtSetError("fid not open for write");
  425. goto error;
  426. }
  427. count = m->t.count;
  428. if(count < 0 || count > m->con->msize-IOHDRSZ){
  429. vtSetError("write count too big");
  430. goto error;
  431. }
  432. if(m->t.offset < 0){
  433. vtSetError("write offset negative");
  434. goto error;
  435. }
  436. if(fid->excl != nil && !exclUpdate(fid))
  437. goto error;
  438. if(fid->qid.type & QTDIR){
  439. vtSetError("is a directory");
  440. goto error;
  441. }
  442. else if(fid->qid.type & QTAUTH)
  443. n = authWrite(fid, m->t.data, count);
  444. else
  445. n = fileWrite(fid->file, m->t.data, count, m->t.offset, fid->uid);
  446. if(n < 0)
  447. goto error;
  448. m->r.count = n;
  449. fidPut(fid);
  450. return 1;
  451. error:
  452. fidPut(fid);
  453. return 0;
  454. }
  455. static int
  456. rTread(Msg* m)
  457. {
  458. Fid *fid;
  459. uchar *data;
  460. int count, n;
  461. if((fid = fidGet(m->con, m->t.fid, 0)) == nil)
  462. return 0;
  463. if(!(fid->open & FidORead)){
  464. vtSetError("fid not open for read");
  465. goto error;
  466. }
  467. count = m->t.count;
  468. if(count < 0 || count > m->con->msize-IOHDRSZ){
  469. vtSetError("read count too big");
  470. goto error;
  471. }
  472. if(m->t.offset < 0){
  473. vtSetError("read offset negative");
  474. goto error;
  475. }
  476. if(fid->excl != nil && !exclUpdate(fid))
  477. goto error;
  478. /*
  479. * TODO: optimise this copy (in convS2M) away somehow.
  480. * This pettifoggery with m->data will do for the moment.
  481. */
  482. data = m->data+IOHDRSZ;
  483. if(fid->qid.type & QTDIR)
  484. n = dirRead(fid, data, count, m->t.offset);
  485. else if(fid->qid.type & QTAUTH)
  486. n = authRead(fid, data, count);
  487. else
  488. n = fileRead(fid->file, data, count, m->t.offset);
  489. if(n < 0)
  490. goto error;
  491. m->r.count = n;
  492. m->r.data = (char*)data;
  493. fidPut(fid);
  494. return 1;
  495. error:
  496. fidPut(fid);
  497. return 0;
  498. }
  499. static int
  500. rTcreate(Msg* m)
  501. {
  502. Fid *fid;
  503. File *file;
  504. ulong mode;
  505. int omode, open, perm;
  506. if((fid = fidGet(m->con, m->t.fid, FidFWlock)) == nil)
  507. return 0;
  508. if(fid->open){
  509. vtSetError("fid open for I/O");
  510. goto error;
  511. }
  512. if(fileIsRoFs(fid->file) || !groupWriteMember(fid->uname)){
  513. vtSetError("read-only filesystem");
  514. goto error;
  515. }
  516. if(!fileIsDir(fid->file)){
  517. vtSetError("not a directory");
  518. goto error;
  519. }
  520. if(permFid(fid, PermW) <= 0)
  521. goto error;
  522. if(!validFileName(m->t.name))
  523. goto error;
  524. if(strcmp(fid->uid, uidnoworld) == 0){
  525. vtSetError(EPermission);
  526. goto error;
  527. }
  528. omode = m->t.mode & OMODE;
  529. open = 0;
  530. if(omode == OREAD || omode == ORDWR || omode == OEXEC)
  531. open |= FidORead;
  532. if(omode == OWRITE || omode == ORDWR)
  533. open |= FidOWrite;
  534. if((open & (FidOWrite|FidORead)) == 0){
  535. vtSetError("unknown mode");
  536. goto error;
  537. }
  538. if(m->t.perm & DMDIR){
  539. if((m->t.mode & (ORCLOSE|OTRUNC)) || (open & FidOWrite)){
  540. vtSetError("illegal mode");
  541. goto error;
  542. }
  543. if(m->t.perm & DMAPPEND){
  544. vtSetError("illegal perm");
  545. goto error;
  546. }
  547. }
  548. mode = fileGetMode(fid->file);
  549. perm = m->t.perm;
  550. if(m->t.perm & DMDIR)
  551. perm &= ~0777|(mode & 0777);
  552. else
  553. perm &= ~0666|(mode & 0666);
  554. mode = perm & 0777;
  555. if(m->t.perm & DMDIR)
  556. mode |= ModeDir;
  557. if(m->t.perm & DMAPPEND)
  558. mode |= ModeAppend;
  559. if(m->t.perm & DMEXCL)
  560. mode |= ModeExclusive;
  561. if(m->t.perm & DMTMP)
  562. mode |= ModeTemporary;
  563. if((file = fileCreate(fid->file, m->t.name, mode, fid->uid)) == nil){
  564. fidPut(fid);
  565. return 0;
  566. }
  567. fileDecRef(fid->file);
  568. fid->qid.vers = fileGetMcount(file);
  569. fid->qid.path = fileGetId(file);
  570. fid->file = file;
  571. mode = fileGetMode(fid->file);
  572. if(mode & ModeDir)
  573. fid->qid.type = QTDIR;
  574. else
  575. fid->qid.type = QTFILE;
  576. if(mode & ModeAppend)
  577. fid->qid.type |= QTAPPEND;
  578. if(mode & ModeExclusive){
  579. fid->qid.type |= QTEXCL;
  580. assert(exclAlloc(fid) != 0);
  581. }
  582. if(m->t.mode & ORCLOSE)
  583. open |= FidORclose;
  584. fid->open = open;
  585. m->r.qid = fid->qid;
  586. m->r.iounit = m->con->msize-IOHDRSZ;
  587. fidPut(fid);
  588. return 1;
  589. error:
  590. fidPut(fid);
  591. return 0;
  592. }
  593. static int
  594. rTopen(Msg* m)
  595. {
  596. Fid *fid;
  597. int isdir, mode, omode, open, rofs;
  598. if((fid = fidGet(m->con, m->t.fid, FidFWlock)) == nil)
  599. return 0;
  600. if(fid->open){
  601. vtSetError("fid open for I/O");
  602. goto error;
  603. }
  604. isdir = fileIsDir(fid->file);
  605. open = 0;
  606. rofs = fileIsRoFs(fid->file) || !groupWriteMember(fid->uname);
  607. if(m->t.mode & ORCLOSE){
  608. if(isdir){
  609. vtSetError("is a directory");
  610. goto error;
  611. }
  612. if(rofs){
  613. vtSetError("read-only filesystem");
  614. goto error;
  615. }
  616. if(permParent(fid, PermW) <= 0)
  617. goto error;
  618. open |= FidORclose;
  619. }
  620. omode = m->t.mode & OMODE;
  621. if(omode == OREAD || omode == ORDWR){
  622. if(permFid(fid, PermR) <= 0)
  623. goto error;
  624. open |= FidORead;
  625. }
  626. if(omode == OWRITE || omode == ORDWR || (m->t.mode & OTRUNC)){
  627. if(isdir){
  628. vtSetError("is a directory");
  629. goto error;
  630. }
  631. if(rofs){
  632. vtSetError("read-only filesystem");
  633. goto error;
  634. }
  635. if(permFid(fid, PermW) <= 0)
  636. goto error;
  637. open |= FidOWrite;
  638. }
  639. if(omode == OEXEC){
  640. if(isdir){
  641. vtSetError("is a directory");
  642. goto error;
  643. }
  644. if(permFid(fid, PermX) <= 0)
  645. goto error;
  646. open |= FidORead;
  647. }
  648. if((open & (FidOWrite|FidORead)) == 0){
  649. vtSetError("unknown mode");
  650. goto error;
  651. }
  652. mode = fileGetMode(fid->file);
  653. if((mode & ModeExclusive) && exclAlloc(fid) == 0)
  654. goto error;
  655. /*
  656. * Everything checks out, try to commit any changes.
  657. */
  658. if((m->t.mode & OTRUNC) && !(mode & ModeAppend))
  659. if(!fileTruncate(fid->file, fid->uid))
  660. goto error;
  661. if(isdir && fid->db != nil){
  662. dirBufFree(fid->db);
  663. fid->db = nil;
  664. }
  665. fid->qid.vers = fileGetMcount(fid->file);
  666. m->r.qid = fid->qid;
  667. m->r.iounit = m->con->msize-IOHDRSZ;
  668. fid->open = open;
  669. fidPut(fid);
  670. return 1;
  671. error:
  672. if(fid->excl != nil)
  673. exclFree(fid);
  674. fidPut(fid);
  675. return 0;
  676. }
  677. static int
  678. rTwalk(Msg* m)
  679. {
  680. Qid qid;
  681. Fcall *r, *t;
  682. int nwname, wlock;
  683. File *file, *nfile;
  684. Fid *fid, *ofid, *nfid;
  685. t = &m->t;
  686. if(t->fid == t->newfid)
  687. wlock = FidFWlock;
  688. else
  689. wlock = 0;
  690. /*
  691. * The file identified by t->fid must be valid in the
  692. * current session and must not have been opened for I/O
  693. * by an open or create message.
  694. */
  695. if((ofid = fidGet(m->con, t->fid, wlock)) == nil)
  696. return 0;
  697. if(ofid->open){
  698. vtSetError("file open for I/O");
  699. fidPut(ofid);
  700. return 0;
  701. }
  702. /*
  703. * If newfid is not the same as fid, allocate a new file;
  704. * a side effect is checking newfid is not already in use (error);
  705. * if there are no names to walk this will be equivalent to a
  706. * simple 'clone' operation.
  707. * It's a no-op if newfid is the same as fid and t->nwname is 0.
  708. */
  709. nfid = nil;
  710. if(t->fid != t->newfid){
  711. nfid = fidGet(m->con, t->newfid, FidFWlock|FidFCreate);
  712. if(nfid == nil){
  713. vtSetError("%s: walk: newfid 0x%ud in use",
  714. argv0, t->newfid);
  715. fidPut(ofid);
  716. return 0;
  717. }
  718. nfid->open = ofid->open & ~FidORclose;
  719. nfid->file = fileIncRef(ofid->file);
  720. nfid->qid = ofid->qid;
  721. nfid->uid = vtStrDup(ofid->uid);
  722. nfid->uname = vtStrDup(ofid->uname);
  723. nfid->fsys = fsysIncRef(ofid->fsys);
  724. fid = nfid;
  725. }
  726. else
  727. fid = ofid;
  728. r = &m->r;
  729. r->nwqid = 0;
  730. if(t->nwname == 0){
  731. if(nfid != nil)
  732. fidPut(nfid);
  733. fidPut(ofid);
  734. return 1;
  735. }
  736. file = fid->file;
  737. fileIncRef(file);
  738. qid = fid->qid;
  739. for(nwname = 0; nwname < t->nwname; nwname++){
  740. /*
  741. * Walked elements must represent a directory and
  742. * the implied user must have permission to search
  743. * the directory. Walking .. is always allowed, so that
  744. * you can't walk into a directory and then not be able
  745. * to walk out of it.
  746. */
  747. if(!(qid.type & QTDIR)){
  748. vtSetError("not a directory");
  749. break;
  750. }
  751. switch(permFile(file, fid, PermX)){
  752. case 1:
  753. break;
  754. case 0:
  755. if(strcmp(t->wname[nwname], "..") == 0)
  756. break;
  757. case -1:
  758. goto Out;
  759. }
  760. if((nfile = fileWalk(file, t->wname[nwname])) == nil)
  761. break;
  762. fileDecRef(file);
  763. file = nfile;
  764. qid.type = QTFILE;
  765. if(fileIsDir(file))
  766. qid.type = QTDIR;
  767. if(fileIsAppend(file))
  768. qid.type |= QTAPPEND;
  769. if(fileIsTemporary(file))
  770. qid.type |= QTTMP;
  771. if(fileIsExclusive(file))
  772. qid.type |= QTEXCL;
  773. qid.vers = fileGetMcount(file);
  774. qid.path = fileGetId(file);
  775. r->wqid[r->nwqid++] = qid;
  776. }
  777. if(nwname == t->nwname){
  778. /*
  779. * Walked all elements. Update the target fid
  780. * from the temporary qid used during the walk,
  781. * and tidy up.
  782. */
  783. fid->qid = r->wqid[r->nwqid-1];
  784. fileDecRef(fid->file);
  785. fid->file = file;
  786. if(nfid != nil)
  787. fidPut(nfid);
  788. fidPut(ofid);
  789. return 1;
  790. }
  791. Out:
  792. /*
  793. * Didn't walk all elements, 'clunk' nfid if it exists
  794. * and leave fid untouched.
  795. * It's not an error if some of the elements were walked OK.
  796. */
  797. fileDecRef(file);
  798. if(nfid != nil)
  799. fidClunk(nfid);
  800. fidPut(ofid);
  801. if(nwname == 0)
  802. return 0;
  803. return 1;
  804. }
  805. static int
  806. rTflush(Msg* m)
  807. {
  808. if(m->t.oldtag != NOTAG)
  809. msgFlush(m);
  810. return 1;
  811. }
  812. static void
  813. parseAname(char *aname, char **fsname, char **path)
  814. {
  815. char *s;
  816. if(aname && aname[0])
  817. s = vtStrDup(aname);
  818. else
  819. s = vtStrDup("main/active");
  820. *fsname = s;
  821. if((*path = strchr(s, '/')) != nil)
  822. *(*path)++ = '\0';
  823. else
  824. *path = "";
  825. }
  826. /*
  827. * Check remote IP address against /mnt/ipok.
  828. * Sources.cs.bell-labs.com uses this to disallow
  829. * network connections from Sudan, Libya, etc.,
  830. * following U.S. cryptography export regulations.
  831. */
  832. static int
  833. conIPCheck(Con* con)
  834. {
  835. char ok[256], *p;
  836. int fd;
  837. if(con->flags&ConIPCheck){
  838. if(con->remote[0] == 0){
  839. vtSetError("cannot verify unknown remote address");
  840. return 0;
  841. }
  842. if(access("/mnt/ipok/ok", AEXIST) < 0){
  843. /* mount closes the fd on success */
  844. if((fd = open("/srv/ipok", ORDWR)) >= 0
  845. && mount(fd, -1, "/mnt/ipok", MREPL, "") < 0)
  846. close(fd);
  847. if(access("/mnt/ipok/ok", AEXIST) < 0){
  848. vtSetError("cannot verify remote address");
  849. return 0;
  850. }
  851. }
  852. snprint(ok, sizeof ok, "/mnt/ipok/ok/%s", con->remote);
  853. if((p = strchr(ok, '!')) != nil)
  854. *p = 0;
  855. if(access(ok, AEXIST) < 0){
  856. vtSetError("restricted remote address");
  857. return 0;
  858. }
  859. }
  860. return 1;
  861. }
  862. static int
  863. rTattach(Msg* m)
  864. {
  865. Fid *fid;
  866. Fsys *fsys;
  867. char *fsname, *path;
  868. if((fid = fidGet(m->con, m->t.fid, FidFWlock|FidFCreate)) == nil)
  869. return 0;
  870. parseAname(m->t.aname, &fsname, &path);
  871. if((fsys = fsysGet(fsname)) == nil){
  872. fidClunk(fid);
  873. vtMemFree(fsname);
  874. return 0;
  875. }
  876. fid->fsys = fsys;
  877. if(m->t.uname[0] != '\0')
  878. fid->uname = vtStrDup(m->t.uname);
  879. else
  880. fid->uname = vtStrDup(unamenone);
  881. if((fid->con->flags&ConIPCheck) && !conIPCheck(fid->con)){
  882. consPrint("reject %s from %s: %R\n", fid->uname, fid->con->remote);
  883. fidClunk(fid);
  884. vtMemFree(fsname);
  885. return 0;
  886. }
  887. if(fsysNoAuthCheck(fsys) || (m->con->flags&ConNoAuthCheck)){
  888. if((fid->uid = uidByUname(fid->uname)) == nil)
  889. fid->uid = vtStrDup(unamenone);
  890. }
  891. else if(!authCheck(&m->t, fid, fsys)){
  892. fidClunk(fid);
  893. vtMemFree(fsname);
  894. return 0;
  895. }
  896. fsysFsRlock(fsys);
  897. if((fid->file = fsysGetRoot(fsys, path)) == nil){
  898. fsysFsRUnlock(fsys);
  899. fidClunk(fid);
  900. vtMemFree(fsname);
  901. return 0;
  902. }
  903. fsysFsRUnlock(fsys);
  904. vtMemFree(fsname);
  905. fid->qid = (Qid){fileGetId(fid->file), 0, QTDIR};
  906. m->r.qid = fid->qid;
  907. fidPut(fid);
  908. return 1;
  909. }
  910. static int
  911. rTauth(Msg* m)
  912. {
  913. int afd;
  914. Con *con;
  915. Fid *afid;
  916. Fsys *fsys;
  917. char *fsname, *path;
  918. parseAname(m->t.aname, &fsname, &path);
  919. if((fsys = fsysGet(fsname)) == nil){
  920. vtMemFree(fsname);
  921. return 0;
  922. }
  923. vtMemFree(fsname);
  924. if(fsysNoAuthCheck(fsys) || (m->con->flags&ConNoAuthCheck)){
  925. m->con->aok = 1;
  926. vtSetError("authentication disabled");
  927. fsysPut(fsys);
  928. return 0;
  929. }
  930. if(strcmp(m->t.uname, unamenone) == 0){
  931. vtSetError("user 'none' requires no authentication");
  932. fsysPut(fsys);
  933. return 0;
  934. }
  935. con = m->con;
  936. if((afid = fidGet(con, m->t.afid, FidFWlock|FidFCreate)) == nil){
  937. fsysPut(fsys);
  938. return 0;
  939. }
  940. afid->fsys = fsys;
  941. if((afd = open("/mnt/factotum/rpc", ORDWR)) < 0){
  942. vtSetError("can't open \"/mnt/factotum/rpc\"");
  943. fidClunk(afid);
  944. return 0;
  945. }
  946. if((afid->rpc = auth_allocrpc(afd)) == nil){
  947. close(afd);
  948. vtSetError("can't auth_allocrpc");
  949. fidClunk(afid);
  950. return 0;
  951. }
  952. if(auth_rpc(afid->rpc, "start", "proto=p9any role=server", 23) != ARok){
  953. vtSetError("can't auth_rpc");
  954. fidClunk(afid);
  955. return 0;
  956. }
  957. afid->open = FidOWrite|FidORead;
  958. afid->qid.type = QTAUTH;
  959. afid->qid.path = m->t.afid;
  960. afid->uname = vtStrDup(m->t.uname);
  961. m->r.qid = afid->qid;
  962. fidPut(afid);
  963. return 1;
  964. }
  965. static int
  966. rTversion(Msg* m)
  967. {
  968. int v;
  969. Con *con;
  970. Fcall *r, *t;
  971. t = &m->t;
  972. r = &m->r;
  973. con = m->con;
  974. vtLock(con->lock);
  975. if(con->state != ConInit){
  976. vtUnlock(con->lock);
  977. vtSetError("Tversion: down");
  978. return 0;
  979. }
  980. con->state = ConNew;
  981. /*
  982. * Release the karma of past lives and suffering.
  983. * Should this be done before or after checking the
  984. * validity of the Tversion?
  985. */
  986. fidClunkAll(con);
  987. if(t->tag != NOTAG){
  988. vtUnlock(con->lock);
  989. vtSetError("Tversion: invalid tag");
  990. return 0;
  991. }
  992. if(t->msize < 256){
  993. vtUnlock(con->lock);
  994. vtSetError("Tversion: message size too small");
  995. return 0;
  996. }
  997. if(t->msize < con->msize)
  998. r->msize = t->msize;
  999. else
  1000. r->msize = con->msize;
  1001. r->version = "unknown";
  1002. if(t->version[0] == '9' && t->version[1] == 'P'){
  1003. /*
  1004. * Currently, the only defined version
  1005. * is "9P2000"; ignore any later versions.
  1006. */
  1007. v = strtol(&t->version[2], 0, 10);
  1008. if(v >= 2000){
  1009. r->version = VERSION9P;
  1010. con->msize = r->msize;
  1011. con->state = ConUp;
  1012. }
  1013. else if(strcmp(t->version, "9PEoF") == 0){
  1014. r->version = "9PEoF";
  1015. con->msize = r->msize;
  1016. con->state = ConMoribund;
  1017. /*
  1018. * Don't want to attempt to write this
  1019. * message as the connection may be already
  1020. * closed.
  1021. */
  1022. m->state = MsgF;
  1023. }
  1024. }
  1025. vtUnlock(con->lock);
  1026. return 1;
  1027. }
  1028. int (*rFcall[Tmax])(Msg*) = {
  1029. [Tversion] = rTversion,
  1030. [Tauth] = rTauth,
  1031. [Tattach] = rTattach,
  1032. [Tflush] = rTflush,
  1033. [Twalk] = rTwalk,
  1034. [Topen] = rTopen,
  1035. [Tcreate] = rTcreate,
  1036. [Tread] = rTread,
  1037. [Twrite] = rTwrite,
  1038. [Tclunk] = rTclunk,
  1039. [Tremove] = rTremove,
  1040. [Tstat] = rTstat,
  1041. [Twstat] = rTwstat,
  1042. };