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