path-utils.cc 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229
  1. // -*- mode: cpp; mode: fold -*-
  2. // Description /*{{{*/
  3. // $Id: path-utils.cc,v 1.2 1999/03/22 02:52:46 jgg Exp $
  4. /* ######################################################################
  5. Misc utility functions for dsync-flist to make use of.
  6. ##################################################################### */
  7. /*}}}*/
  8. #include "dsync-flist.h"
  9. #include <unistd.h>
  10. #include <stdlib.h>
  11. #include <sys/stat.h>
  12. #include <dsync/error.h>
  13. #include <string.h>
  14. // SimplifyPath - Short function to remove relative path components /*{{{*/
  15. // ---------------------------------------------------------------------
  16. /* This short function removes relative path components such as ./ and ../
  17. from the path and removes double // as well. It works by seperating
  18. the path into a list of components and then removing any un-needed
  19. compoments */
  20. bool SimplifyPath(char *Buffer)
  21. {
  22. // Create a list of path compoments
  23. char *Pos[100];
  24. unsigned CurPos = 0;
  25. Pos[CurPos] = Buffer;
  26. CurPos++;
  27. for (char *I = Buffer; *I != 0;)
  28. {
  29. if (*I == '/')
  30. {
  31. *I = 0;
  32. I++;
  33. Pos[CurPos] = I;
  34. CurPos++;
  35. }
  36. else
  37. I++;
  38. }
  39. // Strip //, ./ and ../
  40. for (unsigned I = 0; I != CurPos; I++)
  41. {
  42. if (Pos[I] == 0)
  43. continue;
  44. // Double slash
  45. if (Pos[I][0] == 0)
  46. {
  47. if (I != 0)
  48. Pos[I] = 0;
  49. continue;
  50. }
  51. // Dot slash
  52. if (Pos[I][0] == '.' && Pos[I][1] == 0)
  53. {
  54. Pos[I] = 0;
  55. continue;
  56. }
  57. // Dot dot slash
  58. if (Pos[I][0] == '.' && Pos[I][1] == '.' && Pos[I][2] == 0)
  59. {
  60. Pos[I] = 0;
  61. unsigned J = I;
  62. for (; Pos[J] == 0 && J != 0; J--);
  63. if (Pos[J] == 0)
  64. return _error->Error("Invalid path, too many ../s");
  65. Pos[J] = 0;
  66. continue;
  67. }
  68. }
  69. // Recombine the path into full path
  70. for (unsigned I = 0; I != CurPos; I++)
  71. {
  72. if (Pos[I] == 0)
  73. continue;
  74. memmove(Buffer,Pos[I],strlen(Pos[I]));
  75. Buffer += strlen(Pos[I]);
  76. if (I + 1 != CurPos)
  77. *Buffer++ = '/';
  78. }
  79. *Buffer = 0;
  80. return true;
  81. }
  82. /*}}}*/
  83. // ResolveLink - Resolve a file into an unsymlinked path /*{{{*/
  84. // ---------------------------------------------------------------------
  85. /* The returned path is a path that accesses the same file without
  86. traversing a symlink, the memory buffer used should be twice as large
  87. as the largest path. It uses an LRU cache of past lookups to speed things
  88. up, just don't change directores :> */
  89. struct Cache
  90. {
  91. string Dir;
  92. string Trans;
  93. unsigned long Age;
  94. };
  95. static Cache DirCache[400];
  96. static unsigned long CacheAge = 0;
  97. bool ResolveLink(char *Buffer,unsigned long Max)
  98. {
  99. if (Buffer[0] == 0 || (Buffer[0] == '/' && Buffer[1] == 0))
  100. return true;
  101. // Lookup in the cache
  102. Cache *Entry = 0;
  103. for (int I = 0; I != 400; I++)
  104. {
  105. // Store an empty entry
  106. if (DirCache[I].Dir.empty() == true)
  107. {
  108. Entry = &DirCache[I];
  109. Entry->Age = 0;
  110. continue;
  111. }
  112. // Store the LRU entry
  113. if (Entry != 0 && Entry->Age > DirCache[I].Age)
  114. Entry = &DirCache[I];
  115. if (DirCache[I].Dir != Buffer || DirCache[I].Trans.empty() == true)
  116. continue;
  117. strcpy(Buffer,DirCache[I].Trans.c_str());
  118. DirCache[I].Age = CacheAge++;
  119. return true;
  120. }
  121. // Prepare the cache for our new entry
  122. if (Entry != 0 && Buffer[strlen(Buffer) - 1] == '/')
  123. {
  124. Entry->Age = CacheAge++;
  125. Entry->Dir = Buffer;
  126. }
  127. else
  128. Entry = 0;
  129. // Resolve any symlinks
  130. unsigned Counter = 0;
  131. while (1)
  132. {
  133. Counter++;
  134. if (Counter > 50)
  135. return _error->Error("Exceeded allowed symlink depth");
  136. // Strip off the final component name
  137. char *I = Buffer + strlen(Buffer);
  138. for (; I != Buffer && (*I == '/' || *I == 0); I--);
  139. for (; I != Buffer && *I != '/'; I--);
  140. if (I != Buffer)
  141. I++;
  142. if (strlen(I) == 0)
  143. break;
  144. /* We need to remove the final slash in the directory component for
  145. readlink to work right */
  146. char *End = 0;
  147. if (I[strlen(I) - 1] == '/')
  148. {
  149. End = I + strlen(I) - 1;
  150. *End = 0;
  151. }
  152. int Res = readlink(Buffer,I,Max - (I - Buffer) - 2);
  153. // If it is a link then read the link dest over the final component
  154. if (Res > 0)
  155. {
  156. I[Res] = 0;
  157. // Absolute path..
  158. if (*I == '/')
  159. memmove(Buffer,I,strlen(I)+1);
  160. // Put the slash back..
  161. if (End != 0)
  162. {
  163. I[Res] = '/';
  164. I[Res + 1] = 0;
  165. }
  166. if (SimplifyPath(Buffer) == false)
  167. return false;
  168. }
  169. else
  170. {
  171. // Put the slash back..
  172. if (End != 0)
  173. *End = '/';
  174. break;
  175. }
  176. }
  177. /* Here we are abusive and move the current path component to the end
  178. of the buffer to advoid allocating space */
  179. char *I = Buffer + strlen(Buffer);
  180. for (; I != Buffer && (*I == '/' || *I == 0); I--);
  181. for (; I != Buffer && *I != '/'; I--);
  182. if (I != Buffer)
  183. I++;
  184. unsigned Len = strlen(I) + 1;
  185. char *End = Buffer + Max - Len;
  186. memmove(End,I,Len);
  187. *I = 0;
  188. // Recurse to deal with any links in the files path
  189. if (ResolveLink(Buffer,Max - Len) == false)
  190. return false;
  191. I = Buffer + strlen(Buffer);
  192. memmove(I,End,Len);
  193. // Store in the cache
  194. if (Entry != 0)
  195. Entry->Trans = Buffer;
  196. return true;
  197. }
  198. /*}}}*/