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. 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("walk: newfid 0x%ud in use", t->newfid);
  714. fidPut(ofid);
  715. return 0;
  716. }
  717. nfid->open = ofid->open & ~FidORclose;
  718. nfid->file = fileIncRef(ofid->file);
  719. nfid->qid = ofid->qid;
  720. nfid->uid = vtStrDup(ofid->uid);
  721. nfid->uname = vtStrDup(ofid->uname);
  722. nfid->fsys = fsysIncRef(ofid->fsys);
  723. fid = nfid;
  724. }
  725. else
  726. fid = ofid;
  727. r = &m->r;
  728. r->nwqid = 0;
  729. if(t->nwname == 0){
  730. if(nfid != nil)
  731. fidPut(nfid);
  732. fidPut(ofid);
  733. return 1;
  734. }
  735. file = fid->file;
  736. fileIncRef(file);
  737. qid = fid->qid;
  738. for(nwname = 0; nwname < t->nwname; nwname++){
  739. /*
  740. * Walked elements must represent a directory and
  741. * the implied user must have permission to search
  742. * the directory. Walking .. is always allowed, so that
  743. * you can't walk into a directory and then not be able
  744. * to walk out of it.
  745. */
  746. if(!(qid.type & QTDIR)){
  747. vtSetError("not a directory");
  748. break;
  749. }
  750. switch(permFile(file, fid, PermX)){
  751. case 1:
  752. break;
  753. case 0:
  754. if(strcmp(t->wname[nwname], "..") == 0)
  755. break;
  756. case -1:
  757. goto Out;
  758. }
  759. if((nfile = fileWalk(file, t->wname[nwname])) == nil)
  760. break;
  761. fileDecRef(file);
  762. file = nfile;
  763. qid.type = QTFILE;
  764. if(fileIsDir(file))
  765. qid.type = QTDIR;
  766. qid.vers = fileGetMcount(file);
  767. qid.path = fileGetId(file);
  768. r->wqid[r->nwqid++] = qid;
  769. }
  770. if(nwname == t->nwname){
  771. /*
  772. * Walked all elements. Update the target fid
  773. * from the temporary qid used during the walk,
  774. * and tidy up.
  775. */
  776. fid->qid = r->wqid[r->nwqid-1];
  777. fileDecRef(fid->file);
  778. fid->file = file;
  779. if(nfid != nil)
  780. fidPut(nfid);
  781. fidPut(ofid);
  782. return 1;
  783. }
  784. Out:
  785. /*
  786. * Didn't walk all elements, 'clunk' nfid if it exists
  787. * and leave fid untouched.
  788. * It's not an error if some of the elements were walked OK.
  789. */
  790. fileDecRef(file);
  791. if(nfid != nil)
  792. fidClunk(nfid);
  793. fidPut(ofid);
  794. if(nwname == 0)
  795. return 0;
  796. return 1;
  797. }
  798. static int
  799. rTflush(Msg* m)
  800. {
  801. if(m->t.oldtag != NOTAG)
  802. msgFlush(m);
  803. return 1;
  804. }
  805. static void
  806. parseAname(char *aname, char **fsname, char **path)
  807. {
  808. char *s;
  809. if(aname && aname[0])
  810. s = vtStrDup(aname);
  811. else
  812. s = vtStrDup("main/active");
  813. *fsname = s;
  814. if((*path = strchr(s, '/')) != nil)
  815. *(*path)++ = '\0';
  816. else
  817. *path = "";
  818. }
  819. /*
  820. * Check remote IP address against /mnt/ipok.
  821. * Sources.cs.bell-labs.com uses this to disallow
  822. * network connections from Sudan, Libya, etc.,
  823. * following U.S. cryptography export regulations.
  824. */
  825. static int
  826. conIPCheck(Con* con)
  827. {
  828. char ok[256], *p;
  829. int fd;
  830. if(con->flags&ConIPCheck){
  831. if(con->remote[0] == 0){
  832. vtSetError("cannot verify unknown remote address");
  833. return 0;
  834. }
  835. if(access("/mnt/ipok/ok", AEXIST) < 0){
  836. /* mount closes the fd on success */
  837. if((fd = open("/srv/ipok", ORDWR)) >= 0
  838. && mount(fd, -1, "/mnt/ipok", MREPL, "") < 0)
  839. close(fd);
  840. if(access("/mnt/ipok/ok", AEXIST) < 0){
  841. vtSetError("cannot verify remote address");
  842. return 0;
  843. }
  844. }
  845. snprint(ok, sizeof ok, "/mnt/ipok/ok/%s", con->remote);
  846. if((p = strchr(ok, '!')) != nil)
  847. *p = 0;
  848. if(access(ok, AEXIST) < 0){
  849. vtSetError("restricted remote address");
  850. return 0;
  851. }
  852. }
  853. return 1;
  854. }
  855. static int
  856. rTattach(Msg* m)
  857. {
  858. Fid *fid;
  859. Fsys *fsys;
  860. char *fsname, *path;
  861. if((fid = fidGet(m->con, m->t.fid, FidFWlock|FidFCreate)) == nil)
  862. return 0;
  863. parseAname(m->t.aname, &fsname, &path);
  864. if((fsys = fsysGet(fsname)) == nil){
  865. fidClunk(fid);
  866. vtMemFree(fsname);
  867. return 0;
  868. }
  869. fid->fsys = fsys;
  870. if(m->t.uname[0] != '\0')
  871. fid->uname = vtStrDup(m->t.uname);
  872. else
  873. fid->uname = vtStrDup(unamenone);
  874. if((fid->con->flags&ConIPCheck) && !conIPCheck(fid->con)){
  875. consPrint("reject %s from %s: %R\n", fid->uname, fid->con->remote);
  876. fidClunk(fid);
  877. vtMemFree(fsname);
  878. return 0;
  879. }
  880. if(fsysNoAuthCheck(fsys) || (m->con->flags&ConNoAuthCheck)){
  881. if((fid->uid = uidByUname(fid->uname)) == nil)
  882. fid->uid = vtStrDup(unamenone);
  883. }
  884. else if(!authCheck(&m->t, fid, fsys)){
  885. fidClunk(fid);
  886. vtMemFree(fsname);
  887. return 0;
  888. }
  889. fsysFsRlock(fsys);
  890. if((fid->file = fsysGetRoot(fsys, path)) == nil){
  891. fsysFsRUnlock(fsys);
  892. fidClunk(fid);
  893. vtMemFree(fsname);
  894. return 0;
  895. }
  896. fsysFsRUnlock(fsys);
  897. vtMemFree(fsname);
  898. fid->qid = (Qid){fileGetId(fid->file), 0, QTDIR};
  899. m->r.qid = fid->qid;
  900. fidPut(fid);
  901. return 1;
  902. }
  903. static int
  904. rTauth(Msg* m)
  905. {
  906. int afd;
  907. Con *con;
  908. Fid *afid;
  909. Fsys *fsys;
  910. char *fsname, *path;
  911. parseAname(m->t.aname, &fsname, &path);
  912. if((fsys = fsysGet(fsname)) == nil){
  913. vtMemFree(fsname);
  914. return 0;
  915. }
  916. vtMemFree(fsname);
  917. if(fsysNoAuthCheck(fsys) || (m->con->flags&ConNoAuthCheck)){
  918. m->con->aok = 1;
  919. vtSetError("authentication disabled");
  920. fsysPut(fsys);
  921. return 0;
  922. }
  923. if(strcmp(m->t.uname, unamenone) == 0){
  924. vtSetError("user 'none' requires no authentication");
  925. fsysPut(fsys);
  926. return 0;
  927. }
  928. con = m->con;
  929. if((afid = fidGet(con, m->t.afid, FidFWlock|FidFCreate)) == nil){
  930. fsysPut(fsys);
  931. return 0;
  932. }
  933. afid->fsys = fsys;
  934. if((afd = open("/mnt/factotum/rpc", ORDWR)) < 0){
  935. vtSetError("can't open \"/mnt/factotum/rpc\"");
  936. fidClunk(afid);
  937. return 0;
  938. }
  939. if((afid->rpc = auth_allocrpc(afd)) == nil){
  940. close(afd);
  941. vtSetError("can't auth_allocrpc");
  942. fidClunk(afid);
  943. return 0;
  944. }
  945. if(auth_rpc(afid->rpc, "start", "proto=p9any role=server", 23) != ARok){
  946. vtSetError("can't auth_rpc");
  947. fidClunk(afid);
  948. return 0;
  949. }
  950. afid->open = FidOWrite|FidORead;
  951. afid->qid.type = QTAUTH;
  952. afid->qid.path = m->t.afid;
  953. afid->uname = vtStrDup(m->t.uname);
  954. m->r.qid = afid->qid;
  955. fidPut(afid);
  956. return 1;
  957. }
  958. static int
  959. rTversion(Msg* m)
  960. {
  961. int v;
  962. Con *con;
  963. Fcall *r, *t;
  964. t = &m->t;
  965. r = &m->r;
  966. con = m->con;
  967. vtLock(con->lock);
  968. if(con->state != ConInit){
  969. vtUnlock(con->lock);
  970. vtSetError("Tversion: down");
  971. return 0;
  972. }
  973. con->state = ConNew;
  974. /*
  975. * Release the karma of past lives and suffering.
  976. * Should this be done before or after checking the
  977. * validity of the Tversion?
  978. */
  979. fidClunkAll(con);
  980. if(t->tag != NOTAG){
  981. vtUnlock(con->lock);
  982. vtSetError("Tversion: invalid tag");
  983. return 0;
  984. }
  985. if(t->msize < 256){
  986. vtUnlock(con->lock);
  987. vtSetError("Tversion: message size too small");
  988. return 0;
  989. }
  990. if(t->msize < con->msize)
  991. r->msize = t->msize;
  992. else
  993. r->msize = con->msize;
  994. r->version = "unknown";
  995. if(t->version[0] == '9' && t->version[1] == 'P'){
  996. /*
  997. * Currently, the only defined version
  998. * is "9P2000"; ignore any later versions.
  999. */
  1000. v = strtol(&t->version[2], 0, 10);
  1001. if(v >= 2000){
  1002. r->version = VERSION9P;
  1003. con->msize = r->msize;
  1004. con->state = ConUp;
  1005. }
  1006. else if(strcmp(t->version, "9PEoF") == 0){
  1007. r->version = "9PEoF";
  1008. con->msize = r->msize;
  1009. con->state = ConMoribund;
  1010. /*
  1011. * Don't want to attempt to write this
  1012. * message as the connection may be already
  1013. * closed.
  1014. */
  1015. m->state = MsgF;
  1016. }
  1017. }
  1018. vtUnlock(con->lock);
  1019. return 1;
  1020. }
  1021. int (*rFcall[Tmax])(Msg*) = {
  1022. [Tversion] = rTversion,
  1023. [Tauth] = rTauth,
  1024. [Tattach] = rTattach,
  1025. [Tflush] = rTflush,
  1026. [Twalk] = rTwalk,
  1027. [Topen] = rTopen,
  1028. [Tcreate] = rTcreate,
  1029. [Tread] = rTread,
  1030. [Twrite] = rTwrite,
  1031. [Tclunk] = rTclunk,
  1032. [Tremove] = rTremove,
  1033. [Tstat] = rTstat,
  1034. [Twstat] = rTwstat,
  1035. };