]> git.lizzy.rs Git - plan9front.git/blob - sys/src/cmd/ip/ftpfs/file.c
ip/tftpd: fix %.*s format for homedir path
[plan9front.git] / sys / src / cmd / ip / ftpfs / file.c
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 }