utilunix.c 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984
  1. /*
  2. Various utilities - Unix variants
  3. Copyright (C) 1994, 1995, 1996, 1998, 1999, 2000, 2001, 2002, 2003,
  4. 2004, 2005, 2007, 2011
  5. The Free Software Foundation, Inc.
  6. Written by:
  7. Miguel de Icaza, 1994, 1995, 1996
  8. Janne Kukonlehto, 1994, 1995, 1996
  9. Dugan Porter, 1994, 1995, 1996
  10. Jakub Jelinek, 1994, 1995, 1996
  11. Mauricio Plaza, 1994, 1995, 1996
  12. The mc_realpath routine is mostly from uClibc package, written
  13. by Rick Sladkey <jrs@world.std.com>
  14. This file is part of the Midnight Commander.
  15. The Midnight Commander is free software: you can redistribute it
  16. and/or modify it under the terms of the GNU General Public License as
  17. published by the Free Software Foundation, either version 3 of the License,
  18. or (at your option) any later version.
  19. The Midnight Commander is distributed in the hope that it will be useful,
  20. but WITHOUT ANY WARRANTY; without even the implied warranty of
  21. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  22. GNU General Public License for more details.
  23. You should have received a copy of the GNU General Public License
  24. along with this program. If not, see <http://www.gnu.org/licenses/>.
  25. */
  26. /** \file utilunix.c
  27. * \brief Source: various utilities - Unix variant
  28. */
  29. #include <config.h>
  30. #include <ctype.h>
  31. #include <errno.h>
  32. #include <limits.h>
  33. #include <signal.h>
  34. #include <stdarg.h>
  35. #include <stdio.h>
  36. #include <stdlib.h>
  37. #include <string.h>
  38. #include <fcntl.h>
  39. #ifdef HAVE_SYS_PARAM_H
  40. #include <sys/param.h>
  41. #endif
  42. #include <sys/types.h>
  43. #include <sys/stat.h>
  44. #include <sys/wait.h>
  45. #ifdef HAVE_SYS_IOCTL_H
  46. #include <sys/ioctl.h>
  47. #endif
  48. #ifdef HAVE_GET_PROCESS_STATS
  49. #include <sys/procstats.h>
  50. #endif
  51. #include <unistd.h>
  52. #include <pwd.h>
  53. #include <grp.h>
  54. #include "lib/global.h"
  55. #include "lib/vfs/vfs.h" /* VFS_ENCODING_PREFIX */
  56. #include "lib/strutil.h" /* str_move() */
  57. #include "lib/util.h"
  58. #include "lib/widget.h" /* message() */
  59. #include "lib/vfs/xdirentry.h"
  60. #ifdef HAVE_CHARSET
  61. #include "lib/charsets.h"
  62. #endif
  63. #include "utilunix.h"
  64. /*** global variables ****************************************************************************/
  65. struct sigaction startup_handler;
  66. /*** file scope macro definitions ****************************************************************/
  67. #define UID_CACHE_SIZE 200
  68. #define GID_CACHE_SIZE 30
  69. /* Pipes are guaranteed to be able to hold at least 4096 bytes */
  70. /* More than that would be unportable */
  71. #define MAX_PIPE_SIZE 4096
  72. /*** file scope type declarations ****************************************************************/
  73. typedef struct
  74. {
  75. int index;
  76. char *string;
  77. } int_cache;
  78. /*** file scope variables ************************************************************************/
  79. static int_cache uid_cache[UID_CACHE_SIZE];
  80. static int_cache gid_cache[GID_CACHE_SIZE];
  81. static int error_pipe[2]; /* File descriptors of error pipe */
  82. static int old_error; /* File descriptor of old standard error */
  83. /*** file scope functions ************************************************************************/
  84. /* --------------------------------------------------------------------------------------------- */
  85. static char *
  86. i_cache_match (int id, int_cache * cache, int size)
  87. {
  88. int i;
  89. for (i = 0; i < size; i++)
  90. if (cache[i].index == id)
  91. return cache[i].string;
  92. return 0;
  93. }
  94. /* --------------------------------------------------------------------------------------------- */
  95. static void
  96. i_cache_add (int id, int_cache * cache, int size, char *text, int *last)
  97. {
  98. g_free (cache[*last].string);
  99. cache[*last].string = g_strdup (text);
  100. cache[*last].index = id;
  101. *last = ((*last) + 1) % size;
  102. }
  103. /* --------------------------------------------------------------------------------------------- */
  104. /*** public functions ****************************************************************************/
  105. /* --------------------------------------------------------------------------------------------- */
  106. char *
  107. get_owner (int uid)
  108. {
  109. struct passwd *pwd;
  110. static char ibuf[10];
  111. char *name;
  112. static int uid_last;
  113. name = i_cache_match (uid, uid_cache, UID_CACHE_SIZE);
  114. if (name != NULL)
  115. return name;
  116. pwd = getpwuid (uid);
  117. if (pwd != NULL)
  118. {
  119. i_cache_add (uid, uid_cache, UID_CACHE_SIZE, pwd->pw_name, &uid_last);
  120. return pwd->pw_name;
  121. }
  122. else
  123. {
  124. g_snprintf (ibuf, sizeof (ibuf), "%d", uid);
  125. return ibuf;
  126. }
  127. }
  128. /* --------------------------------------------------------------------------------------------- */
  129. char *
  130. get_group (int gid)
  131. {
  132. struct group *grp;
  133. static char gbuf[10];
  134. char *name;
  135. static int gid_last;
  136. name = i_cache_match (gid, gid_cache, GID_CACHE_SIZE);
  137. if (name != NULL)
  138. return name;
  139. grp = getgrgid (gid);
  140. if (grp != NULL)
  141. {
  142. i_cache_add (gid, gid_cache, GID_CACHE_SIZE, grp->gr_name, &gid_last);
  143. return grp->gr_name;
  144. }
  145. else
  146. {
  147. g_snprintf (gbuf, sizeof (gbuf), "%d", gid);
  148. return gbuf;
  149. }
  150. }
  151. /* --------------------------------------------------------------------------------------------- */
  152. /* Since ncurses uses a handler that automatically refreshes the */
  153. /* screen after a SIGCONT, and we don't want this behavior when */
  154. /* spawning a child, we save the original handler here */
  155. void
  156. save_stop_handler (void)
  157. {
  158. sigaction (SIGTSTP, NULL, &startup_handler);
  159. }
  160. /* --------------------------------------------------------------------------------------------- */
  161. int
  162. my_system (int flags, const char *shell, const char *command)
  163. {
  164. struct sigaction ignore, save_intr, save_quit, save_stop;
  165. pid_t pid;
  166. int status = 0;
  167. ignore.sa_handler = SIG_IGN;
  168. sigemptyset (&ignore.sa_mask);
  169. ignore.sa_flags = 0;
  170. sigaction (SIGINT, &ignore, &save_intr);
  171. sigaction (SIGQUIT, &ignore, &save_quit);
  172. /* Restore the original SIGTSTP handler, we don't want ncurses' */
  173. /* handler messing the screen after the SIGCONT */
  174. sigaction (SIGTSTP, &startup_handler, &save_stop);
  175. pid = fork ();
  176. if (pid < 0)
  177. {
  178. fprintf (stderr, "\n\nfork () = -1\n");
  179. status = -1;
  180. }
  181. else if (pid == 0)
  182. {
  183. signal (SIGINT, SIG_DFL);
  184. signal (SIGQUIT, SIG_DFL);
  185. signal (SIGTSTP, SIG_DFL);
  186. signal (SIGCHLD, SIG_DFL);
  187. if (flags & EXECUTE_AS_SHELL)
  188. execl (shell, shell, "-c", command, (char *) NULL);
  189. else
  190. {
  191. gchar **shell_tokens;
  192. const gchar *only_cmd;
  193. shell_tokens = g_strsplit (shell, " ", 2);
  194. if (shell_tokens == NULL)
  195. only_cmd = shell;
  196. else
  197. only_cmd = (*shell_tokens != NULL) ? *shell_tokens : shell;
  198. execlp (only_cmd, shell, command, (char *) NULL);
  199. /*
  200. execlp will replace current process,
  201. therefore no sence in call of g_strfreev().
  202. But this keeped for estetic reason :)
  203. */
  204. g_strfreev (shell_tokens);
  205. }
  206. _exit (127); /* Exec error */
  207. }
  208. else
  209. {
  210. while (TRUE)
  211. {
  212. if (waitpid (pid, &status, 0) > 0)
  213. {
  214. status = WEXITSTATUS (status);
  215. break;
  216. }
  217. if (errno != EINTR)
  218. {
  219. status = -1;
  220. break;
  221. }
  222. }
  223. }
  224. sigaction (SIGINT, &save_intr, NULL);
  225. sigaction (SIGQUIT, &save_quit, NULL);
  226. sigaction (SIGTSTP, &save_stop, NULL);
  227. return status;
  228. }
  229. /* --------------------------------------------------------------------------------------------- */
  230. /**
  231. * Perform tilde expansion if possible.
  232. * Always return a newly allocated string, even if it's unchanged.
  233. */
  234. char *
  235. tilde_expand (const char *directory)
  236. {
  237. struct passwd *passwd;
  238. const char *p, *q;
  239. char *name;
  240. if (*directory != '~')
  241. return g_strdup (directory);
  242. p = directory + 1;
  243. /* d = "~" or d = "~/" */
  244. if (!(*p) || (*p == PATH_SEP))
  245. {
  246. passwd = getpwuid (geteuid ());
  247. q = (*p == PATH_SEP) ? p + 1 : "";
  248. }
  249. else
  250. {
  251. q = strchr (p, PATH_SEP);
  252. if (!q)
  253. {
  254. passwd = getpwnam (p);
  255. }
  256. else
  257. {
  258. name = g_strndup (p, q - p);
  259. passwd = getpwnam (name);
  260. q++;
  261. g_free (name);
  262. }
  263. }
  264. /* If we can't figure the user name, leave tilde unexpanded */
  265. if (!passwd)
  266. return g_strdup (directory);
  267. return g_strconcat (passwd->pw_dir, PATH_SEP_STR, q, (char *) NULL);
  268. }
  269. /* --------------------------------------------------------------------------------------------- */
  270. /**
  271. * Creates a pipe to hold standard error for a later analysis.
  272. * The pipe can hold 4096 bytes. Make sure no more is written
  273. * or a deadlock might occur.
  274. */
  275. void
  276. open_error_pipe (void)
  277. {
  278. if (pipe (error_pipe) < 0)
  279. {
  280. message (D_NORMAL, _("Warning"), _("Pipe failed"));
  281. }
  282. old_error = dup (2);
  283. if (old_error < 0 || close (2) || dup (error_pipe[1]) != 2)
  284. {
  285. message (D_NORMAL, _("Warning"), _("Dup failed"));
  286. close (error_pipe[0]);
  287. error_pipe[0] = -1;
  288. }
  289. else
  290. {
  291. /*
  292. * Settng stderr in nonblocking mode as we close it earlier, than
  293. * program stops. We try to read some error at program startup,
  294. * but we should not block on it.
  295. *
  296. * TODO: make piped stdin/stderr poll()/select()able to get rid
  297. * of following hack.
  298. */
  299. int fd_flags;
  300. fd_flags = fcntl (error_pipe[0], F_GETFL, NULL);
  301. if (fd_flags != -1)
  302. {
  303. fd_flags |= O_NONBLOCK;
  304. if (fcntl (error_pipe[0], F_SETFL, fd_flags) == -1)
  305. {
  306. /* TODO: handle it somehow */
  307. }
  308. }
  309. }
  310. /* we never write there */
  311. close (error_pipe[1]);
  312. error_pipe[1] = -1;
  313. }
  314. /* --------------------------------------------------------------------------------------------- */
  315. /**
  316. * Returns true if an error was displayed
  317. * error: -1 - ignore errors, 0 - display warning, 1 - display error
  318. * text is prepended to the error message from the pipe
  319. */
  320. int
  321. close_error_pipe (int error, const char *text)
  322. {
  323. const char *title;
  324. char msg[MAX_PIPE_SIZE];
  325. int len = 0;
  326. /* already closed */
  327. if (error_pipe[0] == -1)
  328. return 0;
  329. if (error < 0 || (error > 0 && (error & D_ERROR) != 0))
  330. title = MSG_ERROR;
  331. else
  332. title = _("Warning");
  333. if (old_error >= 0)
  334. {
  335. if (dup2 (old_error, 2) == -1)
  336. {
  337. if (error < 0)
  338. error = D_ERROR;
  339. message (error, MSG_ERROR, _("Error dup'ing old error pipe"));
  340. return 1;
  341. }
  342. close (old_error);
  343. len = read (error_pipe[0], msg, MAX_PIPE_SIZE - 1);
  344. if (len >= 0)
  345. msg[len] = 0;
  346. close (error_pipe[0]);
  347. error_pipe[0] = -1;
  348. }
  349. if (error < 0)
  350. return 0; /* Just ignore error message */
  351. if (text == NULL)
  352. {
  353. if (len <= 0)
  354. return 0; /* Nothing to show */
  355. /* Show message from pipe */
  356. message (error, title, "%s", msg);
  357. }
  358. else
  359. {
  360. /* Show given text and possible message from pipe */
  361. message (error, title, "%s\n%s", text, msg);
  362. }
  363. return 1;
  364. }
  365. /* --------------------------------------------------------------------------------------------- */
  366. /**
  367. * Canonicalize path, and return a new path. Do everything in place.
  368. * The new path differs from path in:
  369. * Multiple `/'s are collapsed to a single `/'.
  370. * Leading `./'s and trailing `/.'s are removed.
  371. * Trailing `/'s are removed.
  372. * Non-leading `../'s and trailing `..'s are handled by removing
  373. * portions of the path.
  374. * Well formed UNC paths are modified only in the local part.
  375. */
  376. void
  377. custom_canonicalize_pathname (char *path, CANON_PATH_FLAGS flags)
  378. {
  379. char *p, *s;
  380. size_t len;
  381. char *lpath = path; /* path without leading UNC part */
  382. const size_t url_delim_len = strlen (VFS_PATH_URL_DELIMITER);
  383. /* Detect and preserve UNC paths: //server/... */
  384. if ((flags & CANON_PATH_GUARDUNC) && path[0] == PATH_SEP && path[1] == PATH_SEP)
  385. {
  386. p = path + 2;
  387. while (p[0] && p[0] != '/')
  388. p++;
  389. if (p[0] == '/' && p > path + 2)
  390. lpath = p;
  391. }
  392. if (!lpath[0] || !lpath[1])
  393. return;
  394. if (flags & CANON_PATH_JOINSLASHES)
  395. {
  396. /* Collapse multiple slashes */
  397. p = lpath;
  398. while (*p)
  399. {
  400. if (p[0] == PATH_SEP && p[1] == PATH_SEP && (p == lpath || *(p - 1) != ':'))
  401. {
  402. s = p + 1;
  403. while (*(++s) == PATH_SEP);
  404. str_move (p + 1, s);
  405. }
  406. p++;
  407. }
  408. }
  409. if (flags & CANON_PATH_JOINSLASHES)
  410. {
  411. /* Collapse "/./" -> "/" */
  412. p = lpath;
  413. while (*p)
  414. {
  415. if (p[0] == PATH_SEP && p[1] == '.' && p[2] == PATH_SEP)
  416. str_move (p, p + 2);
  417. else
  418. p++;
  419. }
  420. }
  421. if (flags & CANON_PATH_REMSLASHDOTS)
  422. {
  423. /* Remove trailing slashes */
  424. p = lpath + strlen (lpath) - 1;
  425. while (p > lpath && *p == PATH_SEP)
  426. {
  427. if (p >= lpath - (url_delim_len + 1)
  428. && strncmp (p - url_delim_len + 1, VFS_PATH_URL_DELIMITER, url_delim_len) == 0)
  429. break;
  430. *p-- = 0;
  431. }
  432. /* Remove leading "./" */
  433. if (lpath[0] == '.' && lpath[1] == PATH_SEP)
  434. {
  435. if (lpath[2] == 0)
  436. {
  437. lpath[1] = 0;
  438. return;
  439. }
  440. else
  441. {
  442. str_move (lpath, lpath + 2);
  443. }
  444. }
  445. /* Remove trailing "/" or "/." */
  446. len = strlen (lpath);
  447. if (len < 2)
  448. return;
  449. if (lpath[len - 1] == PATH_SEP
  450. && (len < url_delim_len
  451. || strncmp (lpath + len - url_delim_len, VFS_PATH_URL_DELIMITER,
  452. url_delim_len) != 0))
  453. {
  454. lpath[len - 1] = '\0';
  455. }
  456. else
  457. {
  458. if (lpath[len - 1] == '.' && lpath[len - 2] == PATH_SEP)
  459. {
  460. if (len == 2)
  461. {
  462. lpath[1] = '\0';
  463. return;
  464. }
  465. else
  466. {
  467. lpath[len - 2] = '\0';
  468. }
  469. }
  470. }
  471. }
  472. if (flags & CANON_PATH_REMDOUBLEDOTS)
  473. {
  474. #ifdef HAVE_CHARSET
  475. const size_t enc_prefix_len = strlen (VFS_ENCODING_PREFIX);
  476. #endif /* HAVE_CHARSET */
  477. /* Collapse "/.." with the previous part of path */
  478. p = lpath;
  479. while (p[0] && p[1] && p[2])
  480. {
  481. if ((p[0] != PATH_SEP || p[1] != '.' || p[2] != '.') || (p[3] != PATH_SEP && p[3] != 0))
  482. {
  483. p++;
  484. continue;
  485. }
  486. /* search for the previous token */
  487. s = p - 1;
  488. if (s >= lpath + url_delim_len - 2
  489. && strncmp (s - url_delim_len + 2, VFS_PATH_URL_DELIMITER, url_delim_len) == 0)
  490. {
  491. s -= (url_delim_len - 2);
  492. while (s >= lpath && *s-- != PATH_SEP);
  493. }
  494. while (s >= lpath)
  495. {
  496. if (s - url_delim_len > lpath
  497. && strncmp (s - url_delim_len, VFS_PATH_URL_DELIMITER, url_delim_len) == 0)
  498. {
  499. char *vfs_prefix = s - url_delim_len;
  500. struct vfs_class *vclass;
  501. while (vfs_prefix > lpath && *--vfs_prefix != PATH_SEP);
  502. if (*vfs_prefix == PATH_SEP)
  503. vfs_prefix++;
  504. *(s - url_delim_len) = '\0';
  505. vclass = vfs_prefix_to_class (vfs_prefix);
  506. *(s - url_delim_len) = *VFS_PATH_URL_DELIMITER;
  507. if (vclass != NULL)
  508. {
  509. struct vfs_s_subclass *sub = (struct vfs_s_subclass *) vclass->data;
  510. if (sub != NULL && sub->flags & VFS_S_REMOTE)
  511. {
  512. s = vfs_prefix;
  513. continue;
  514. }
  515. }
  516. }
  517. if (*s == PATH_SEP)
  518. break;
  519. s--;
  520. }
  521. s++;
  522. /* If the previous token is "..", we cannot collapse it */
  523. if (s[0] == '.' && s[1] == '.' && s + 2 == p)
  524. {
  525. p += 3;
  526. continue;
  527. }
  528. if (p[3] != 0)
  529. {
  530. if (s == lpath && *s == PATH_SEP)
  531. {
  532. /* "/../foo" -> "/foo" */
  533. str_move (s + 1, p + 4);
  534. }
  535. else
  536. {
  537. /* "token/../foo" -> "foo" */
  538. #ifdef HAVE_CHARSET
  539. if ((strncmp (s, VFS_ENCODING_PREFIX, enc_prefix_len) == 0)
  540. && (is_supported_encoding (s + enc_prefix_len)))
  541. /* special case: remove encoding */
  542. str_move (s, p + 1);
  543. else
  544. #endif /* HAVE_CHARSET */
  545. str_move (s, p + 4);
  546. }
  547. p = (s > lpath) ? s - 1 : s;
  548. continue;
  549. }
  550. /* trailing ".." */
  551. if (s == lpath)
  552. {
  553. /* "token/.." -> "." */
  554. if (lpath[0] != PATH_SEP)
  555. {
  556. lpath[0] = '.';
  557. }
  558. lpath[1] = 0;
  559. }
  560. else
  561. {
  562. /* "foo/token/.." -> "foo" */
  563. if (s == lpath + 1)
  564. s[0] = 0;
  565. #ifdef HAVE_CHARSET
  566. else if ((strncmp (s, VFS_ENCODING_PREFIX, enc_prefix_len) == 0)
  567. && (is_supported_encoding (s + enc_prefix_len)))
  568. {
  569. /* special case: remove encoding */
  570. s[0] = '.';
  571. s[1] = '.';
  572. s[2] = '\0';
  573. /* search for the previous token */
  574. /* s[-1] == PATH_SEP */
  575. p = s - 1;
  576. while (p >= lpath && *p != PATH_SEP)
  577. p--;
  578. if (p != NULL)
  579. continue;
  580. }
  581. #endif /* HAVE_CHARSET */
  582. else
  583. {
  584. if (s >= lpath + url_delim_len
  585. && strncmp (s - url_delim_len, VFS_PATH_URL_DELIMITER, url_delim_len) == 0)
  586. *s = '\0';
  587. else
  588. s[-1] = '\0';
  589. }
  590. break;
  591. }
  592. break;
  593. }
  594. }
  595. }
  596. /* --------------------------------------------------------------------------------------------- */
  597. void
  598. canonicalize_pathname (char *path)
  599. {
  600. custom_canonicalize_pathname (path, CANON_PATH_ALL);
  601. }
  602. /* --------------------------------------------------------------------------------------------- */
  603. #ifdef HAVE_GET_PROCESS_STATS
  604. int
  605. gettimeofday (struct timeval *tp, void *tzp)
  606. {
  607. return get_process_stats (tp, PS_SELF, 0, 0);
  608. }
  609. #endif /* HAVE_GET_PROCESS_STATS */
  610. /* --------------------------------------------------------------------------------------------- */
  611. #ifndef HAVE_REALPATH
  612. char *
  613. mc_realpath (const char *path, char *resolved_path)
  614. {
  615. char copy_path[PATH_MAX];
  616. char link_path[PATH_MAX];
  617. char got_path[PATH_MAX];
  618. char *new_path = got_path;
  619. char *max_path;
  620. int readlinks = 0;
  621. int n;
  622. /* Make a copy of the source path since we may need to modify it. */
  623. if (strlen (path) >= PATH_MAX - 2)
  624. {
  625. errno = ENAMETOOLONG;
  626. return NULL;
  627. }
  628. strcpy (copy_path, path);
  629. path = copy_path;
  630. max_path = copy_path + PATH_MAX - 2;
  631. /* If it's a relative pathname use getwd for starters. */
  632. if (*path != '/')
  633. {
  634. new_path = g_get_current_dir ();
  635. if (new_path == NULL)
  636. {
  637. strcpy (got_path, "");
  638. }
  639. else
  640. {
  641. g_snprintf (got_path, PATH_MAX, "%s", new_path);
  642. g_free (new_path);
  643. new_path = got_path;
  644. }
  645. new_path += strlen (got_path);
  646. if (new_path[-1] != '/')
  647. *new_path++ = '/';
  648. }
  649. else
  650. {
  651. *new_path++ = '/';
  652. path++;
  653. }
  654. /* Expand each slash-separated pathname component. */
  655. while (*path != '\0')
  656. {
  657. /* Ignore stray "/". */
  658. if (*path == '/')
  659. {
  660. path++;
  661. continue;
  662. }
  663. if (*path == '.')
  664. {
  665. /* Ignore ".". */
  666. if (path[1] == '\0' || path[1] == '/')
  667. {
  668. path++;
  669. continue;
  670. }
  671. if (path[1] == '.')
  672. {
  673. if (path[2] == '\0' || path[2] == '/')
  674. {
  675. path += 2;
  676. /* Ignore ".." at root. */
  677. if (new_path == got_path + 1)
  678. continue;
  679. /* Handle ".." by backing up. */
  680. while ((--new_path)[-1] != '/');
  681. continue;
  682. }
  683. }
  684. }
  685. /* Safely copy the next pathname component. */
  686. while (*path != '\0' && *path != '/')
  687. {
  688. if (path > max_path)
  689. {
  690. errno = ENAMETOOLONG;
  691. return NULL;
  692. }
  693. *new_path++ = *path++;
  694. }
  695. #ifdef S_IFLNK
  696. /* Protect against infinite loops. */
  697. if (readlinks++ > MAXSYMLINKS)
  698. {
  699. errno = ELOOP;
  700. return NULL;
  701. }
  702. /* See if latest pathname component is a symlink. */
  703. *new_path = '\0';
  704. n = readlink (got_path, link_path, PATH_MAX - 1);
  705. if (n < 0)
  706. {
  707. /* EINVAL means the file exists but isn't a symlink. */
  708. if (errno != EINVAL)
  709. {
  710. /* Make sure it's null terminated. */
  711. *new_path = '\0';
  712. strcpy (resolved_path, got_path);
  713. return NULL;
  714. }
  715. }
  716. else
  717. {
  718. /* Note: readlink doesn't add the null byte. */
  719. link_path[n] = '\0';
  720. if (*link_path == '/')
  721. /* Start over for an absolute symlink. */
  722. new_path = got_path;
  723. else
  724. /* Otherwise back up over this component. */
  725. while (*(--new_path) != '/');
  726. /* Safe sex check. */
  727. if (strlen (path) + n >= PATH_MAX - 2)
  728. {
  729. errno = ENAMETOOLONG;
  730. return NULL;
  731. }
  732. /* Insert symlink contents into path. */
  733. strcat (link_path, path);
  734. strcpy (copy_path, link_path);
  735. path = copy_path;
  736. }
  737. #endif /* S_IFLNK */
  738. *new_path++ = '/';
  739. }
  740. /* Delete trailing slash but don't whomp a lone slash. */
  741. if (new_path != got_path + 1 && new_path[-1] == '/')
  742. new_path--;
  743. /* Make sure it's null terminated. */
  744. *new_path = '\0';
  745. strcpy (resolved_path, got_path);
  746. return resolved_path;
  747. }
  748. #endif /* HAVE_REALPATH */
  749. /* --------------------------------------------------------------------------------------------- */
  750. /**
  751. * Return the index of the permissions triplet
  752. *
  753. */
  754. int
  755. get_user_permissions (struct stat *st)
  756. {
  757. static gboolean initialized = FALSE;
  758. static gid_t *groups;
  759. static int ngroups;
  760. static uid_t uid;
  761. int i;
  762. if (!initialized)
  763. {
  764. uid = geteuid ();
  765. ngroups = getgroups (0, NULL);
  766. if (ngroups == -1)
  767. ngroups = 0; /* ignore errors */
  768. /* allocate space for one element in addition to what
  769. * will be filled by getgroups(). */
  770. groups = g_new (gid_t, ngroups + 1);
  771. if (ngroups != 0)
  772. {
  773. ngroups = getgroups (ngroups, groups);
  774. if (ngroups == -1)
  775. ngroups = 0; /* ignore errors */
  776. }
  777. /* getgroups() may or may not return the effective group ID,
  778. * so we always include it at the end of the list. */
  779. groups[ngroups++] = getegid ();
  780. initialized = TRUE;
  781. }
  782. if (st->st_uid == uid || uid == 0)
  783. return 0;
  784. for (i = 0; i < ngroups; i++)
  785. {
  786. if (st->st_gid == groups[i])
  787. return 1;
  788. }
  789. return 2;
  790. }
  791. /* --------------------------------------------------------------------------------------------- */
  792. /**
  793. * Build filename from arguments.
  794. * Like to g_build_filename(), but respect VFS_PATH_URL_DELIMITER
  795. */
  796. char *
  797. mc_build_filenamev (const char *first_element, va_list args)
  798. {
  799. gboolean absolute;
  800. const char *element = first_element;
  801. GString *path;
  802. char *ret;
  803. if (element == NULL)
  804. return NULL;
  805. path = g_string_new ("");
  806. absolute = (*first_element != '\0' && *first_element == PATH_SEP);
  807. do
  808. {
  809. if (*element == '\0')
  810. element = va_arg (args, char *);
  811. else
  812. {
  813. char *tmp_element;
  814. size_t len;
  815. const char *start;
  816. tmp_element = g_strdup (element);
  817. element = va_arg (args, char *);
  818. canonicalize_pathname (tmp_element);
  819. len = strlen (tmp_element);
  820. start = (tmp_element[0] == PATH_SEP) ? tmp_element + 1 : tmp_element;
  821. g_string_append (path, start);
  822. if (tmp_element[len - 1] != PATH_SEP && element != NULL)
  823. g_string_append_c (path, PATH_SEP);
  824. g_free (tmp_element);
  825. }
  826. }
  827. while (element != NULL);
  828. if (absolute)
  829. g_string_prepend_c (path, PATH_SEP);
  830. ret = g_string_free (path, FALSE);
  831. canonicalize_pathname (ret);
  832. return ret;
  833. }
  834. /* --------------------------------------------------------------------------------------------- */
  835. /**
  836. * Build filename from arguments.
  837. * Like to g_build_filename(), but respect VFS_PATH_URL_DELIMITER
  838. */
  839. char *
  840. mc_build_filename (const char *first_element, ...)
  841. {
  842. va_list args;
  843. char *ret;
  844. if (first_element == NULL)
  845. return NULL;
  846. va_start (args, first_element);
  847. ret = mc_build_filenamev (first_element, args);
  848. va_end (args);
  849. return ret;
  850. }
  851. /* --------------------------------------------------------------------------------------------- */