proto.c 30 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657
  1. #include <u.h>
  2. #include <libc.h>
  3. #include <bio.h>
  4. #include <ip.h>
  5. #include <mp.h>
  6. #include <libsec.h>
  7. #include <auth.h>
  8. #include <fcall.h>
  9. #include <ctype.h>
  10. #include <String.h>
  11. #include "ftpfs.h"
  12. enum
  13. {
  14. /* return codes */
  15. Extra= 1,
  16. Success= 2,
  17. Incomplete= 3,
  18. TempFail= 4,
  19. PermFail= 5,
  20. Impossible= 6,
  21. };
  22. Node *remdir; /* current directory on remote machine */
  23. Node *remroot; /* root directory on remote machine */
  24. int ctlfd; /* fd for control connection */
  25. Biobuf ctlin; /* input buffer for control connection */
  26. Biobuf stdin; /* input buffer for standard input */
  27. Biobuf dbuf; /* buffer for data connection */
  28. char msg[512]; /* buffer for replies */
  29. char net[Maxpath]; /* network for connections */
  30. int listenfd; /* fd to listen on for connections */
  31. char netdir[Maxpath];
  32. int os, defos;
  33. char topsdir[64]; /* name of listed directory for TOPS */
  34. String *remrootpath; /* path on remote side to remote root */
  35. char *user;
  36. int nopassive;
  37. long lastsend;
  38. extern int usetls;
  39. static void sendrequest(char*, char*);
  40. static int getreply(Biobuf*, char*, int, int);
  41. static int active(int, Biobuf**, char*, char*);
  42. static int passive(int, Biobuf**, char*, char*);
  43. static int data(int, Biobuf**, char*, char*);
  44. static int port(void);
  45. static void ascii(void);
  46. static void image(void);
  47. static void unixpath(Node*, String*);
  48. static void vmspath(Node*, String*);
  49. static void mvspath(Node*, String*);
  50. static Node* vmsdir(char*);
  51. static int getpassword(char*, char*);
  52. static int nw_mode(char dirlet, char *s);
  53. /*
  54. * connect to remote server, default network is "tcp/ip"
  55. */
  56. void
  57. hello(char *dest)
  58. {
  59. char *p;
  60. char dir[Maxpath];
  61. TLSconn conn;
  62. Binit(&stdin, 0, OREAD); /* init for later use */
  63. ctlfd = dial(netmkaddr(dest, "tcp", "ftp"), 0, dir, 0);
  64. if(ctlfd < 0){
  65. fprint(2, "can't dial %s: %r\n", dest);
  66. exits("dialing");
  67. }
  68. Binit(&ctlin, ctlfd, OREAD);
  69. /* remember network for the data connections */
  70. p = strrchr(dir+1, '/');
  71. if(p == 0)
  72. fatal("wrong dial(2) linked with ftp");
  73. *p = 0;
  74. safecpy(net, dir, sizeof(net));
  75. /* wait for hello from other side */
  76. if(getreply(&ctlin, msg, sizeof(msg), 1) != Success)
  77. fatal("bad hello");
  78. if(strstr(msg, "Plan 9"))
  79. os = Plan9;
  80. if(usetls){
  81. sendrequest("AUTH", "TLS");
  82. if(getreply(&ctlin, msg, sizeof(msg), 1) != Success)
  83. fatal("bad auth tls");
  84. ctlfd = tlsClient(ctlfd, &conn);
  85. if(ctlfd < 0)
  86. fatal("starting tls: %r");
  87. free(conn.cert);
  88. Binit(&ctlin, ctlfd, OREAD);
  89. sendrequest("PBSZ", "0");
  90. if(getreply(&ctlin, msg, sizeof(msg), 1) != Success)
  91. fatal("bad pbsz 0");
  92. sendrequest("PROT", "P");
  93. if(getreply(&ctlin, msg, sizeof(msg), 1) != Success)
  94. fatal("bad prot p");
  95. }
  96. }
  97. /*
  98. * login to remote system
  99. */
  100. void
  101. rlogin(char *rsys, char *keyspec)
  102. {
  103. char *line;
  104. char pass[128];
  105. UserPasswd *up;
  106. up = nil;
  107. for(;;){
  108. if(up == nil && os != Plan9)
  109. up = auth_getuserpasswd(auth_getkey, "proto=pass server=%s service=ftp %s", rsys, keyspec);
  110. if(up != nil){
  111. sendrequest("USER", up->user);
  112. } else {
  113. print("User[default = %s]: ", user);
  114. line = Brdline(&stdin, '\n');
  115. if(line == 0)
  116. exits(0);
  117. line[Blinelen(&stdin)-1] = 0;
  118. if(*line){
  119. free(user);
  120. user = strdup(line);
  121. }
  122. sendrequest("USER", user);
  123. }
  124. switch(getreply(&ctlin, msg, sizeof(msg), 1)){
  125. case Success:
  126. goto out;
  127. case Incomplete:
  128. break;
  129. case TempFail:
  130. case PermFail:
  131. continue;
  132. }
  133. if(up != nil){
  134. sendrequest("PASS", up->passwd);
  135. } else {
  136. if(getpassword(pass, pass+sizeof(pass)) < 0)
  137. exits(0);
  138. sendrequest("PASS", pass);
  139. }
  140. if(getreply(&ctlin, msg, sizeof(msg), 1) == Success){
  141. if(strstr(msg, "Sess#"))
  142. defos = MVS;
  143. break;
  144. }
  145. }
  146. out:
  147. if(up != nil){
  148. memset(up, 0, sizeof(*up));
  149. free(up);
  150. }
  151. }
  152. /*
  153. * login to remote system with given user name and password.
  154. */
  155. void
  156. clogin(char *cuser, char *cpassword)
  157. {
  158. free(user);
  159. user = strdup(cuser);
  160. if (strcmp(user, "anonymous") != 0 &&
  161. strcmp(user, "ftp") != 0)
  162. fatal("User must be 'anonymous' or 'ftp'");
  163. sendrequest("USER", user);
  164. switch(getreply(&ctlin, msg, sizeof(msg), 1)){
  165. case Success:
  166. return;
  167. case Incomplete:
  168. break;
  169. case TempFail:
  170. case PermFail:
  171. fatal("login failed");
  172. }
  173. if (cpassword == 0)
  174. fatal("password needed");
  175. sendrequest("PASS", cpassword);
  176. if(getreply(&ctlin, msg, sizeof(msg), 1) != Success)
  177. fatal("password failed");
  178. if(strstr(msg, "Sess#"))
  179. defos = MVS;
  180. return;
  181. }
  182. /*
  183. * find out about the other side. go to it's root if requested. set
  184. * image mode if a Plan9 system.
  185. */
  186. void
  187. preamble(char *mountroot)
  188. {
  189. char *p, *ep;
  190. int rv;
  191. OS *o;
  192. /*
  193. * create a root directory mirror
  194. */
  195. remroot = newnode(0, s_copy("/"));
  196. remroot->d->qid.type = QTDIR;
  197. remroot->d->mode = DMDIR|0777;
  198. remdir = remroot;
  199. /*
  200. * get system type
  201. */
  202. sendrequest("SYST", nil);
  203. switch(getreply(&ctlin, msg, sizeof(msg), 1)){
  204. case Success:
  205. for(o = oslist; o->os != Unknown; o++)
  206. if(strncmp(msg+4, o->name, strlen(o->name)) == 0)
  207. break;
  208. os = o->os;
  209. if(os == NT)
  210. os = Unix;
  211. break;
  212. default:
  213. os = defos;
  214. break;
  215. }
  216. if(os == Unknown)
  217. os = defos;
  218. remrootpath = s_reset(remrootpath);
  219. switch(os){
  220. case NetWare:
  221. /*
  222. * Request long, rather than 8.3 filenames,
  223. * where the Servers & Volume support them.
  224. */
  225. sendrequest("SITE LONG", nil);
  226. getreply(&ctlin, msg, sizeof(msg), 0);
  227. /* FALL THRU */
  228. case Unix:
  229. case Plan9:
  230. /*
  231. * go to the remote root, if asked
  232. */
  233. if(mountroot){
  234. sendrequest("CWD", mountroot);
  235. getreply(&ctlin, msg, sizeof(msg), 0);
  236. } else {
  237. s_append(remrootpath, "/usr/");
  238. s_append(remrootpath, user);
  239. }
  240. /*
  241. * get the root directory
  242. */
  243. sendrequest("PWD", nil);
  244. rv = getreply(&ctlin, msg, sizeof(msg), 1);
  245. if(rv == PermFail){
  246. sendrequest("XPWD", nil);
  247. rv = getreply(&ctlin, msg, sizeof(msg), 1);
  248. }
  249. if(rv == Success){
  250. p = strchr(msg, '"');
  251. if(p){
  252. p++;
  253. ep = strchr(p, '"');
  254. if(ep){
  255. *ep = 0;
  256. s_append(s_reset(remrootpath), p);
  257. }
  258. }
  259. }
  260. break;
  261. case Tops:
  262. case VM:
  263. /*
  264. * top directory is a figment of our imagination.
  265. * make it permanently cached & valid.
  266. */
  267. CACHED(remroot);
  268. VALID(remroot);
  269. remroot->d->atime = time(0) + 100000;
  270. /*
  271. * no initial directory. We are in the
  272. * imaginary root.
  273. */
  274. remdir = newtopsdir("???");
  275. topsdir[0] = 0;
  276. if(os == Tops && readdir(remdir) >= 0){
  277. CACHED(remdir);
  278. if(*topsdir)
  279. remdir->remname = s_copy(topsdir);
  280. VALID(remdir);
  281. }
  282. break;
  283. case VMS:
  284. /*
  285. * top directory is a figment of our imagination.
  286. * make it permanently cached & valid.
  287. */
  288. CACHED(remroot);
  289. VALID(remroot);
  290. remroot->d->atime = time(0) + 100000;
  291. /*
  292. * get current directory
  293. */
  294. sendrequest("PWD", nil);
  295. rv = getreply(&ctlin, msg, sizeof(msg), 1);
  296. if(rv == PermFail){
  297. sendrequest("XPWD", nil);
  298. rv = getreply(&ctlin, msg, sizeof(msg), 1);
  299. }
  300. if(rv == Success){
  301. p = strchr(msg, '"');
  302. if(p){
  303. p++;
  304. ep = strchr(p, '"');
  305. if(ep){
  306. *ep = 0;
  307. remroot = remdir = vmsdir(p);
  308. }
  309. }
  310. }
  311. break;
  312. case MVS:
  313. usenlst = 1;
  314. break;
  315. }
  316. if(os == Plan9)
  317. image();
  318. }
  319. static void
  320. ascii(void)
  321. {
  322. sendrequest("TYPE A", nil);
  323. switch(getreply(&ctlin, msg, sizeof(msg), 0)){
  324. case Success:
  325. break;
  326. default:
  327. fatal("can't set type to ascii");
  328. }
  329. }
  330. static void
  331. image(void)
  332. {
  333. sendrequest("TYPE I", nil);
  334. switch(getreply(&ctlin, msg, sizeof(msg), 0)){
  335. case Success:
  336. break;
  337. default:
  338. fatal("can't set type to image/binary");
  339. }
  340. }
  341. /*
  342. * decode the time fields, return seconds since epoch began
  343. */
  344. char *monthchars = "janfebmaraprmayjunjulaugsepoctnovdec";
  345. static Tm now;
  346. static ulong
  347. cracktime(char *month, char *day, char *yr, char *hms)
  348. {
  349. Tm tm;
  350. int i;
  351. char *p;
  352. /* default time */
  353. if(now.year == 0)
  354. now = *localtime(time(0));
  355. tm = now;
  356. tm.yday = 0;
  357. /* convert ascii month to a number twixt 1 and 12 */
  358. if(*month >= '0' && *month <= '9'){
  359. tm.mon = atoi(month) - 1;
  360. if(tm.mon < 0 || tm.mon > 11)
  361. tm.mon = 5;
  362. } else {
  363. for(p = month; *p; p++)
  364. *p = tolower(*p);
  365. for(i = 0; i < 12; i++)
  366. if(strncmp(&monthchars[i*3], month, 3) == 0){
  367. tm.mon = i;
  368. break;
  369. }
  370. }
  371. tm.mday = atoi(day);
  372. if(hms){
  373. tm.hour = strtol(hms, &p, 0);
  374. if(*p == ':'){
  375. tm.min = strtol(p+1, &p, 0);
  376. if(*p == ':')
  377. tm.sec = strtol(p+1, &p, 0);
  378. }
  379. if(tolower(*p) == 'p')
  380. tm.hour += 12;
  381. }
  382. if(yr){
  383. tm.year = atoi(yr);
  384. if(tm.year >= 1900)
  385. tm.year -= 1900;
  386. } else {
  387. if(tm.mon > now.mon || (tm.mon == now.mon && tm.mday > now.mday+1))
  388. tm.year--;
  389. }
  390. /* convert to epoch seconds */
  391. return tm2sec(&tm);
  392. }
  393. /*
  394. * decode a Unix or Plan 9 file mode
  395. */
  396. static ulong
  397. crackmode(char *p)
  398. {
  399. ulong flags;
  400. ulong mode;
  401. int i;
  402. flags = 0;
  403. switch(strlen(p)){
  404. case 10: /* unix and new style plan 9 */
  405. switch(*p){
  406. case 'l':
  407. return DMSYML|0777;
  408. case 'd':
  409. flags |= DMDIR;
  410. case 'a':
  411. flags |= DMAPPEND;
  412. }
  413. p++;
  414. if(p[2] == 'l')
  415. flags |= DMEXCL;
  416. break;
  417. case 11: /* old style plan 9 */
  418. switch(*p++){
  419. case 'd':
  420. flags |= DMDIR;
  421. break;
  422. case 'a':
  423. flags |= DMAPPEND;
  424. break;
  425. }
  426. if(*p++ == 'l')
  427. flags |= DMEXCL;
  428. break;
  429. default:
  430. return DMDIR|0777;
  431. }
  432. mode = 0;
  433. for(i = 0; i < 3; i++){
  434. mode <<= 3;
  435. if(*p++ == 'r')
  436. mode |= DMREAD;
  437. if(*p++ == 'w')
  438. mode |= DMWRITE;
  439. if(*p == 'x' || *p == 's' || *p == 'S')
  440. mode |= DMEXEC;
  441. p++;
  442. }
  443. return mode | flags;
  444. }
  445. /*
  446. * find first punctuation
  447. */
  448. char*
  449. strpunct(char *p)
  450. {
  451. int c;
  452. for(;c = *p; p++){
  453. if(ispunct(c))
  454. return p;
  455. }
  456. return 0;
  457. }
  458. /*
  459. * decode a Unix or Plan 9 directory listing
  460. */
  461. static Dir*
  462. crackdir(char *p, String **remname)
  463. {
  464. char *field[15];
  465. char *dfield[4];
  466. char *cp;
  467. String *s;
  468. int dn, n;
  469. Dir d, *dp;
  470. memset(&d, 0, sizeof(d));
  471. n = getfields(p, field, 15, 1, " \t");
  472. if(n > 2 && strcmp(field[n-2], "->") == 0)
  473. n -= 2;
  474. switch(os){
  475. case TSO:
  476. cp = strchr(field[0], '.');
  477. if(cp){
  478. *cp++ = 0;
  479. s = s_copy(cp);
  480. d.uid = field[0];
  481. } else {
  482. s = s_copy(field[0]);
  483. d.uid = "TSO";
  484. }
  485. d.gid = "TSO";
  486. d.mode = 0666;
  487. d.length = 0;
  488. d.atime = 0;
  489. break;
  490. case OS½:
  491. s = s_copy(field[n-1]);
  492. d.uid = "OS½";
  493. d.gid = d.uid;
  494. d.mode = 0666;
  495. switch(n){
  496. case 5:
  497. if(strcmp(field[1], "DIR") == 0)
  498. d.mode |= DMDIR;
  499. d.length = atoi(field[0]);
  500. dn = getfields(field[2], dfield, 4, 1, "-");
  501. if(dn == 3)
  502. d.atime = cracktime(dfield[0], dfield[1], dfield[2], field[3]);
  503. break;
  504. }
  505. break;
  506. case Tops:
  507. if(n != 4){ /* tops directory name */
  508. safecpy(topsdir, field[0], sizeof(topsdir));
  509. return 0;
  510. }
  511. s = s_copy(field[3]);
  512. d.length = atoi(field[0]);
  513. d.mode = 0666;
  514. d.uid = "Tops";
  515. d.gid = d.uid;
  516. dn = getfields(field[1], dfield, 4, 1, "-");
  517. if(dn == 3)
  518. d.atime = cracktime(dfield[1], dfield[0], dfield[2], field[2]);
  519. else
  520. d.atime = time(0);
  521. break;
  522. case VM:
  523. switch(n){
  524. case 9:
  525. s = s_copy(field[0]);
  526. s_append(s, ".");
  527. s_append(s, field[1]);
  528. d.length = atoi(field[3])*atoi(field[4]);
  529. if(*field[2] == 'F')
  530. d.mode = 0666;
  531. else
  532. d.mode = 0777;
  533. d.uid = "VM";
  534. d.gid = d.uid;
  535. dn = getfields(field[6], dfield, 4, 1, "/-");
  536. if(dn == 3)
  537. d.atime = cracktime(dfield[0], dfield[1], dfield[2], field[7]);
  538. else
  539. d.atime = time(0);
  540. break;
  541. case 1:
  542. s = s_copy(field[0]);
  543. d.uid = "VM";
  544. d.gid = d.uid;
  545. d.mode = 0777;
  546. d.atime = 0;
  547. break;
  548. default:
  549. return nil;
  550. }
  551. break;
  552. case VMS:
  553. switch(n){
  554. case 6:
  555. for(cp = field[0]; *cp; cp++)
  556. *cp = tolower(*cp);
  557. cp = strchr(field[0], ';');
  558. if(cp)
  559. *cp = 0;
  560. d.mode = 0666;
  561. cp = field[0] + strlen(field[0]) - 4;
  562. if(strcmp(cp, ".dir") == 0){
  563. d.mode |= DMDIR;
  564. *cp = 0;
  565. }
  566. s = s_copy(field[0]);
  567. d.length = atoi(field[1])*512;
  568. field[4][strlen(field[4])-1] = 0;
  569. d.uid = field[4]+1;
  570. d.gid = d.uid;
  571. dn = getfields(field[2], dfield, 4, 1, "/-");
  572. if(dn == 3)
  573. d.atime = cracktime(dfield[1], dfield[0], dfield[2], field[3]);
  574. else
  575. d.atime = time(0);
  576. break;
  577. default:
  578. return nil;
  579. }
  580. break;
  581. case NetWare:
  582. switch(n){
  583. case 8: /* New style */
  584. s = s_copy(field[7]);
  585. d.uid = field[2];
  586. d.gid = d.uid;
  587. d.mode = nw_mode(field[0][0], field[1]);
  588. d.length = atoi(field[3]);
  589. if(strchr(field[6], ':'))
  590. d.atime = cracktime(field[4], field[5], nil, field[6]);
  591. else
  592. d.atime = cracktime(field[4], field[5], field[6], nil);
  593. break;
  594. case 9:
  595. s = s_copy(field[8]);
  596. d.uid = field[2];
  597. d.gid = d.uid;
  598. d.mode = 0666;
  599. if(*field[0] == 'd')
  600. d.mode |= DMDIR;
  601. d.length = atoi(field[3]);
  602. d.atime = cracktime(field[4], field[5], field[6], field[7]);
  603. break;
  604. case 1:
  605. s = s_copy(field[0]);
  606. d.uid = "none";
  607. d.gid = d.uid;
  608. d.mode = 0777;
  609. d.atime = 0;
  610. break;
  611. default:
  612. return nil;
  613. }
  614. break;
  615. case Unix:
  616. case Plan9:
  617. default:
  618. switch(n){
  619. case 8: /* ls -l */
  620. s = s_copy(field[7]);
  621. d.uid = field[2];
  622. d.gid = d.uid;
  623. d.mode = crackmode(field[0]);
  624. d.length = atoi(field[3]);
  625. if(strchr(field[6], ':'))
  626. d.atime = cracktime(field[4], field[5], 0, field[6]);
  627. else
  628. d.atime = cracktime(field[4], field[5], field[6], 0);
  629. break;
  630. case 9: /* ls -lg */
  631. s = s_copy(field[8]);
  632. d.uid = field[2];
  633. d.gid = field[3];
  634. d.mode = crackmode(field[0]);
  635. d.length = atoi(field[4]);
  636. if(strchr(field[7], ':'))
  637. d.atime = cracktime(field[5], field[6], 0, field[7]);
  638. else
  639. d.atime = cracktime(field[5], field[6], field[7], 0);
  640. break;
  641. case 10: /* plan 9 */
  642. s = s_copy(field[9]);
  643. d.uid = field[3];
  644. d.gid = field[4];
  645. d.mode = crackmode(field[0]);
  646. d.length = atoi(field[5]);
  647. if(strchr(field[8], ':'))
  648. d.atime = cracktime(field[6], field[7], 0, field[8]);
  649. else
  650. d.atime = cracktime(field[6], field[7], field[8], 0);
  651. break;
  652. case 4: /* a Windows_NT version */
  653. s = s_copy(field[3]);
  654. d.uid = "NT";
  655. d.gid = d.uid;
  656. if(strcmp("<DIR>", field[2]) == 0){
  657. d.length = 0;
  658. d.mode = DMDIR|0777;
  659. } else {
  660. d.mode = 0666;
  661. d.length = atoi(field[2]);
  662. }
  663. dn = getfields(field[0], dfield, 4, 1, "/-");
  664. if(dn == 3)
  665. d.atime = cracktime(dfield[0], dfield[1], dfield[2], field[1]);
  666. break;
  667. case 1:
  668. s = s_copy(field[0]);
  669. d.uid = "none";
  670. d.gid = d.uid;
  671. d.mode = 0777;
  672. d.atime = 0;
  673. break;
  674. default:
  675. return nil;
  676. }
  677. }
  678. d.muid = d.uid;
  679. d.qid.type = (d.mode & DMDIR) ? QTDIR : QTFILE;
  680. d.mtime = d.atime;
  681. if(ext && (d.qid.type & QTDIR) == 0)
  682. s_append(s, ext);
  683. d.name = s_to_c(s);
  684. /* allocate a freeable dir structure */
  685. dp = reallocdir(&d, 0);
  686. *remname = s;
  687. return dp;
  688. }
  689. /*
  690. * probe files in a directory to see if they are directories
  691. */
  692. /*
  693. * read a remote directory
  694. */
  695. int
  696. readdir(Node *node)
  697. {
  698. Biobuf *bp;
  699. char *line;
  700. Node *np;
  701. Dir *d;
  702. long n;
  703. int tries, x, files;
  704. static int uselist;
  705. int usenlist;
  706. String *remname;
  707. if(changedir(node) < 0)
  708. return -1;
  709. usenlist = 0;
  710. for(tries = 0; tries < 3; tries++){
  711. if(usenlist || usenlst)
  712. x = data(OREAD, &bp, "NLST", nil);
  713. else if(os == Unix && !uselist)
  714. x = data(OREAD, &bp, "LIST -l", nil);
  715. else
  716. x = data(OREAD, &bp, "LIST", nil);
  717. switch(x){
  718. case Extra:
  719. break;
  720. /* case TempFail:
  721. continue;
  722. */
  723. default:
  724. if(os == Unix && uselist == 0){
  725. uselist = 1;
  726. continue;
  727. }
  728. return seterr(nosuchfile);
  729. }
  730. files = 0;
  731. while(line = Brdline(bp, '\n')){
  732. n = Blinelen(bp);
  733. if(debug)
  734. write(2, line, n);
  735. if(n > 1 && line[n-2] == '\r')
  736. n--;
  737. line[n - 1] = 0;
  738. d = crackdir(line, &remname);
  739. if(d == nil)
  740. continue;
  741. files++;
  742. np = extendpath(node, remname);
  743. d->qid.path = np->d->qid.path;
  744. d->qid.vers = np->d->qid.vers;
  745. d->type = np->d->type;
  746. d->dev = 1; /* mark node as valid */
  747. if(os == MVS && node == remroot){
  748. d->qid.type = QTDIR;
  749. d->mode |= DMDIR;
  750. }
  751. free(np->d);
  752. np->d = d;
  753. }
  754. close(Bfildes(bp));
  755. switch(getreply(&ctlin, msg, sizeof(msg), 0)){
  756. case Success:
  757. if(files == 0 && !usenlst && !usenlist){
  758. usenlist = 1;
  759. continue;
  760. }
  761. if(files && usenlist)
  762. usenlst = 1;
  763. if(usenlst)
  764. node->chdirunknown = 1;
  765. return 0;
  766. case TempFail:
  767. break;
  768. default:
  769. return seterr(nosuchfile);
  770. }
  771. }
  772. return seterr(nosuchfile);
  773. }
  774. /*
  775. * create a remote directory
  776. */
  777. int
  778. createdir(Node *node)
  779. {
  780. if(changedir(node->parent) < 0)
  781. return -1;
  782. sendrequest("MKD", node->d->name);
  783. if(getreply(&ctlin, msg, sizeof(msg), 0) != Success)
  784. return -1;
  785. return 0;
  786. }
  787. /*
  788. * change to a remote directory.
  789. */
  790. int
  791. changedir(Node *node)
  792. {
  793. Node *to;
  794. String *cdpath;
  795. to = node;
  796. if(to == remdir)
  797. return 0;
  798. /* build an absolute path */
  799. switch(os){
  800. case Tops:
  801. case VM:
  802. switch(node->depth){
  803. case 0:
  804. remdir = node;
  805. return 0;
  806. case 1:
  807. cdpath = s_clone(node->remname);
  808. break;
  809. default:
  810. return seterr(nosuchfile);
  811. }
  812. break;
  813. case VMS:
  814. switch(node->depth){
  815. case 0:
  816. remdir = node;
  817. return 0;
  818. default:
  819. cdpath = s_new();
  820. vmspath(node, cdpath);
  821. }
  822. break;
  823. case MVS:
  824. if(node->depth == 0)
  825. cdpath = s_clone(remrootpath);
  826. else{
  827. cdpath = s_new();
  828. mvspath(node, cdpath);
  829. }
  830. break;
  831. default:
  832. if(node->depth == 0)
  833. cdpath = s_clone(remrootpath);
  834. else{
  835. cdpath = s_new();
  836. unixpath(node, cdpath);
  837. }
  838. break;
  839. }
  840. uncachedir(remdir, 0);
  841. /*
  842. * connect, if we need a password (Incomplete)
  843. * act like it worked (best we can do).
  844. */
  845. sendrequest("CWD", s_to_c(cdpath));
  846. s_free(cdpath);
  847. switch(getreply(&ctlin, msg, sizeof(msg), 0)){
  848. case Success:
  849. case Incomplete:
  850. remdir = node;
  851. return 0;
  852. default:
  853. return seterr(nosuchfile);
  854. }
  855. }
  856. /*
  857. * read a remote file
  858. */
  859. int
  860. readfile1(Node *node)
  861. {
  862. Biobuf *bp;
  863. char buf[4*1024];
  864. long off;
  865. int n;
  866. int tries;
  867. if(changedir(node->parent) < 0)
  868. return -1;
  869. for(tries = 0; tries < 4; tries++){
  870. switch(data(OREAD, &bp, "RETR", s_to_c(node->remname))){
  871. case Extra:
  872. break;
  873. case TempFail:
  874. continue;
  875. default:
  876. return seterr(nosuchfile);
  877. }
  878. off = 0;
  879. while((n = read(Bfildes(bp), buf, sizeof buf)) > 0){
  880. if(filewrite(node, buf, off, n) != n){
  881. off = -1;
  882. break;
  883. }
  884. off += n;
  885. }
  886. if(off < 0)
  887. return -1;
  888. /* make sure a file gets created even for a zero length file */
  889. if(off == 0)
  890. filewrite(node, buf, 0, 0);
  891. close(Bfildes(bp));
  892. switch(getreply(&ctlin, msg, sizeof(msg), 0)){
  893. case Success:
  894. return off;
  895. case TempFail:
  896. continue;
  897. default:
  898. return seterr(nosuchfile);
  899. }
  900. }
  901. return seterr(nosuchfile);
  902. }
  903. int
  904. readfile(Node *node)
  905. {
  906. int rv, inimage;
  907. switch(os){
  908. case MVS:
  909. case Plan9:
  910. case Tops:
  911. case TSO:
  912. inimage = 0;
  913. break;
  914. default:
  915. inimage = 1;
  916. image();
  917. break;
  918. }
  919. rv = readfile1(node);
  920. if(inimage)
  921. ascii();
  922. return rv;
  923. }
  924. /*
  925. * write back a file
  926. */
  927. int
  928. createfile1(Node *node)
  929. {
  930. Biobuf *bp;
  931. char buf[4*1024];
  932. long off;
  933. int n;
  934. if(changedir(node->parent) < 0)
  935. return -1;
  936. if(data(OWRITE, &bp, "STOR", s_to_c(node->remname)) != Extra)
  937. return -1;
  938. for(off = 0; ; off += n){
  939. n = fileread(node, buf, off, sizeof(buf));
  940. if(n <= 0)
  941. break;
  942. write(Bfildes(bp), buf, n);
  943. }
  944. close(Bfildes(bp));
  945. if(getreply(&ctlin, msg, sizeof(msg), 0) != Success)
  946. return -1;
  947. return off;
  948. }
  949. int
  950. createfile(Node *node)
  951. {
  952. int rv;
  953. switch(os){
  954. case Plan9:
  955. case Tops:
  956. break;
  957. default:
  958. image();
  959. break;
  960. }
  961. rv = createfile1(node);
  962. switch(os){
  963. case Plan9:
  964. case Tops:
  965. break;
  966. default:
  967. ascii();
  968. break;
  969. }
  970. return rv;
  971. }
  972. /*
  973. * remove a remote file
  974. */
  975. int
  976. removefile(Node *node)
  977. {
  978. if(changedir(node->parent) < 0)
  979. return -1;
  980. sendrequest("DELE", s_to_c(node->remname));
  981. if(getreply(&ctlin, msg, sizeof(msg), 0) != Success)
  982. return -1;
  983. return 0;
  984. }
  985. /*
  986. * remove a remote directory
  987. */
  988. int
  989. removedir(Node *node)
  990. {
  991. if(changedir(node->parent) < 0)
  992. return -1;
  993. sendrequest("RMD", s_to_c(node->remname));
  994. if(getreply(&ctlin, msg, sizeof(msg), 0) != Success)
  995. return -1;
  996. return 0;
  997. }
  998. /*
  999. * tell remote that we're exiting and then do it
  1000. */
  1001. void
  1002. quit(void)
  1003. {
  1004. sendrequest("QUIT", nil);
  1005. getreply(&ctlin, msg, sizeof(msg), 0);
  1006. exits(0);
  1007. }
  1008. /*
  1009. * send a request
  1010. */
  1011. static void
  1012. sendrequest(char *a, char *b)
  1013. {
  1014. char buf[2*1024];
  1015. int n;
  1016. n = strlen(a)+2+1;
  1017. if(b != nil)
  1018. n += strlen(b)+1;
  1019. if(n >= sizeof(buf))
  1020. fatal("proto request too long");
  1021. strcpy(buf, a);
  1022. if(b != nil){
  1023. strcat(buf, " ");
  1024. strcat(buf, b);
  1025. }
  1026. strcat(buf, "\r\n");
  1027. n = strlen(buf);
  1028. if(write(ctlfd, buf, n) != n)
  1029. fatal("remote side hung up");
  1030. if(debug)
  1031. write(2, buf, n);
  1032. lastsend = time(0);
  1033. }
  1034. /*
  1035. * replies codes are in the range [100, 999] and may contain multiple lines of
  1036. * continuation.
  1037. */
  1038. static int
  1039. getreply(Biobuf *bp, char *msg, int len, int printreply)
  1040. {
  1041. char *line;
  1042. char *p;
  1043. int rv;
  1044. int i, n;
  1045. while(line = Brdline(bp, '\n')){
  1046. /* add line to message buffer, strip off \r */
  1047. n = Blinelen(bp);
  1048. if(n > 1 && line[n-2] == '\r'){
  1049. n--;
  1050. line[n-1] = '\n';
  1051. }
  1052. if(printreply && !quiet)
  1053. write(1, line, n);
  1054. else if(debug)
  1055. write(2, line, n);
  1056. if(n > len - 1)
  1057. i = len - 1;
  1058. else
  1059. i = n;
  1060. if(i > 0){
  1061. memmove(msg, line, i);
  1062. msg += i;
  1063. len -= i;
  1064. *msg = 0;
  1065. }
  1066. /* stop if not a continuation */
  1067. rv = strtol(line, &p, 10);
  1068. if(rv >= 100 && rv < 600 && p==line+3 && *p != '-')
  1069. return rv/100;
  1070. /* tell user about continuations */
  1071. if(!debug && !quiet && !printreply)
  1072. write(2, line, n);
  1073. }
  1074. fatal("remote side closed connection");
  1075. return 0;
  1076. }
  1077. /*
  1078. * Announce on a local port and tell its address to the the remote side
  1079. */
  1080. static int
  1081. port(void)
  1082. {
  1083. char buf[256];
  1084. int n, fd;
  1085. char *ptr;
  1086. uchar ipaddr[IPaddrlen];
  1087. int port;
  1088. /* get a channel to listen on, let kernel pick the port number */
  1089. sprint(buf, "%s!*!0", net);
  1090. listenfd = announce(buf, netdir);
  1091. if(listenfd < 0)
  1092. return seterr("can't announce");
  1093. /* get the local address and port number */
  1094. sprint(buf, "%s/local", netdir);
  1095. fd = open(buf, OREAD);
  1096. if(fd < 0)
  1097. return seterr("opening %s: %r", buf);
  1098. n = read(fd, buf, sizeof(buf)-1);
  1099. close(fd);
  1100. if(n <= 0)
  1101. return seterr("opening %s/local: %r", netdir);
  1102. buf[n] = 0;
  1103. ptr = strchr(buf, ' ');
  1104. if(ptr)
  1105. *ptr = 0;
  1106. ptr = strchr(buf, '!')+1;
  1107. port = atoi(ptr);
  1108. memset(ipaddr, 0, IPaddrlen);
  1109. if (*net){
  1110. strcpy(buf, net);
  1111. ptr = strchr(buf +1, '/');
  1112. if (ptr)
  1113. *ptr = 0;
  1114. myipaddr(ipaddr, buf);
  1115. }
  1116. /* tell remote side */
  1117. sprint(buf, "PORT %d,%d,%d,%d,%d,%d", ipaddr[IPv4off+0], ipaddr[IPv4off+1],
  1118. ipaddr[IPv4off+2], ipaddr[IPv4off+3], port>>8, port&0xff);
  1119. sendrequest(buf, nil);
  1120. if(getreply(&ctlin, msg, sizeof(msg), 0) != Success)
  1121. return seterr(msg);
  1122. return 0;
  1123. }
  1124. /*
  1125. * have server call back for a data connection
  1126. */
  1127. static int
  1128. active(int mode, Biobuf **bpp, char *cmda, char *cmdb)
  1129. {
  1130. int cfd, dfd, rv;
  1131. char newdir[Maxpath];
  1132. char datafile[Maxpath + 6];
  1133. TLSconn conn;
  1134. if(port() < 0)
  1135. return TempFail;
  1136. sendrequest(cmda, cmdb);
  1137. rv = getreply(&ctlin, msg, sizeof(msg), 0);
  1138. if(rv != Extra){
  1139. close(listenfd);
  1140. return rv;
  1141. }
  1142. /* wait for a new call */
  1143. cfd = listen(netdir, newdir);
  1144. if(cfd < 0)
  1145. fatal("waiting for data connection");
  1146. close(listenfd);
  1147. /* open it's data connection and close the control connection */
  1148. sprint(datafile, "%s/data", newdir);
  1149. dfd = open(datafile, ORDWR);
  1150. close(cfd);
  1151. if(dfd < 0)
  1152. fatal("opening data connection");
  1153. if(usetls){
  1154. memset(&conn, 0, sizeof(conn));
  1155. dfd = tlsClient(dfd, &conn);
  1156. if(dfd < 0)
  1157. fatal("starting tls: %r");
  1158. free(conn.cert);
  1159. }
  1160. Binit(&dbuf, dfd, mode);
  1161. *bpp = &dbuf;
  1162. return Extra;
  1163. }
  1164. /*
  1165. * call out for a data connection
  1166. */
  1167. static int
  1168. passive(int mode, Biobuf **bpp, char *cmda, char *cmdb)
  1169. {
  1170. char msg[1024];
  1171. char ds[1024];
  1172. char *f[6];
  1173. char *p;
  1174. int x, fd;
  1175. TLSconn conn;
  1176. if(nopassive)
  1177. return Impossible;
  1178. sendrequest("PASV", nil);
  1179. if(getreply(&ctlin, msg, sizeof(msg), 0) != Success){
  1180. nopassive = 1;
  1181. return Impossible;
  1182. }
  1183. /* get address and port number from reply, this is AI */
  1184. p = strchr(msg, '(');
  1185. if(p == 0){
  1186. for(p = msg+3; *p; p++)
  1187. if(isdigit(*p))
  1188. break;
  1189. } else
  1190. p++;
  1191. if(getfields(p, f, 6, 0, ",") < 6){
  1192. if(debug)
  1193. fprint(2, "passive mode protocol botch: %s\n", msg);
  1194. werrstr("ftp protocol botch");
  1195. nopassive = 1;
  1196. return Impossible;
  1197. }
  1198. snprint(ds, sizeof(ds), "%s!%s.%s.%s.%s!%d", net,
  1199. f[0], f[1], f[2], f[3],
  1200. ((atoi(f[4])&0xff)<<8) + (atoi(f[5])&0xff));
  1201. /* open data connection */
  1202. fd = dial(ds, 0, 0, 0);
  1203. if(fd < 0){
  1204. if(debug)
  1205. fprint(2, "passive mode connect to %s failed: %r\n", ds);
  1206. nopassive = 1;
  1207. return TempFail;
  1208. }
  1209. /* tell remote to send a file */
  1210. sendrequest(cmda, cmdb);
  1211. x = getreply(&ctlin, msg, sizeof(msg), 0);
  1212. if(x != Extra){
  1213. close(fd);
  1214. if(debug)
  1215. fprint(2, "passive mode retrieve failed: %s\n", msg);
  1216. werrstr(msg);
  1217. return x;
  1218. }
  1219. if(usetls){
  1220. memset(&conn, 0, sizeof(conn));
  1221. fd = tlsClient(fd, &conn);
  1222. if(fd < 0)
  1223. fatal("starting tls: %r");
  1224. free(conn.cert);
  1225. }
  1226. Binit(&dbuf, fd, mode);
  1227. *bpp = &dbuf;
  1228. return Extra;
  1229. }
  1230. static int
  1231. data(int mode, Biobuf **bpp, char* cmda, char *cmdb)
  1232. {
  1233. int x;
  1234. x = passive(mode, bpp, cmda, cmdb);
  1235. if(x != Impossible)
  1236. return x;
  1237. return active(mode, bpp, cmda, cmdb);
  1238. }
  1239. /*
  1240. * used for keep alives
  1241. */
  1242. void
  1243. nop(void)
  1244. {
  1245. if(lastsend - time(0) < 15)
  1246. return;
  1247. sendrequest("PWD", nil);
  1248. getreply(&ctlin, msg, sizeof(msg), 0);
  1249. }
  1250. /*
  1251. * turn a vms spec into a path
  1252. */
  1253. static Node*
  1254. vmsextendpath(Node *np, char *name)
  1255. {
  1256. np = extendpath(np, s_copy(name));
  1257. if(!ISVALID(np)){
  1258. np->d->qid.type = QTDIR;
  1259. np->d->atime = time(0);
  1260. np->d->mtime = np->d->atime;
  1261. strcpy(np->d->uid, "who");
  1262. strcpy(np->d->gid, "cares");
  1263. np->d->mode = DMDIR|0777;
  1264. np->d->length = 0;
  1265. if(changedir(np) >= 0)
  1266. VALID(np);
  1267. }
  1268. return np;
  1269. }
  1270. static Node*
  1271. vmsdir(char *name)
  1272. {
  1273. char *cp;
  1274. Node *np;
  1275. char *oname;
  1276. np = remroot;
  1277. cp = strchr(name, '[');
  1278. if(cp)
  1279. strcpy(cp, cp+1);
  1280. cp = strchr(name, ']');
  1281. if(cp)
  1282. *cp = 0;
  1283. oname = name = strdup(name);
  1284. if(name == 0)
  1285. return 0;
  1286. while(cp = strchr(name, '.')){
  1287. *cp = 0;
  1288. np = vmsextendpath(np, name);
  1289. name = cp+1;
  1290. }
  1291. np = vmsextendpath(np, name);
  1292. /*
  1293. * walk back to first accessible directory
  1294. */
  1295. for(; np->parent != np; np = np->parent)
  1296. if(ISVALID(np)){
  1297. CACHED(np->parent);
  1298. break;
  1299. }
  1300. free(oname);
  1301. return np;
  1302. }
  1303. /*
  1304. * walk up the tree building a VMS style path
  1305. */
  1306. static void
  1307. vmspath(Node *node, String *path)
  1308. {
  1309. char *p;
  1310. int n;
  1311. if(node->depth == 1){
  1312. p = strchr(s_to_c(node->remname), ':');
  1313. if(p){
  1314. n = p - s_to_c(node->remname) + 1;
  1315. s_nappend(path, s_to_c(node->remname), n);
  1316. s_append(path, "[");
  1317. s_append(path, p+1);
  1318. } else {
  1319. s_append(path, "[");
  1320. s_append(path, s_to_c(node->remname));
  1321. }
  1322. s_append(path, "]");
  1323. return;
  1324. }
  1325. vmspath(node->parent, path);
  1326. s_append(path, ".");
  1327. s_append(path, s_to_c(node->remname));
  1328. }
  1329. /*
  1330. * walk up the tree building a Unix style path
  1331. */
  1332. static void
  1333. unixpath(Node *node, String *path)
  1334. {
  1335. if(node == node->parent){
  1336. s_append(path, s_to_c(remrootpath));
  1337. return;
  1338. }
  1339. unixpath(node->parent, path);
  1340. if(s_len(path) > 0 && strcmp(s_to_c(path), "/") != 0)
  1341. s_append(path, "/");
  1342. s_append(path, s_to_c(node->remname));
  1343. }
  1344. /*
  1345. * walk up the tree building a MVS style path
  1346. */
  1347. static void
  1348. mvspath(Node *node, String *path)
  1349. {
  1350. if(node == node->parent){
  1351. s_append(path, s_to_c(remrootpath));
  1352. return;
  1353. }
  1354. mvspath(node->parent, path);
  1355. if(s_len(path) > 0)
  1356. s_append(path, ".");
  1357. s_append(path, s_to_c(node->remname));
  1358. }
  1359. static int
  1360. getpassword(char *buf, char *e)
  1361. {
  1362. char *p;
  1363. int c;
  1364. int consctl, rv = 0;
  1365. consctl = open("/dev/consctl", OWRITE);
  1366. if(consctl >= 0)
  1367. write(consctl, "rawon", 5);
  1368. print("Password: ");
  1369. e--;
  1370. for(p = buf; p <= e; p++){
  1371. c = Bgetc(&stdin);
  1372. if(c < 0){
  1373. rv = -1;
  1374. goto out;
  1375. }
  1376. if(c == '\n' || c == '\r')
  1377. break;
  1378. *p = c;
  1379. }
  1380. *p = 0;
  1381. print("\n");
  1382. out:
  1383. if(consctl >= 0)
  1384. close(consctl);
  1385. return rv;
  1386. }
  1387. /*
  1388. * convert from latin1 to utf
  1389. */
  1390. static char*
  1391. fromlatin1(char *from)
  1392. {
  1393. char *p, *to;
  1394. Rune r;
  1395. if(os == Plan9)
  1396. return nil;
  1397. /* don't convert if we don't have to */
  1398. for(p = from; *p; p++)
  1399. if(*p & 0x80)
  1400. break;
  1401. if(*p == 0)
  1402. return nil;
  1403. to = malloc(3*strlen(from)+2);
  1404. if(to == nil)
  1405. return nil;
  1406. for(p = to; *from; from++){
  1407. r = (*from) & 0xff;
  1408. p += runetochar(p, &r);
  1409. }
  1410. *p = 0;
  1411. return to;
  1412. }
  1413. Dir*
  1414. reallocdir(Dir *d, int dofree)
  1415. {
  1416. Dir *dp;
  1417. char *p;
  1418. int nn, ng, nu, nm;
  1419. char *utf;
  1420. if(d->name == nil)
  1421. d->name = "?name?";
  1422. if(d->uid == nil)
  1423. d->uid = "?uid?";
  1424. if(d->gid == nil)
  1425. d->gid = d->uid;
  1426. if(d->muid == nil)
  1427. d->muid = d->uid;
  1428. utf = fromlatin1(d->name);
  1429. if(utf != nil)
  1430. d->name = utf;
  1431. nn = strlen(d->name)+1;
  1432. nu = strlen(d->uid)+1;
  1433. ng = strlen(d->gid)+1;
  1434. nm = strlen(d->muid)+1;
  1435. dp = malloc(sizeof(Dir)+nn+nu+ng+nm);
  1436. if(dp == nil){
  1437. if(dofree)
  1438. free(d);
  1439. if(utf != nil)
  1440. free(utf);
  1441. return nil;
  1442. }
  1443. *dp = *d;
  1444. p = (char*)&dp[1];
  1445. strcpy(p, d->name);
  1446. dp->name = p;
  1447. p += nn;
  1448. strcpy(p, d->uid);
  1449. dp->uid = p;
  1450. p += nu;
  1451. strcpy(p, d->gid);
  1452. dp->gid = p;
  1453. p += ng;
  1454. strcpy(p, d->muid);
  1455. dp->muid = p;
  1456. if(dofree)
  1457. free(d);
  1458. if(utf != nil)
  1459. free(utf);
  1460. return dp;
  1461. }
  1462. Dir*
  1463. dir_change_name(Dir *d, char *name)
  1464. {
  1465. if(d->name && strlen(d->name) >= strlen(name)){
  1466. strcpy(d->name, name);
  1467. return d;
  1468. }
  1469. d->name = name;
  1470. return reallocdir(d, 1);
  1471. }
  1472. Dir*
  1473. dir_change_uid(Dir *d, char *name)
  1474. {
  1475. if(d->uid && strlen(d->uid) >= strlen(name)){
  1476. strcpy(d->name, name);
  1477. return d;
  1478. }
  1479. d->uid = name;
  1480. return reallocdir(d, 1);
  1481. }
  1482. Dir*
  1483. dir_change_gid(Dir *d, char *name)
  1484. {
  1485. if(d->gid && strlen(d->gid) >= strlen(name)){
  1486. strcpy(d->name, name);
  1487. return d;
  1488. }
  1489. d->gid = name;
  1490. return reallocdir(d, 1);
  1491. }
  1492. Dir*
  1493. dir_change_muid(Dir *d, char *name)
  1494. {
  1495. if(d->muid && strlen(d->muid) >= strlen(name)){
  1496. strcpy(d->name, name);
  1497. return d;
  1498. }
  1499. d->muid = name;
  1500. return reallocdir(d, 1);
  1501. }
  1502. static int
  1503. nw_mode(char dirlet, char *s) /* NetWare file mode mapping */
  1504. {
  1505. int mode = 0777;
  1506. if(dirlet == 'd')
  1507. mode |= DMDIR;
  1508. if (strlen(s) >= 10 && s[0] != '[' || s[9] != ']')
  1509. return(mode);
  1510. if (s[1] == '-') /* can't read file */
  1511. mode &= ~0444;
  1512. if (dirlet == 'd' && s[6] == '-') /* cannot scan dir */
  1513. mode &= ~0444;
  1514. if (s[2] == '-') /* can't write file */
  1515. mode &= ~0222;
  1516. if (dirlet == 'd' && s[7] == '-' && s[3] == '-') /* cannot create in, or modify dir */
  1517. mode &= ~0222;
  1518. return(mode);
  1519. }