chroot_realpath.c 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163
  1. /*
  2. * chroot_realpath.c -- reslove pathname as if inside chroot
  3. * Based on realpath.c Copyright (C) 1993 Rick Sladkey <jrs@world.std.com>
  4. *
  5. * This program is free software; you can redistribute it and/or modify
  6. * it under the terms of the GNU Library Public License as published by
  7. * the Free Software Foundation; either version 2, or (at your option)
  8. * any later version.
  9. *
  10. * This program is distributed in the hope that it will be useful,
  11. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. * GNU Library Public License for more details.
  14. *
  15. * 2005/09/12: Dan Howell (modified from realpath.c to emulate chroot)
  16. */
  17. #ifdef HAVE_CONFIG_H
  18. #include <config.h>
  19. #endif
  20. #include <sys/types.h>
  21. #include <unistd.h>
  22. #include <stdio.h>
  23. #include <string.h>
  24. #include <strings.h>
  25. #include <limits.h> /* for PATH_MAX */
  26. #include <sys/param.h> /* for MAXPATHLEN */
  27. #include <errno.h>
  28. #ifndef __set_errno
  29. #define __set_errno(val) ((errno) = (val))
  30. #endif
  31. #include <sys/stat.h> /* for S_IFLNK */
  32. #ifndef PATH_MAX
  33. #define PATH_MAX _POSIX_PATH_MAX
  34. #endif
  35. #define MAX_READLINKS 32
  36. char *chroot_realpath(const char *chroot, const char *path, char resolved_path[])
  37. {
  38. char copy_path[PATH_MAX];
  39. char link_path[PATH_MAX];
  40. char got_path[PATH_MAX];
  41. char *got_path_root = got_path;
  42. char *new_path = got_path;
  43. char *max_path;
  44. int readlinks = 0;
  45. int n;
  46. int chroot_len;
  47. /* Trivial case. */
  48. if (chroot == NULL || *chroot == '\0' ||
  49. (*chroot == '/' && chroot[1] == '\0')) {
  50. strcpy(resolved_path, path);
  51. return resolved_path;
  52. }
  53. chroot_len = strlen(chroot);
  54. if (chroot_len + strlen(path) >= PATH_MAX - 3) {
  55. __set_errno(ENAMETOOLONG);
  56. return NULL;
  57. }
  58. /* Make a copy of the source path since we may need to modify it. */
  59. strcpy(copy_path, path);
  60. path = copy_path;
  61. max_path = copy_path + PATH_MAX - chroot_len - 3;
  62. /* Start with the chroot path. */
  63. strcpy(new_path, chroot);
  64. new_path += chroot_len;
  65. while (*new_path == '/' && new_path > got_path)
  66. new_path--;
  67. got_path_root = new_path;
  68. *new_path++ = '/';
  69. /* Expand each slash-separated pathname component. */
  70. while (*path != '\0') {
  71. /* Ignore stray "/". */
  72. if (*path == '/') {
  73. path++;
  74. continue;
  75. }
  76. if (*path == '.') {
  77. /* Ignore ".". */
  78. if (path[1] == '\0' || path[1] == '/') {
  79. path++;
  80. continue;
  81. }
  82. if (path[1] == '.') {
  83. if (path[2] == '\0' || path[2] == '/') {
  84. path += 2;
  85. /* Ignore ".." at root. */
  86. if (new_path == got_path_root + 1)
  87. continue;
  88. /* Handle ".." by backing up. */
  89. while ((--new_path)[-1] != '/');
  90. continue;
  91. }
  92. }
  93. }
  94. /* Safely copy the next pathname component. */
  95. while (*path != '\0' && *path != '/') {
  96. if (path > max_path) {
  97. __set_errno(ENAMETOOLONG);
  98. return NULL;
  99. }
  100. *new_path++ = *path++;
  101. }
  102. if (*path == '\0')
  103. /* Don't follow symlink for last pathname component. */
  104. break;
  105. #ifdef S_IFLNK
  106. /* Protect against infinite loops. */
  107. if (readlinks++ > MAX_READLINKS) {
  108. __set_errno(ELOOP);
  109. return NULL;
  110. }
  111. /* See if latest pathname component is a symlink. */
  112. *new_path = '\0';
  113. n = readlink(got_path, link_path, PATH_MAX - 1);
  114. if (n < 0) {
  115. /* EINVAL means the file exists but isn't a symlink. */
  116. if (errno != EINVAL) {
  117. /* Make sure it's null terminated. */
  118. *new_path = '\0';
  119. strcpy(resolved_path, got_path);
  120. return NULL;
  121. }
  122. } else {
  123. /* Note: readlink doesn't add the null byte. */
  124. link_path[n] = '\0';
  125. if (*link_path == '/')
  126. /* Start over for an absolute symlink. */
  127. new_path = got_path_root;
  128. else
  129. /* Otherwise back up over this component. */
  130. while (*(--new_path) != '/');
  131. /* Safe sex check. */
  132. if (strlen(path) + n >= PATH_MAX - 2) {
  133. __set_errno(ENAMETOOLONG);
  134. return NULL;
  135. }
  136. /* Insert symlink contents into path. */
  137. strcat(link_path, path);
  138. strcpy(copy_path, link_path);
  139. path = copy_path;
  140. }
  141. #endif /* S_IFLNK */
  142. *new_path++ = '/';
  143. }
  144. /* Delete trailing slash but don't whomp a lone slash. */
  145. if (new_path != got_path + 1 && new_path[-1] == '/')
  146. new_path--;
  147. /* Make sure it's null terminated. */
  148. *new_path = '\0';
  149. strcpy(resolved_path, got_path);
  150. return resolved_path;
  151. }