file.c 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257
  1. #include <u.h>
  2. #include <libc.h>
  3. #include <String.h>
  4. #include "ftpfs.h"
  5. enum
  6. {
  7. Chunk= 1024, /* chunk size for buffered data */
  8. Nfile= 128, /* maximum number of cached files */
  9. };
  10. /* a file (with cached data) */
  11. struct File
  12. {
  13. char *mem; /* part of file cached in memory */
  14. ulong len; /* length of cached data */
  15. long off; /* current offset into tpath */
  16. short fd; /* fd to cache file */
  17. char inuse;
  18. char dirty;
  19. ulong atime; /* time of last access */
  20. Node *node;
  21. char *template;
  22. };
  23. static File files[Nfile];
  24. static ulong now;
  25. static int ntmp;
  26. /*
  27. * lookup a file, create one if not found. if there are no
  28. * free files, free the last oldest clean one.
  29. */
  30. static File*
  31. fileget(Node *node)
  32. {
  33. File *fp;
  34. File *oldest;
  35. fp = node->fp;
  36. if(fp)
  37. return fp;
  38. oldest = 0;
  39. for(fp = files; fp < &files[Nfile]; fp++){
  40. if(fp->inuse == 0)
  41. break;
  42. if(fp->dirty == 0 && (oldest == 0 || oldest->atime > fp->atime))
  43. oldest = fp;
  44. }
  45. if(fp == &files[Nfile]){
  46. uncache(oldest->node);
  47. fp = oldest;
  48. }
  49. node->fp = fp;
  50. fp->node = node;
  51. fp->atime = now++;
  52. fp->inuse = 1;
  53. fp->fd = -1;
  54. if(fp->mem){
  55. free(fp->mem);
  56. fp->mem = nil;
  57. }
  58. return fp;
  59. }
  60. /*
  61. * free a cached file
  62. */
  63. void
  64. filefree(Node *node)
  65. {
  66. File *fp;
  67. fp = node->fp;
  68. if(fp == 0)
  69. return;
  70. if(fp->fd > 0){
  71. ntmp--;
  72. close(fp->fd);
  73. remove(fp->template);
  74. free(fp->template);
  75. fp->template = 0;
  76. }
  77. fp->fd = -1;
  78. if(fp->mem){
  79. free(fp->mem);
  80. fp->mem = nil;
  81. }
  82. fp->len = 0;
  83. fp->inuse = 0;
  84. fp->dirty = 0;
  85. node->fp = 0;
  86. }
  87. /*
  88. * satisfy read first from in memory chunk and then from temporary
  89. * file. It's up to the caller to make sure that the file is valid.
  90. */
  91. int
  92. fileread(Node *node, char *a, long off, int n)
  93. {
  94. int sofar;
  95. int i;
  96. File *fp;
  97. fp = node->fp;
  98. if(fp == 0)
  99. fatal("fileread");
  100. if(off + n > fp->len)
  101. n = fp->len - off;
  102. for(sofar = 0; sofar < n; sofar += i, off += i, a += i){
  103. if(off >= fp->len)
  104. return sofar;
  105. if(off < Chunk){
  106. i = n;
  107. if(off + i > Chunk)
  108. i = Chunk - off;
  109. memmove(a, fp->mem + off, i);
  110. continue;
  111. }
  112. if(fp->off != off)
  113. if(seek(fp->fd, off, 0) < 0){
  114. fp->off = -1;
  115. return -1;
  116. }
  117. i = read(fp->fd, a, n-sofar);
  118. if(i < 0){
  119. fp->off = -1;
  120. return -1;
  121. }
  122. if(i == 0)
  123. break;
  124. fp->off = off + i;
  125. }
  126. return sofar;
  127. }
  128. void
  129. uncachedir(Node *parent, Node *child)
  130. {
  131. Node *sp;
  132. if(parent == 0 || parent == child)
  133. return;
  134. for(sp = parent->children; sp; sp = sp->sibs)
  135. if(sp->opens == 0)
  136. if(sp != child)
  137. if(sp->fp != nil)
  138. if(sp->fp->dirty == 0)
  139. if(sp->fp->fd >= 0){
  140. filefree(sp);
  141. UNCACHED(sp);
  142. }
  143. }
  144. static int
  145. createtmp(File *fp)
  146. {
  147. char template[32];
  148. strcpy(template, "/tmp/ftpXXXXXXXXXXX");
  149. mktemp(template);
  150. if(strcmp(template, "/") == 0){
  151. fprint(2, "ftpfs can't create tmp file %s: %r\n", template);
  152. return -1;
  153. }
  154. if(ntmp >= 16)
  155. uncachedir(fp->node->parent, fp->node);
  156. fp->fd = create(template, ORDWR|ORCLOSE, 0600);
  157. fp->template = strdup(template);
  158. fp->off = 0;
  159. ntmp++;
  160. return fp->fd;
  161. }
  162. /*
  163. * write cached data (first Chunk bytes stay in memory)
  164. */
  165. int
  166. filewrite(Node *node, char *a, long off, int n)
  167. {
  168. int i, sofar;
  169. File *fp;
  170. fp = fileget(node);
  171. if(fp->mem == nil){
  172. fp->mem = malloc(Chunk);
  173. if(fp->mem == nil)
  174. return seterr("out of memory");
  175. }
  176. for(sofar = 0; sofar < n; sofar += i, off += i, a += i){
  177. if(off < Chunk){
  178. i = n;
  179. if(off + i > Chunk)
  180. i = Chunk - off;
  181. memmove(fp->mem + off, a, i);
  182. continue;
  183. }
  184. if(fp->fd < 0)
  185. if(createtmp(fp) < 0)
  186. return seterr("can't create temp file");
  187. if(fp->off != off)
  188. if(seek(fp->fd, off, 0) < 0){
  189. fp->off = -1;
  190. return seterr("can't seek temp file");
  191. }
  192. i = write(fp->fd, a, n-sofar);
  193. if(i <= 0){
  194. fp->off = -1;
  195. return seterr("can't write temp file");
  196. }
  197. fp->off = off + i;
  198. }
  199. if(off > fp->len)
  200. fp->len = off;
  201. if(off > node->d->length)
  202. node->d->length = off;
  203. return sofar;
  204. }
  205. /*
  206. * mark a file as dirty
  207. */
  208. void
  209. filedirty(Node *node)
  210. {
  211. File *fp;
  212. fp = fileget(node);
  213. fp->dirty = 1;
  214. }
  215. /*
  216. * mark a file as clean
  217. */
  218. void
  219. fileclean(Node *node)
  220. {
  221. if(node->fp)
  222. node->fp->dirty = 0;
  223. }
  224. int
  225. fileisdirty(Node *node)
  226. {
  227. return node->fp && node->fp->dirty;
  228. }