9p.c 22 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174
  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. fid->qid.vers = fileGetMcount(fid->file);
  662. }
  663. if(isdir && fid->db != nil){
  664. dirBufFree(fid->db);
  665. fid->db = nil;
  666. }
  667. m->r.qid = fid->qid;
  668. m->r.iounit = m->con->msize-IOHDRSZ;
  669. fid->open = open;
  670. fidPut(fid);
  671. return 1;
  672. error:
  673. if(fid->excl != nil)
  674. exclFree(fid);
  675. fidPut(fid);
  676. return 0;
  677. }
  678. static int
  679. rTwalk(Msg* m)
  680. {
  681. Qid qid;
  682. Fcall *r, *t;
  683. int nwname, wlock;
  684. File *file, *nfile;
  685. Fid *fid, *ofid, *nfid;
  686. t = &m->t;
  687. if(t->fid == t->newfid)
  688. wlock = FidFWlock;
  689. else
  690. wlock = 0;
  691. /*
  692. * The file identified by t->fid must be valid in the
  693. * current session and must not have been opened for I/O
  694. * by an open or create message.
  695. */
  696. if((ofid = fidGet(m->con, t->fid, wlock)) == nil)
  697. return 0;
  698. if(ofid->open){
  699. vtSetError("file open for I/O");
  700. fidPut(ofid);
  701. return 0;
  702. }
  703. /*
  704. * If newfid is not the same as fid, allocate a new file;
  705. * a side effect is checking newfid is not already in use (error);
  706. * if there are no names to walk this will be equivalent to a
  707. * simple 'clone' operation.
  708. * It's a no-op if newfid is the same as fid and t->nwname is 0.
  709. */
  710. nfid = nil;
  711. if(t->fid != t->newfid){
  712. nfid = fidGet(m->con, t->newfid, FidFWlock|FidFCreate);
  713. if(nfid == nil){
  714. vtSetError("fid in use");
  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. qid.vers = fileGetMcount(file);
  768. qid.path = fileGetId(file);
  769. r->wqid[r->nwqid++] = qid;
  770. }
  771. if(nwname == t->nwname){
  772. /*
  773. * Walked all elements. Update the target fid
  774. * from the temporary qid used during the walk,
  775. * and tidy up.
  776. */
  777. fid->qid = r->wqid[r->nwqid-1];
  778. fileDecRef(fid->file);
  779. fid->file = file;
  780. if(nfid != nil)
  781. fidPut(nfid);
  782. fidPut(ofid);
  783. return 1;
  784. }
  785. Out:
  786. /*
  787. * Didn't walk all elements, 'clunk' nfid if it exists
  788. * and leave fid untouched.
  789. * It's not an error if some of the elements were walked OK.
  790. */
  791. fileDecRef(file);
  792. if(nfid != nil)
  793. fidClunk(nfid);
  794. fidPut(ofid);
  795. if(nwname == 0)
  796. return 0;
  797. return 1;
  798. }
  799. static int
  800. rTflush(Msg* m)
  801. {
  802. if(m->t.oldtag != NOTAG)
  803. msgFlush(m);
  804. return 1;
  805. }
  806. static void
  807. parseAname(char *aname, char **fsname, char **path)
  808. {
  809. char *s;
  810. if(aname && aname[0])
  811. s = vtStrDup(aname);
  812. else
  813. s = vtStrDup("main/active");
  814. *fsname = s;
  815. if((*path = strchr(s, '/')) != nil)
  816. *(*path)++ = '\0';
  817. else
  818. *path = "";
  819. }
  820. /*
  821. * Check remote IP address against /mnt/ipok.
  822. * Sources.cs.bell-labs.com uses this to disallow
  823. * network connections from Sudan, Libya, etc.,
  824. * following U.S. cryptography export regulations.
  825. */
  826. static int
  827. conIPCheck(Con* con)
  828. {
  829. char ok[256], *p;
  830. int fd;
  831. if(con->flags&ConIPCheck){
  832. if(con->remote[0] == 0){
  833. vtSetError("cannot verify unknown remote address");
  834. return 0;
  835. }
  836. if(access("/mnt/ipok/ok", AEXIST) < 0){
  837. /* mount closes the fd on success */
  838. if((fd = open("/srv/ipok", ORDWR)) >= 0
  839. && mount(fd, -1, "/mnt/ipok", MREPL, "") < 0)
  840. close(fd);
  841. if(access("/mnt/ipok/ok", AEXIST) < 0){
  842. vtSetError("cannot verify remote address");
  843. return 0;
  844. }
  845. }
  846. snprint(ok, sizeof ok, "/mnt/ipok/ok/%s", con->remote);
  847. if((p = strchr(ok, '!')) != nil)
  848. *p = 0;
  849. if(access(ok, AEXIST) < 0){
  850. vtSetError("restricted remote address");
  851. return 0;
  852. }
  853. }
  854. return 1;
  855. }
  856. static int
  857. rTattach(Msg* m)
  858. {
  859. Fid *fid;
  860. Fsys *fsys;
  861. char *fsname, *path;
  862. if((fid = fidGet(m->con, m->t.fid, FidFWlock|FidFCreate)) == nil)
  863. return 0;
  864. parseAname(m->t.aname, &fsname, &path);
  865. if((fsys = fsysGet(fsname)) == nil){
  866. fidClunk(fid);
  867. vtMemFree(fsname);
  868. return 0;
  869. }
  870. fid->fsys = fsys;
  871. if(m->t.uname[0] != '\0')
  872. fid->uname = vtStrDup(m->t.uname);
  873. else
  874. fid->uname = vtStrDup(unamenone);
  875. if((fid->con->flags&ConIPCheck) && !conIPCheck(fid->con)){
  876. consPrint("reject %s from %s: %R\n", fid->uname, fid->con->remote);
  877. fidClunk(fid);
  878. vtMemFree(fsname);
  879. return 0;
  880. }
  881. if(fsysNoAuthCheck(fsys) || (m->con->flags&ConNoAuthCheck)){
  882. if((fid->uid = uidByUname(fid->uname)) == nil)
  883. fid->uid = vtStrDup(unamenone);
  884. }
  885. else if(!authCheck(&m->t, fid, fsys)){
  886. fidClunk(fid);
  887. vtMemFree(fsname);
  888. return 0;
  889. }
  890. fsysFsRlock(fsys);
  891. if((fid->file = fsysGetRoot(fsys, path)) == nil){
  892. fsysFsRUnlock(fsys);
  893. fidClunk(fid);
  894. vtMemFree(fsname);
  895. return 0;
  896. }
  897. fsysFsRUnlock(fsys);
  898. vtMemFree(fsname);
  899. fid->qid = (Qid){fileGetId(fid->file), 0, QTDIR};
  900. m->r.qid = fid->qid;
  901. fidPut(fid);
  902. return 1;
  903. }
  904. static int
  905. rTauth(Msg* m)
  906. {
  907. int afd;
  908. Con *con;
  909. Fid *afid;
  910. Fsys *fsys;
  911. char *fsname, *path;
  912. parseAname(m->t.aname, &fsname, &path);
  913. if((fsys = fsysGet(fsname)) == nil){
  914. vtMemFree(fsname);
  915. return 0;
  916. }
  917. vtMemFree(fsname);
  918. if(fsysNoAuthCheck(fsys) || (m->con->flags&ConNoAuthCheck)){
  919. m->con->aok = 1;
  920. vtSetError("authentication disabled");
  921. fsysPut(fsys);
  922. return 0;
  923. }
  924. if(strcmp(m->t.uname, unamenone) == 0){
  925. vtSetError("user 'none' requires no authentication");
  926. fsysPut(fsys);
  927. return 0;
  928. }
  929. con = m->con;
  930. if((afid = fidGet(con, m->t.afid, FidFWlock|FidFCreate)) == nil){
  931. fsysPut(fsys);
  932. return 0;
  933. }
  934. afid->fsys = fsys;
  935. if((afd = open("/mnt/factotum/rpc", ORDWR)) < 0){
  936. vtSetError("can't open \"/mnt/factotum/rpc\"");
  937. fidClunk(afid);
  938. return 0;
  939. }
  940. if((afid->rpc = auth_allocrpc(afd)) == nil){
  941. close(afd);
  942. vtSetError("can't auth_allocrpc");
  943. fidClunk(afid);
  944. return 0;
  945. }
  946. if(auth_rpc(afid->rpc, "start", "proto=p9any role=server", 23) != ARok){
  947. vtSetError("can't auth_rpc");
  948. fidClunk(afid);
  949. return 0;
  950. }
  951. afid->open = FidOWrite|FidORead;
  952. afid->qid.type = QTAUTH;
  953. afid->qid.path = m->t.afid;
  954. afid->uname = vtStrDup(m->t.uname);
  955. m->r.qid = afid->qid;
  956. fidPut(afid);
  957. return 1;
  958. }
  959. static int
  960. rTversion(Msg* m)
  961. {
  962. int v;
  963. Con *con;
  964. Fcall *r, *t;
  965. t = &m->t;
  966. r = &m->r;
  967. con = m->con;
  968. vtLock(con->lock);
  969. if(con->state != ConInit){
  970. vtUnlock(con->lock);
  971. vtSetError("Tversion: down");
  972. return 0;
  973. }
  974. con->state = ConNew;
  975. /*
  976. * Release the karma of past lives and suffering.
  977. * Should this be done before or after checking the
  978. * validity of the Tversion?
  979. */
  980. fidClunkAll(con);
  981. if(t->tag != NOTAG){
  982. vtUnlock(con->lock);
  983. vtSetError("Tversion: invalid tag");
  984. return 0;
  985. }
  986. if(t->msize < 256){
  987. vtUnlock(con->lock);
  988. vtSetError("Tversion: message size too small");
  989. return 0;
  990. }
  991. if(t->msize < con->msize)
  992. r->msize = t->msize;
  993. else
  994. r->msize = con->msize;
  995. r->version = "unknown";
  996. if(t->version[0] == '9' && t->version[1] == 'P'){
  997. /*
  998. * Currently, the only defined version
  999. * is "9P2000"; ignore any later versions.
  1000. */
  1001. v = strtol(&t->version[2], 0, 10);
  1002. if(v >= 2000){
  1003. r->version = VERSION9P;
  1004. con->msize = r->msize;
  1005. con->state = ConUp;
  1006. }
  1007. else if(strcmp(t->version, "9PEoF") == 0){
  1008. r->version = "9PEoF";
  1009. con->msize = r->msize;
  1010. con->state = ConMoribund;
  1011. /*
  1012. * Don't want to attempt to write this
  1013. * message as the connection may be already
  1014. * closed.
  1015. */
  1016. m->state = MsgF;
  1017. }
  1018. }
  1019. vtUnlock(con->lock);
  1020. return 1;
  1021. }
  1022. int (*rFcall[Tmax])(Msg*) = {
  1023. [Tversion] = rTversion,
  1024. [Tauth] = rTauth,
  1025. [Tattach] = rTattach,
  1026. [Tflush] = rTflush,
  1027. [Twalk] = rTwalk,
  1028. [Topen] = rTopen,
  1029. [Tcreate] = rTcreate,
  1030. [Tread] = rTread,
  1031. [Twrite] = rTwrite,
  1032. [Tclunk] = rTclunk,
  1033. [Tremove] = rTremove,
  1034. [Tstat] = rTstat,
  1035. [Twstat] = rTwstat,
  1036. };