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*
fileget(Node * node)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
filefree(Node * node)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
fileread(Node * node,char * a,long off,int n)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
uncachedir(Node * parent,Node * child)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
createtmp(File * fp)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
filewrite(Node * node,char * a,long off,int n)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
filedirty(Node * node)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
fileclean(Node * node)247 fileclean(Node *node)
248 {
249 if(node->fp)
250 node->fp->dirty = 0;
251 }
252
253 int
fileisdirty(Node * node)254 fileisdirty(Node *node)
255 {
256 return node->fp && node->fp->dirty;
257 }
258