]> mj.ucw.cz Git - libucw.git/blob - lib/fb-file.c
fastbufs: added some I/O checks
[libucw.git] / lib / fb-file.c
1 /*
2  *      UCW Library -- Fast Buffered I/O on Files
3  *
4  *      (c) 1997--2004 Martin Mares <mj@ucw.cz>
5  *      (c) 2007 Pavel Charvat <pchar@ucw.cz>
6  *
7  *      This software may be freely distributed and used according to the terms
8  *      of the GNU Lesser General Public License.
9  */
10
11 #include "lib/lib.h"
12 #include "lib/fastbuf.h"
13 #include "lib/lfs.h"
14
15 #include <stdio.h>
16 #include <string.h>
17 #include <fcntl.h>
18 #include <unistd.h>
19
20 struct fb_file {
21   struct fastbuf fb;
22   int fd;                               /* File descriptor */
23   int is_temp_file;                     /* 0=normal file, 1=temporary file, delete on close, -1=shared FD */
24   int keep_back_buf;                    /* Optimize for backwards reading */
25   sh_off_t wpos;                        /* Real file position */
26   uns wlen;                             /* Window size */
27 };
28 #define FB_FILE(f) ((struct fb_file *)(f)->is_fastbuf)
29 #define FB_BUFFER(f) (byte *)(FB_FILE(f) + 1)
30
31 static int
32 bfd_refill(struct fastbuf *f)
33 {
34   struct fb_file *F = FB_FILE(f);
35   byte *read_ptr = (f->buffer = FB_BUFFER(f));
36   uns blen = f->bufend - f->buffer, back = F->keep_back_buf ? blen >> 2 : 0, read_len = blen;
37   /* Forward or no seek */
38   if (F->wpos <= f->pos)
39     {
40       sh_off_t diff = f->pos - F->wpos;
41       if (diff > ((sh_off_t)blen << 2)) /* FIXME: Formula for long forward seeks */
42         {
43 long_seek:
44           f->bptr = f->buffer + back;
45           f->bstop = f->buffer + blen;
46           goto seek;
47         }
48       if ((uns)diff < back) /* Reuse part of previous window (also F->wpos == f->pos) */
49         {
50           uns keep = back - (uns)diff;
51           if (keep >= F->wlen)
52             back = diff + (keep = F->wlen);
53           else
54             memmove(f->buffer, f->buffer + F->wlen - keep, keep);
55           read_len -= keep;
56           read_ptr += keep;
57         }
58       else /* Short forward seek */
59         {
60           uns skip = diff - back;
61           F->wpos += skip;
62           while (skip)
63             {
64               int l = read(F->fd, f->buffer, MIN(skip, blen));
65               if (unlikely(l <= 0))
66                 if (l < 0)
67                   die("Error reading %s: %m", f->name);
68                 else
69                   {
70                     F->wpos -= skip;
71                     goto eof;
72                   }
73               skip -= l;
74             }
75         }
76       f->bptr = f->buffer + back;
77       f->bstop = f->buffer + blen;
78     }
79   /* Backwards seek */
80   else
81     {
82       sh_off_t diff = F->wpos - f->pos;
83       if (diff > ((sh_off_t)blen << 1)) /* FIXME: Formula for long backwards seeks */
84         {
85           if ((sh_off_t)back > f->pos)
86             back = f->pos;
87           goto long_seek;
88         }
89       if ((uns)diff <= F->wlen) /* Seek into previous window (for example brewind) */
90         {
91           f->bstop = f->buffer + F->wlen;
92           f->bptr = f->bstop - diff;
93           f->pos = F->wpos;
94           return 1;
95         }
96       back *= 3;
97       if ((sh_off_t)back > f->pos)
98         back = f->pos;
99       f->bptr = f->buffer + back;
100       read_len = back + diff - F->wlen;
101       if (F->wlen && read_len < blen) /* Reuse part of previous window */
102         {
103           uns keep = MIN(F->wlen, blen - read_len);
104           memmove(f->buffer + read_len, f->buffer, keep);
105           f->bstop = f->buffer + read_len + keep;
106         }
107       else
108         f->bstop = f->buffer + (read_len = blen);
109 seek:
110       F->wpos = f->pos + (f->buffer - f->bptr);
111       if (sh_seek(F->fd, F->wpos, SEEK_SET) < 0)
112         die("Error seeking %s: %m", f->name);
113     }
114   do
115     {
116       int l = read(F->fd, read_ptr, read_len);
117       if (unlikely(l < 0))
118         die("Error reading %s: %m", f->name);
119       if (!l)
120         if (unlikely(read_ptr < f->bptr))
121           goto eof;
122         else
123           break; /* Incomplete read because of EOF */
124       read_ptr += l;
125       read_len -= l;
126       F->wpos += l;
127     }
128   while (read_ptr <= f->bptr);
129   if (read_len)
130     f->bstop = read_ptr;
131   f->pos += f->bstop - f->bptr;
132   F->wlen = f->bstop - f->buffer;
133   return f->bstop - f->bptr;
134 eof:
135   /* Seeked behind EOF */
136   f->bptr = f->bstop = f->buffer;
137   F->wlen = 0;
138   return 0;
139 }
140
141 static void
142 bfd_spout(struct fastbuf *f)
143 {
144   if (FB_FILE(f)->wpos != f->pos && sh_seek(FB_FILE(f)->fd, f->pos, SEEK_SET) < 0)
145     die("Error seeking %s: %m", f->name);
146
147   int l = f->bptr - f->buffer;
148   byte *c = f->buffer;
149
150   FB_FILE(f)->wpos = (f->pos += l);
151   FB_FILE(f)->wlen = 0;
152   while (l)
153     {
154       int z = write(FB_FILE(f)->fd, c, l);
155       if (z <= 0)
156         die("Error writing %s: %m", f->name);
157       l -= z;
158       c += z;
159     }
160   f->bptr = f->buffer = FB_BUFFER(f);
161 }
162
163 static int
164 bfd_seek(struct fastbuf *f, sh_off_t pos, int whence)
165 {
166   sh_off_t l;
167   switch (whence)
168     {
169       case SEEK_SET:
170         f->pos = pos;
171         return 1;
172       case SEEK_CUR:
173         l = f->pos + pos;
174         if ((pos > 0) ^ (l > f->pos))
175           return 0;
176         f->pos = l;
177         return 1;
178       case SEEK_END:
179         l = sh_seek(FB_FILE(f)->fd, pos, SEEK_END);
180         if (l < 0)
181           return 0;
182         FB_FILE(f)->wpos = f->pos = l;
183         FB_FILE(f)->wlen = 0;
184         return 1;
185       default:
186         ASSERT(0);
187     }
188 }
189
190 static void
191 bfd_close(struct fastbuf *f)
192 {
193   switch (FB_FILE(f)->is_temp_file)
194     {
195     case 1:
196       if (unlink(f->name) < 0)
197         log(L_ERROR, "unlink(%s): %m", f->name);
198     case 0:
199       if (close(FB_FILE(f)->fd))
200         die("close(%s): %m", f->name);
201     }
202   xfree(f);
203 }
204
205 static int
206 bfd_config(struct fastbuf *f, uns item, int value)
207 {
208   switch (item)
209     {
210       case BCONFIG_IS_TEMP_FILE:
211         FB_FILE(f)->is_temp_file = value;
212         return 0;
213       case BCONFIG_KEEP_BACK_BUF:
214         FB_FILE(f)->keep_back_buf = value;
215         return 0;
216       default:
217         return -1;
218     }
219 }
220
221 struct fastbuf *
222 bfdopen_internal(int fd, byte *name, uns buflen)
223 {
224   ASSERT(buflen);
225   int namelen = strlen(name) + 1;
226   struct fb_file *F = xmalloc_zero(sizeof(struct fb_file) + buflen + namelen);
227   struct fastbuf *f = &F->fb;
228
229   bzero(F, sizeof(*F));
230   f->buffer = (byte *)(F+1);
231   f->bptr = f->bstop = f->buffer;
232   f->bufend = f->buffer + buflen;
233   f->name = f->bufend;
234   memcpy(f->name, name, namelen);
235   F->fd = fd;
236   f->refill = bfd_refill;
237   f->spout = bfd_spout;
238   f->seek = bfd_seek;
239   f->close = bfd_close;
240   f->config = bfd_config;
241   f->can_overwrite_buffer = 2;
242   return f;
243 }
244
245 struct fastbuf *
246 bopen_try(byte *name, uns mode, uns buflen)
247 {
248   return bopen_file_try(name, mode, &(struct fb_params){ .type = FB_STD, .buffer_size = buflen });
249 }
250
251 struct fastbuf *
252 bopen(byte *name, uns mode, uns buflen)
253 {
254   return bopen_file(name, mode, &(struct fb_params){ .type = FB_STD, .buffer_size = buflen });
255 }
256
257 struct fastbuf *
258 bfdopen(int fd, uns buflen)
259 {
260   return bopen_fd(fd, &(struct fb_params){ .type = FB_STD, .buffer_size = buflen });
261 }
262
263 struct fastbuf *
264 bfdopen_shared(int fd, uns buflen)
265 {
266   struct fastbuf *f = bfdopen(fd, buflen);
267   FB_FILE(f)->is_temp_file = -1;
268   return f;
269 }
270
271 void
272 bfilesync(struct fastbuf *b)
273 {
274   bflush(b);
275   if (fsync(FB_FILE(b)->fd) < 0)
276     log(L_ERROR, "fsync(%s) failed: %m", b->name);
277 }
278
279 #ifdef TEST
280
281 int main(int argc UNUSED, char **argv UNUSED)
282 {
283   struct fastbuf *f, *t;
284
285   f = bopen("/etc/profile", O_RDONLY, 16);
286   t = bfdopen(1, 13);
287   bbcopy(f, t, 100);
288   printf("%d %d\n", (int)btell(f), (int)btell(t));
289   bclose(f);
290   bclose(t);
291   return 0;
292 }
293
294 #endif