path.c 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178
  1. /* Copyright (C) 2014 Stony Brook University
  2. This file is part of Graphene Library OS.
  3. Graphene Library OS is free software: you can redistribute it and/or
  4. modify it under the terms of the GNU Lesser General Public License
  5. as published by the Free Software Foundation, either version 3 of the
  6. License, or (at your option) any later version.
  7. Graphene Library OS is distributed in the hope that it will be useful,
  8. but WITHOUT ANY WARRANTY; without even the implied warranty of
  9. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  10. GNU Lesser General Public License for more details.
  11. You should have received a copy of the GNU Lesser General Public License
  12. along with this program. If not, see <http://www.gnu.org/licenses/>. */
  13. /*
  14. * path.c
  15. *
  16. * This file contains functions to read app config (manifest) file and create a tree to
  17. * lookup / access config values.
  18. */
  19. #include <api.h>
  20. #include <pal_error.h>
  21. /*
  22. * Finds next '/' in `path`.
  23. * Returns a pointer to it or to the nullbyte ending the string if no '/' has been found.
  24. */
  25. static inline const char* find_next_slash(const char* path) {
  26. while (*path && *path != '/') {
  27. path++;
  28. }
  29. return path;
  30. }
  31. /*
  32. * Finds previous '/' in `path` (starting from `offset` - 1).
  33. * If the last character is '/', then it is skipped (as a token can end with '/').
  34. *
  35. * Returns whether '/' was found.
  36. * Updates `*offset` to the index of the found '/' (or 0 if none was found).
  37. */
  38. static inline bool find_prev_slash_offset(const char* path, size_t* offset) {
  39. size_t off = *offset;
  40. if (!off) {
  41. return false;
  42. }
  43. off--; // get offset to last character
  44. /* Skip trailing '/' if there is one */
  45. if (off && path[off] == '/') {
  46. off--;
  47. }
  48. while (off && path[off] != '/') {
  49. off--;
  50. }
  51. *offset = off;
  52. return path[off] == '/';
  53. }
  54. /*
  55. * Before calling this function *size_ptr should hold the size of buf.
  56. * After returning it holds number of bytes actually written to it (excluding the ending '\0').
  57. */
  58. int get_norm_path(const char* path, char* buf, size_t* size_ptr) {
  59. if (!path || !buf || !size_ptr) {
  60. return -PAL_ERROR_INVAL;
  61. }
  62. size_t size = *size_ptr;
  63. if (!size) {
  64. return -PAL_ERROR_INVAL;
  65. }
  66. /* reserve 1 byte for ending '\0' */
  67. size--;
  68. size_t offset = 0, ret_size = 0; /* accounts for undiscardable bytes written to `buf`
  69. * i.e. `buf - ret_size` points to original `buf` */
  70. bool need_slash = false; // is '/' needed before next token
  71. bool is_absolute_path = *path == '/';
  72. /* handle an absolute path */
  73. if (is_absolute_path) {
  74. if (size < 1) {
  75. return -PAL_ERROR_TOOLONG;
  76. }
  77. *buf++ = '/';
  78. size--;
  79. ret_size++;
  80. path++;
  81. }
  82. while (1) {
  83. /* handle next token */
  84. const char* end = find_next_slash(path);
  85. if (end - path == 2 && path[0] == '.' && path[1] == '.') {
  86. /* ".." */
  87. if (offset) {
  88. /* eat up previously written token */
  89. need_slash = find_prev_slash_offset(buf, &offset);
  90. } else if (!is_absolute_path) {
  91. /* append undiscardable ".." since there is no previous token
  92. * but only if the path is not absolute */
  93. if (need_slash + 2u > size) {
  94. return -PAL_ERROR_TOOLONG;
  95. }
  96. if (need_slash) {
  97. *buf++ = '/';
  98. }
  99. *buf++ = '.';
  100. *buf++ = '.';
  101. size -= need_slash + 2u;
  102. ret_size += need_slash + 2u;
  103. need_slash = true;
  104. } else {
  105. /* remaining case: offset == 0, path is absolute and ".." was just seen,
  106. * i.e. "/..", which is collapsed to "/", hence nothing needs to be done
  107. */
  108. }
  109. } else if ((end == path) || (end - path == 1 && path[0] == '.')) {
  110. /* ignore "//" and "." */
  111. } else {
  112. size_t len = (size_t)(end - path);
  113. if (need_slash + len > size - offset) {
  114. return -PAL_ERROR_TOOLONG;
  115. }
  116. if (need_slash) {
  117. buf[offset++] = '/';
  118. }
  119. memcpy(buf + offset, path, len);
  120. offset += len;
  121. need_slash = true;
  122. }
  123. if (!*end) {
  124. break;
  125. }
  126. path = end + 1;
  127. }
  128. buf[offset] = '\0';
  129. *size_ptr = ret_size + offset;
  130. return 0;
  131. }
  132. /*
  133. * Returns the part after the last '/' (so `path` should probably be normalized).
  134. * Before calling this function *size should hold the size of buf.
  135. * After returning it holds number of bytes actually written to it (excluding the trailing '\0').
  136. */
  137. int get_base_name(const char* path, char* buf, size_t* size) {
  138. if (!path || !buf || !size) {
  139. return -PAL_ERROR_INVAL;
  140. }
  141. const char* end;
  142. while (*(end = find_next_slash(path))) {
  143. path = end + 1;
  144. }
  145. size_t result = (size_t)(end - path);
  146. if (result + 1 > *size) {
  147. return -PAL_ERROR_TOOLONG;
  148. }
  149. memcpy(buf, path, result);
  150. buf[result] = '\0';
  151. *size = result;
  152. return 0;
  153. }