partfs.c 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587
  1. /*
  2. * partfs - serve an underlying file, with devsd-style partitions
  3. */
  4. #include <u.h>
  5. #include <libc.h>
  6. #include <auth.h>
  7. #include <fcall.h>
  8. #include <thread.h>
  9. #include <9p.h>
  10. typedef struct Part Part;
  11. struct Part
  12. {
  13. int inuse;
  14. int vers;
  15. ulong mode;
  16. char *name;
  17. vlong offset; /* in sectors */
  18. vlong length; /* in sectors */
  19. };
  20. enum
  21. {
  22. Qroot = 0,
  23. Qdir,
  24. Qctl,
  25. Qpart,
  26. };
  27. int fd = -1, ctlfd = -1;
  28. int rdonly;
  29. ulong ctlmode = 0666;
  30. ulong time0;
  31. vlong nsect, sectsize;
  32. char *inquiry = "partfs hard drive";
  33. char *sdname = "sdXX";
  34. Part tab[64];
  35. char*
  36. ctlstring(void)
  37. {
  38. Part *p;
  39. Fmt fmt;
  40. fmtstrinit(&fmt);
  41. fmtprint(&fmt, "inquiry %s\n", inquiry);
  42. fmtprint(&fmt, "geometry %lld %lld\n", nsect, sectsize);
  43. for (p = tab; p < tab + nelem(tab); p++)
  44. if (p->inuse)
  45. fmtprint(&fmt, "part %s %lld %lld\n",
  46. p->name, p->offset, p->length);
  47. return fmtstrflush(&fmt);
  48. }
  49. int
  50. addpart(char *name, vlong start, vlong end)
  51. {
  52. Part *p;
  53. if(start < 0 || start > end || end > nsect){
  54. werrstr("bad partition boundaries");
  55. return -1;
  56. }
  57. if (strcmp(name, "ctl") == 0 || strcmp(name, "data") == 0) {
  58. werrstr("partition name already in use");
  59. return -1;
  60. }
  61. for (p = tab; p < tab + nelem(tab) && p->inuse; p++)
  62. if (strcmp(p->name, name) == 0) {
  63. werrstr("partition name already in use");
  64. return -1;
  65. }
  66. if(p == tab + nelem(tab)){
  67. werrstr("no free partition slots");
  68. return -1;
  69. }
  70. p->inuse = 1;
  71. free(p->name);
  72. p->name = estrdup9p(name);
  73. p->offset = start;
  74. p->length = end - start;
  75. p->mode = ctlmode;
  76. p->vers++;
  77. return 0;
  78. }
  79. int
  80. delpart(char *s)
  81. {
  82. Part *p;
  83. for (p = tab; p < tab + nelem(tab); p++)
  84. if(p->inuse && strcmp(p->name, s) == 0)
  85. break;
  86. if(p == tab + nelem(tab)){
  87. werrstr("partition not found");
  88. return -1;
  89. }
  90. p->inuse = 0;
  91. free(p->name);
  92. p->name = nil;
  93. return 0;
  94. }
  95. static void
  96. addparts(char *buf)
  97. {
  98. char *f[4], *p, *q;
  99. /*
  100. * Use partitions passed from boot program,
  101. * e.g.
  102. * sdC0part=dos 63 123123/plan9 123123 456456
  103. * This happens before /boot sets hostname so the
  104. * partitions will have the null-string for user.
  105. */
  106. for(p = buf; p != nil; p = q){
  107. if(q = strchr(p, '/'))
  108. *q++ = '\0';
  109. if(tokenize(p, f, nelem(f)) >= 3 &&
  110. addpart(f[0], strtoull(f[1], 0, 0), strtoull(f[2], 0, 0)) < 0)
  111. fprint(2, "%s: addpart %s: %r\n", argv0, f[0]);
  112. }
  113. }
  114. static void
  115. ctlwrite0(Req *r, char *msg, Cmdbuf *cb)
  116. {
  117. vlong start, end;
  118. Part *p;
  119. r->ofcall.count = r->ifcall.count;
  120. if(cb->nf < 1){
  121. respond(r, "empty control message");
  122. return;
  123. }
  124. if(strcmp(cb->f[0], "part") == 0){
  125. if(cb->nf != 4){
  126. respondcmderror(r, cb, "part takes 3 args");
  127. return;
  128. }
  129. start = strtoll(cb->f[2], 0, 0);
  130. end = strtoll(cb->f[3], 0, 0);
  131. if(addpart(cb->f[1], start, end) < 0){
  132. respondcmderror(r, cb, "%r");
  133. return;
  134. }
  135. }
  136. else if(strcmp(cb->f[0], "delpart") == 0){
  137. if(cb->nf != 2){
  138. respondcmderror(r, cb, "delpart takes 1 arg");
  139. return;
  140. }
  141. if(delpart(cb->f[1]) < 0){
  142. respondcmderror(r, cb, "%r");
  143. return;
  144. }
  145. }
  146. else if(strcmp(cb->f[0], "inquiry") == 0){
  147. if(cb->nf != 2){
  148. respondcmderror(r, cb, "inquiry takes 1 arg");
  149. return;
  150. }
  151. free(inquiry);
  152. inquiry = estrdup9p(cb->f[1]);
  153. }
  154. else if(strcmp(cb->f[0], "geometry") == 0){
  155. if(cb->nf != 3){
  156. respondcmderror(r, cb, "geometry takes 2 args");
  157. return;
  158. }
  159. nsect = strtoll(cb->f[1], 0, 0);
  160. sectsize = strtoll(cb->f[2], 0, 0);
  161. if(tab[0].inuse && strcmp(tab[0].name, "data") == 0 &&
  162. tab[0].vers == 0){
  163. tab[0].offset = 0;
  164. tab[0].length = nsect;
  165. }
  166. for(p = tab; p < tab + nelem(tab); p++)
  167. if(p->inuse && p->offset + p->length > nsect){
  168. p->inuse = 0;
  169. free(p->name);
  170. p->name = nil;
  171. }
  172. } else
  173. /* pass through to underlying ctl file, if any */
  174. if (write(ctlfd, msg, r->ifcall.count) != r->ifcall.count) {
  175. respondcmderror(r, cb, "%r");
  176. return;
  177. }
  178. respond(r, nil);
  179. }
  180. void
  181. ctlwrite(Req *r)
  182. {
  183. char *msg;
  184. Cmdbuf *cb;
  185. r->ofcall.count = r->ifcall.count;
  186. msg = emalloc9p(r->ifcall.count+1);
  187. memmove(msg, r->ifcall.data, r->ifcall.count);
  188. msg[r->ifcall.count] = '\0';
  189. cb = parsecmd(r->ifcall.data, r->ifcall.count);
  190. ctlwrite0(r, msg, cb);
  191. free(cb);
  192. free(msg);
  193. }
  194. int
  195. rootgen(int off, Dir *d, void*)
  196. {
  197. memset(d, 0, sizeof *d);
  198. d->atime = time0;
  199. d->mtime = time0;
  200. if(off == 0){
  201. d->name = estrdup9p(sdname);
  202. d->mode = DMDIR|0777;
  203. d->qid.path = Qdir;
  204. d->qid.type = QTDIR;
  205. d->uid = estrdup9p("partfs");
  206. d->gid = estrdup9p("partfs");
  207. d->muid = estrdup9p("");
  208. return 0;
  209. }
  210. return -1;
  211. }
  212. int
  213. dirgen(int off, Dir *d, void*)
  214. {
  215. int n;
  216. Part *p;
  217. memset(d, 0, sizeof *d);
  218. d->atime = time0;
  219. d->mtime = time0;
  220. if(off == 0){
  221. d->name = estrdup9p("ctl");
  222. d->mode = ctlmode;
  223. d->qid.path = Qctl;
  224. goto Have;
  225. }
  226. off--;
  227. n = 0;
  228. for(p = tab; p < tab + nelem(tab); p++, n++){
  229. if(!p->inuse)
  230. continue;
  231. if(n == off){
  232. d->name = estrdup9p(p->name);
  233. d->length = p->length*sectsize;
  234. d->mode = p->mode;
  235. d->qid.path = Qpart + p - tab;
  236. d->qid.vers = p->vers;
  237. goto Have;
  238. }
  239. }
  240. return -1;
  241. Have:
  242. d->uid = estrdup9p("partfs");
  243. d->gid = estrdup9p("partfs");
  244. d->muid = estrdup9p("");
  245. return 0;
  246. }
  247. int
  248. rdwrpart(Req *r)
  249. {
  250. int q;
  251. long count, tot;
  252. vlong offset;
  253. uchar *dat;
  254. Part *p;
  255. q = r->fid->qid.path - Qpart;
  256. if(q < 0 || q > nelem(tab) || !tab[q].inuse ||
  257. tab[q].vers != r->fid->qid.vers){
  258. respond(r, "unknown partition");
  259. return -1;
  260. }
  261. p = &tab[q];
  262. offset = r->ifcall.offset;
  263. count = r->ifcall.count;
  264. if(offset < 0){
  265. respond(r, "negative offset");
  266. return -1;
  267. }
  268. if(count < 0){
  269. respond(r, "negative count");
  270. return -1;
  271. }
  272. if(offset > p->length*sectsize){
  273. respond(r, "offset past end of partition");
  274. return -1;
  275. }
  276. if(offset+count > p->length*sectsize)
  277. count = p->length*sectsize - offset;
  278. offset += p->offset*sectsize;
  279. if(r->ifcall.type == Tread)
  280. dat = (uchar*)r->ofcall.data;
  281. else
  282. dat = (uchar*)r->ifcall.data;
  283. /* pass i/o through to underlying file */
  284. seek(fd, offset, 0);
  285. if (r->ifcall.type == Twrite) {
  286. tot = write(fd, dat, count);
  287. if (tot != count) {
  288. respond(r, "%r");
  289. return -1;
  290. }
  291. } else {
  292. tot = read(fd, dat, count);
  293. if (tot < 0) {
  294. respond(r, "%r");
  295. return -1;
  296. }
  297. }
  298. r->ofcall.count = tot;
  299. respond(r, nil);
  300. return 0;
  301. }
  302. void
  303. fsread(Req *r)
  304. {
  305. char *s;
  306. switch((int)r->fid->qid.path){
  307. case Qroot:
  308. dirread9p(r, rootgen, nil);
  309. break;
  310. case Qdir:
  311. dirread9p(r, dirgen, nil);
  312. break;
  313. case Qctl:
  314. s = ctlstring();
  315. readstr(r, s);
  316. free(s);
  317. break;
  318. default:
  319. rdwrpart(r);
  320. return;
  321. }
  322. respond(r, nil);
  323. }
  324. void
  325. fswrite(Req *r)
  326. {
  327. switch((int)r->fid->qid.path){
  328. case Qroot:
  329. case Qdir:
  330. respond(r, "write to a directory?");
  331. break;
  332. case Qctl:
  333. ctlwrite(r);
  334. break;
  335. default:
  336. rdwrpart(r);
  337. break;
  338. }
  339. }
  340. void
  341. fsopen(Req *r)
  342. {
  343. if(r->ifcall.mode&ORCLOSE)
  344. respond(r, "cannot open ORCLOSE");
  345. switch((int)r->fid->qid.path){
  346. case Qroot:
  347. case Qdir:
  348. if(r->ifcall.mode != OREAD){
  349. respond(r, "bad mode for directory open");
  350. return;
  351. }
  352. }
  353. respond(r, nil);
  354. }
  355. void
  356. fsstat(Req *r)
  357. {
  358. int q;
  359. Dir *d;
  360. Part *p;
  361. d = &r->d;
  362. memset(d, 0, sizeof *d);
  363. d->qid = r->fid->qid;
  364. d->atime = d->mtime = time0;
  365. q = r->fid->qid.path;
  366. switch(q){
  367. case Qroot:
  368. d->name = estrdup9p("/");
  369. d->mode = DMDIR|0777;
  370. break;
  371. case Qdir:
  372. d->name = estrdup9p(sdname);
  373. d->mode = DMDIR|0777;
  374. break;
  375. case Qctl:
  376. d->name = estrdup9p("ctl");
  377. d->mode = 0666;
  378. break;
  379. default:
  380. q -= Qpart;
  381. if(q < 0 || q > nelem(tab) || tab[q].inuse == 0 ||
  382. r->fid->qid.vers != tab[q].vers){
  383. respond(r, "partition no longer exists");
  384. return;
  385. }
  386. p = &tab[q];
  387. d->name = estrdup9p(p->name);
  388. d->length = p->length * sectsize;
  389. d->mode = p->mode;
  390. break;
  391. }
  392. d->uid = estrdup9p("partfs");
  393. d->gid = estrdup9p("partfs");
  394. d->muid = estrdup9p("");
  395. respond(r, nil);
  396. }
  397. void
  398. fsattach(Req *r)
  399. {
  400. char *spec;
  401. spec = r->ifcall.aname;
  402. if(spec && spec[0]){
  403. respond(r, "invalid attach specifier");
  404. return;
  405. }
  406. r->ofcall.qid = (Qid){Qroot, 0, QTDIR};
  407. r->fid->qid = r->ofcall.qid;
  408. respond(r, nil);
  409. }
  410. char*
  411. fswalk1(Fid *fid, char *name, Qid *qid)
  412. {
  413. Part *p;
  414. switch((int)fid->qid.path){
  415. case Qroot:
  416. if(strcmp(name, sdname) == 0){
  417. fid->qid.path = Qdir;
  418. fid->qid.type = QTDIR;
  419. *qid = fid->qid;
  420. return nil;
  421. }
  422. break;
  423. case Qdir:
  424. if(strcmp(name, "ctl") == 0){
  425. fid->qid.path = Qctl;
  426. fid->qid.vers = 0;
  427. fid->qid.type = 0;
  428. *qid = fid->qid;
  429. return nil;
  430. }
  431. for(p = tab; p < tab + nelem(tab); p++)
  432. if(p->inuse && strcmp(p->name, name) == 0){
  433. fid->qid.path = p - tab + Qpart;
  434. fid->qid.vers = p->vers;
  435. fid->qid.type = 0;
  436. *qid = fid->qid;
  437. return nil;
  438. }
  439. break;
  440. }
  441. return "file not found";
  442. }
  443. Srv fs = {
  444. .attach=fsattach,
  445. .open= fsopen,
  446. .read= fsread,
  447. .write= fswrite,
  448. .stat= fsstat,
  449. .walk1= fswalk1,
  450. };
  451. char *mtpt = "/dev";
  452. char *srvname;
  453. void
  454. usage(void)
  455. {
  456. fprint(2, "usage: %s [-Dr] [-d sdname] [-m mtpt] [-p 9parts] "
  457. "[-s srvname] diskimage\n", argv0);
  458. fprint(2, "\tdefault mtpt is /dev\n");
  459. exits("usage");
  460. }
  461. void
  462. main(int argc, char **argv)
  463. {
  464. int isdir;
  465. char *file, *cname, *parts;
  466. Dir *dir;
  467. quotefmtinstall();
  468. time0 = time(0);
  469. parts = nil;
  470. ARGBEGIN{
  471. case 'D':
  472. chatty9p++;
  473. break;
  474. case 'd':
  475. sdname = EARGF(usage());
  476. break;
  477. case 'm':
  478. mtpt = EARGF(usage());
  479. break;
  480. case 'p':
  481. parts = EARGF(usage());
  482. break;
  483. case 'r':
  484. rdonly = 1;
  485. break;
  486. case 's':
  487. srvname = EARGF(usage());
  488. break;
  489. default:
  490. usage();
  491. }ARGEND
  492. if(argc != 1)
  493. usage();
  494. file = argv[0];
  495. dir = dirstat(file);
  496. if(!dir)
  497. sysfatal("%s: %r", file);
  498. isdir = (dir->mode & DMDIR) != 0;
  499. free(dir);
  500. if (isdir) {
  501. cname = smprint("%s/ctl", file);
  502. if ((ctlfd = open(cname, ORDWR)) < 0)
  503. sysfatal("open %s: %r", cname);
  504. file = smprint("%s/data", file);
  505. }
  506. if((fd = open(file, rdonly? OREAD: ORDWR)) < 0)
  507. sysfatal("open %s: %r", file);
  508. sectsize = 512; /* conventional */
  509. dir = dirfstat(fd);
  510. if (dir)
  511. nsect = dir->length / sectsize;
  512. free(dir);
  513. inquiry = estrdup9p(inquiry);
  514. tab[0].inuse = 1;
  515. tab[0].name = estrdup9p("data");
  516. tab[0].mode = 0666;
  517. tab[0].length = nsect;
  518. /*
  519. * hack for booting from usb: add 9load partitions.
  520. */
  521. if(parts != nil)
  522. addparts(parts);
  523. postmountsrv(&fs, srvname, mtpt, MBEFORE);
  524. exits(nil);
  525. }