logo

utils-std

Collection of commonly available Unix tools git clone https://anongit.hacktivis.me/git/utils-std.git/

offline_realpath.c (3199B)


  1. // Based on the realpath(3) function from musl
  2. // Copyright © 2005-2020 Rich Felker, et al.
  3. // SPDX-License-Identifier: MIT
  4. #define _POSIX_C_SOURCE 200809L
  5. #include "fs.h"
  6. #include "strchrnul.h"
  7. #include <errno.h>
  8. #include <limits.h>
  9. #include <stdlib.h>
  10. #include <string.h>
  11. #include <unistd.h>
  12. static size_t
  13. slash_len(const char *s)
  14. {
  15. const char *s0 = s;
  16. while(*s == '/')
  17. s++;
  18. return s - s0;
  19. }
  20. // realpath(3) but without checking for symlinks
  21. char *
  22. offline_realpath(const char *restrict filename, char *restrict resolved)
  23. {
  24. char stack[PATH_MAX + 1];
  25. char output[PATH_MAX];
  26. size_t p, q, l, l0, nup = 0;
  27. int check_dir = 0;
  28. if(!filename)
  29. {
  30. errno = EINVAL;
  31. return 0;
  32. }
  33. l = strnlen(filename, sizeof stack);
  34. if(!l)
  35. {
  36. errno = ENOENT;
  37. return 0;
  38. }
  39. if(l >= PATH_MAX) goto toolong;
  40. p = sizeof stack - l - 1;
  41. q = 0;
  42. memcpy(stack + p, filename, l + 1);
  43. /* Main loop. Each iteration pops the next part from stack of
  44. * remaining path components and consumes any slashes that follow.
  45. * If not a link, it's moved to output; if a link, contents are
  46. * pushed to the stack. */
  47. for(;; p += slash_len(stack + p))
  48. {
  49. /* If stack starts with /, the whole component is / or //
  50. * and the output state must be reset. */
  51. if(stack[p] == '/')
  52. {
  53. check_dir = 0;
  54. nup = 0;
  55. q = 0;
  56. output[q++] = '/';
  57. p++;
  58. /* Initial // is special. */
  59. if(stack[p] == '/' && stack[p + 1] != '/') output[q++] = '/';
  60. continue;
  61. }
  62. char *z = utils_strchrnul(stack + p, '/');
  63. l0 = l = z - (stack + p);
  64. if(!l && !check_dir) break;
  65. /* Skip any . component but preserve check_dir status. */
  66. if(l == 1 && stack[p] == '.')
  67. {
  68. p += l;
  69. continue;
  70. }
  71. /* Copy next component onto output at least temporarily, to
  72. * call readlink, but wait to advance output position until
  73. * determining it's not a link. */
  74. if(q && output[q - 1] != '/')
  75. {
  76. if(!p) goto toolong;
  77. stack[--p] = '/';
  78. l++;
  79. }
  80. if(q + l >= PATH_MAX) goto toolong;
  81. memcpy(output + q, stack + p, l);
  82. output[q + l] = 0;
  83. p += l;
  84. int up = 0;
  85. if(l0 == 2 && stack[p - 2] == '.' && stack[p - 1] == '.')
  86. {
  87. up = 1;
  88. /* Any non-.. path components we could cancel start
  89. * after nup repetitions of the 3-byte string "../";
  90. * if there are none, accumulate .. components to
  91. * later apply to cwd, if needed. */
  92. if(q <= 3 * nup)
  93. {
  94. nup++;
  95. q += l;
  96. continue;
  97. }
  98. }
  99. check_dir = 0;
  100. if(up)
  101. {
  102. while(q && output[q - 1] != '/')
  103. q--;
  104. if(q > 1 && (q > 2 || output[0] != '/')) q--;
  105. continue;
  106. }
  107. if(l0) q += l;
  108. check_dir = stack[p];
  109. }
  110. output[q] = 0;
  111. if(output[0] != '/')
  112. {
  113. if(!getcwd(stack, sizeof stack)) return 0;
  114. l = strlen(stack);
  115. /* Cancel any initial .. components. */
  116. p = 0;
  117. while(nup--)
  118. {
  119. while(l > 1 && stack[l - 1] != '/')
  120. l--;
  121. if(l > 1) l--;
  122. p += 2;
  123. if(p < q) p++;
  124. }
  125. if(q - p && stack[l - 1] != '/') stack[l++] = '/';
  126. if(l + (q - p) + 1 >= PATH_MAX) goto toolong;
  127. memmove(output + l, output + p, q - p + 1);
  128. memcpy(output, stack, l);
  129. q = l + q - p;
  130. }
  131. if(resolved)
  132. return memcpy(resolved, output, q + 1);
  133. else
  134. return strdup(output);
  135. toolong:
  136. errno = ENAMETOOLONG;
  137. return 0;
  138. }