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