1
0

realpath.c 9.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382
  1. /*++
  2. Copyright (c) 2015 Minoca Corp.
  3. This file is licensed under the terms of the GNU General Public License
  4. version 3. Alternative licensing terms are available. Contact
  5. info@minocacorp.com for details. See the LICENSE file at the root of this
  6. project for complete licensing information.
  7. Module Name:
  8. realpath.c
  9. Abstract:
  10. This module implements support for the realpath function.
  11. Author:
  12. Evan Green 9-Mar-2015
  13. Environment:
  14. User Mode C Library
  15. --*/
  16. //
  17. // ------------------------------------------------------------------- Includes
  18. //
  19. #include "libcp.h"
  20. #include <assert.h>
  21. #include <errno.h>
  22. #include <limits.h>
  23. #include <stdio.h>
  24. #include <stdlib.h>
  25. #include <sys/param.h>
  26. #include <sys/stat.h>
  27. #include <unistd.h>
  28. //
  29. // ---------------------------------------------------------------- Definitions
  30. //
  31. //
  32. // ------------------------------------------------------ Data Type Definitions
  33. //
  34. //
  35. // ----------------------------------------------- Internal Function Prototypes
  36. //
  37. //
  38. // -------------------------------------------------------------------- Globals
  39. //
  40. //
  41. // ------------------------------------------------------------------ Functions
  42. //
  43. LIBC_API
  44. char *
  45. realpath (
  46. const char *Path,
  47. char *ResolvedPath
  48. )
  49. /*++
  50. Routine Description:
  51. This routine returns the canonical path for the given file path. This
  52. canonical path will include no '.' or '..' components, and will not
  53. contain symbolic links in any components of the path. All path components
  54. must exist.
  55. Arguments:
  56. Path - Supplies a pointer to the path to canonicalize.
  57. ResolvedPath - Supplies an optional pointer to the buffer to place the
  58. resolved path in. This must be at least PATH_MAX bytes.
  59. Return Value:
  60. Returns a pointer to the resolved path on success.
  61. NULL on failure.
  62. --*/
  63. {
  64. PSTR AllocatedPath;
  65. PSTR AppendedLink;
  66. UINTN ComponentSize;
  67. PSTR Destination;
  68. PSTR End;
  69. UINTN EndLength;
  70. PSTR Link;
  71. UINTN LinkCount;
  72. ssize_t LinkSize;
  73. PVOID NewBuffer;
  74. size_t NewSize;
  75. long PathMax;
  76. PSTR ResolvedPathEnd;
  77. PSTR Start;
  78. struct stat Stat;
  79. int Status;
  80. AllocatedPath = NULL;
  81. AppendedLink = NULL;
  82. Link = NULL;
  83. LinkCount = 0;
  84. if (Path == NULL) {
  85. errno = EINVAL;
  86. return NULL;
  87. }
  88. if (*Path == '\0') {
  89. errno = ENOENT;
  90. return NULL;
  91. }
  92. PathMax = PATH_MAX;
  93. if (ResolvedPath == NULL) {
  94. AllocatedPath = malloc(PathMax);
  95. if (AllocatedPath == NULL) {
  96. return NULL;
  97. }
  98. ResolvedPath = AllocatedPath;
  99. }
  100. ResolvedPathEnd = ResolvedPath + PathMax;
  101. //
  102. // Add the current working directory if this is a relative path.
  103. //
  104. if (Path[0] != '/') {
  105. if (getcwd(ResolvedPath, PathMax) == NULL) {
  106. Status = errno;
  107. goto realpathEnd;
  108. }
  109. Destination = memchr(ResolvedPath, '\0', PathMax);
  110. } else {
  111. ResolvedPath[0] = '/';
  112. Destination = ResolvedPath + 1;
  113. }
  114. Start = (PSTR)Path;
  115. while (*Start != '\0') {
  116. //
  117. // Skip leading separators.
  118. //
  119. while (*Start == '/') {
  120. Start += 1;
  121. }
  122. End = Start;
  123. while ((*End != '\0') && (*End != '/')) {
  124. End += 1;
  125. }
  126. ComponentSize = End - Start;
  127. if (ComponentSize == 0) {
  128. break;
  129. }
  130. //
  131. // For dot dot, back up to the previous component.
  132. //
  133. if ((ComponentSize == 2) && (Start[0] == '.') && (Start[1] == '.')) {
  134. if (Destination > ResolvedPath + 1) {
  135. Destination -= 1;
  136. while (*(Destination - 1) != '/') {
  137. Destination -= 1;
  138. }
  139. }
  140. //
  141. // If it's just a dot, do nothing. Otherwise, it's a component.
  142. //
  143. } else if (!((ComponentSize == 1) && (Start[0] == '.'))) {
  144. if (*(Destination - 1) != '/') {
  145. *Destination = '/';
  146. Destination += 1;
  147. }
  148. //
  149. // Handle the component being too big, needing reallocation.
  150. //
  151. if (Destination + ComponentSize >= ResolvedPathEnd) {
  152. //
  153. // If the buffer was handed in, there is no reallocation.
  154. //
  155. if (AllocatedPath == NULL) {
  156. Status = ENAMETOOLONG;
  157. if (Destination > ResolvedPath + 1) {
  158. Destination -= 1;
  159. }
  160. *Destination = '\0';
  161. goto realpathEnd;
  162. }
  163. NewSize = ResolvedPathEnd - ResolvedPath;
  164. if (ComponentSize + 1 > PathMax) {
  165. NewSize += ComponentSize + 1;
  166. } else {
  167. NewSize += PathMax;
  168. }
  169. NewBuffer = realloc(AllocatedPath, NewSize);
  170. if (NewBuffer == NULL) {
  171. Status = errno;
  172. goto realpathEnd;
  173. }
  174. Destination = NewBuffer + (Destination - ResolvedPath);
  175. AllocatedPath = NewBuffer;
  176. ResolvedPath = NewBuffer;
  177. ResolvedPathEnd = ResolvedPath + NewSize;
  178. }
  179. memcpy(Destination, Start, ComponentSize);
  180. Destination += ComponentSize;
  181. *Destination = '\0';
  182. if (lstat(ResolvedPath, &Stat) < 0) {
  183. Status = errno;
  184. goto realpathEnd;
  185. }
  186. //
  187. // Follow symbolic links.
  188. //
  189. if (S_ISLNK(Stat.st_mode)) {
  190. if (Link == NULL) {
  191. Link = malloc(PathMax);
  192. if (Link == NULL) {
  193. Status = errno;
  194. goto realpathEnd;
  195. }
  196. }
  197. LinkCount += 1;
  198. if (LinkCount > MAXSYMLINKS) {
  199. Status = ELOOP;
  200. goto realpathEnd;
  201. }
  202. LinkSize = readlink(ResolvedPath, Link, PathMax - 1);
  203. if (LinkSize < 0) {
  204. Status = errno;
  205. goto realpathEnd;
  206. }
  207. Link[LinkSize] = '\0';
  208. //
  209. // Create another buffer containing the concatenation of the
  210. // link destination and the rest of the path string.
  211. //
  212. if (AppendedLink == NULL) {
  213. AppendedLink = malloc(PathMax);
  214. if (AppendedLink == NULL) {
  215. Status = errno;
  216. goto realpathEnd;
  217. }
  218. }
  219. EndLength = strlen(End);
  220. if ((EndLength + LinkSize) >= PathMax) {
  221. Status = ENAMETOOLONG;
  222. goto realpathEnd;
  223. }
  224. //
  225. // The loop may have already been through here, which means
  226. // the end buffer may already point inside this buffer. Hence
  227. // the need for the more delicate memmove (and the need to do
  228. // it before copying the link over the beginning part).
  229. //
  230. memmove(&(AppendedLink[LinkSize]), End, EndLength + 1);
  231. memcpy(AppendedLink, Link, LinkSize);
  232. Path = AppendedLink;
  233. End = AppendedLink;
  234. //
  235. // If it's an absolute link, start there.
  236. //
  237. if (Link[0] == '/') {
  238. Destination = ResolvedPath + 1;
  239. //
  240. // If it's a relative link, back up a component.
  241. //
  242. } else {
  243. if (Destination > ResolvedPath + 1) {
  244. Destination -= 1;
  245. while (*(Destination - 1) != '/') {
  246. Destination -= 1;
  247. }
  248. }
  249. }
  250. //
  251. // Fail if it's not the final component and it's not a directory.
  252. // This also catches paths that end in a slash, enforcing that they
  253. // must also be directories.
  254. //
  255. } else if ((!S_ISDIR(Stat.st_mode)) && (*End != '\0')) {
  256. Status = ENOTDIR;
  257. goto realpathEnd;
  258. }
  259. }
  260. //
  261. // Move to the next component.
  262. //
  263. Start = End;
  264. }
  265. //
  266. // Remove a trailing slash.
  267. //
  268. if ((Destination > ResolvedPath + 1) && (*(Destination - 1) == '/')) {
  269. Destination -= 1;
  270. }
  271. *Destination = '\0';
  272. Status = 0;
  273. realpathEnd:
  274. if (Link != NULL) {
  275. free(Link);
  276. }
  277. if (AppendedLink != NULL) {
  278. free(AppendedLink);
  279. }
  280. assert((ResolvedPath != NULL) &&
  281. ((AllocatedPath == NULL) || (ResolvedPath == AllocatedPath)));
  282. if (Status != 0) {
  283. errno = Status;
  284. if (AllocatedPath != NULL) {
  285. free(AllocatedPath);
  286. AllocatedPath = NULL;
  287. }
  288. ResolvedPath = NULL;
  289. }
  290. return ResolvedPath;
  291. }
  292. //
  293. // --------------------------------------------------------- Internal Functions
  294. //