xref: /plan9/sys/src/cmd/ip/ftpfs/file.c (revision 9a747e4fd48b9f4522c70c07e8f882a15030f964)
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