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