9p.c 23 KB

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