path.c 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166
  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
  17. * a tree to 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 `size` - 1) and returns offset to it.
  33. * If the last character is '/', then it is skipped (as a token can end with '/').
  34. */
  35. static inline size_t find_prev_slash_offset(const char* path, size_t size) {
  36. if (size && path[size - 1] == '/') {
  37. size--;
  38. }
  39. while (size && path[size - 1] != '/') {
  40. size--;
  41. }
  42. return size;
  43. }
  44. /*
  45. * Before calling this function *size_ptr should hold the size of buf.
  46. * After returning it holds number of bytes actually written to it
  47. * (excluding the ending '\0').
  48. */
  49. int get_norm_path(const char* path, char* buf, size_t* size_ptr) {
  50. if (!path || !buf || !size_ptr) {
  51. return -PAL_ERROR_INVAL;
  52. }
  53. size_t size = *size_ptr;
  54. if (!size) {
  55. return -PAL_ERROR_ZEROSIZE;
  56. }
  57. /* reserve 1 byte for ending '\0' */
  58. size--;
  59. size_t offset = 0,
  60. ret_size = 0; /* accounts for undiscardable bytes written to `buf`
  61. * i.e. `buf - ret_size` points to original `buf` */
  62. unsigned char need_slash = 0; // is '/' needed before next token
  63. bool is_absolute_path = *path == '/';
  64. /* handle an absolute path */
  65. if (is_absolute_path) {
  66. if (size < 1) {
  67. return -PAL_ERROR_TOOLONG;
  68. }
  69. *buf++ = '/';
  70. size--;
  71. ret_size++;
  72. path++;
  73. }
  74. while (1) {
  75. /* handle next token */
  76. const char* end = find_next_slash(path);
  77. if (end - path == 2 && path[0] == '.' && path[1] == '.') {
  78. /* ".." */
  79. if (offset) {
  80. /* eat up previously written token */
  81. offset = find_prev_slash_offset(buf, offset);
  82. need_slash = 0;
  83. } else if (!is_absolute_path) {
  84. /* append undiscardable ".." since there is no previous token
  85. * but only if the path is not absolute */
  86. if (need_slash + 2u > size) {
  87. return -PAL_ERROR_TOOLONG;
  88. }
  89. if (need_slash) {
  90. *buf++ = '/';
  91. }
  92. *buf++ = '.';
  93. *buf++ = '.';
  94. size -= need_slash + 2u;
  95. ret_size += need_slash + 2u;
  96. need_slash = 1;
  97. } else {
  98. /* remaining case: offset == 0, path is absolute and ".." was just seen,
  99. * i.e. "/..", which is collapsed to "/", hence nothing needs to be done */
  100. }
  101. } else if ((end == path) || (end - path == 1 && path[0] == '.')) {
  102. /* ignore "//" and "." */
  103. } else {
  104. size_t len = (size_t)(end - path);
  105. if (need_slash + len > size - offset) {
  106. return -PAL_ERROR_TOOLONG;
  107. }
  108. if (need_slash) {
  109. buf[offset++] = '/';
  110. }
  111. memcpy(buf + offset, path, len);
  112. offset += len;
  113. need_slash = 1;
  114. }
  115. if (!*end) {
  116. break;
  117. }
  118. path = end + 1;
  119. }
  120. buf[offset] = '\0';
  121. *size_ptr = ret_size + offset;
  122. return 0;
  123. }
  124. /*
  125. * Before calling this function *size should hold the size of buf.
  126. * After returning it holds number of bytes actually written to it
  127. * (excluding the trailing '\0').
  128. */
  129. int get_base_name(const char* path, char* buf, size_t* size) {
  130. if (!path || !buf || !size) {
  131. return -PAL_ERROR_INVAL;
  132. }
  133. const char* end;
  134. while (*(end = find_next_slash(path))) {
  135. path = end + 1;
  136. }
  137. size_t result = (size_t)(end - path);
  138. if (result + 1 > *size) {
  139. return -PAL_ERROR_TOOLONG;
  140. }
  141. memcpy(buf, path, result);
  142. buf[result] = '\0';
  143. *size = result;
  144. return 0;
  145. }