utilunix.c 29 KB

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