]> mj.ucw.cz Git - libucw.git/blob - ucw/fb-atomic.c
Set CONFIG_URL_ESCAPE_COMPAT flag
[libucw.git] / ucw / fb-atomic.c
1 /*
2  *      UCW Library -- Atomic Buffered Write to Files
3  *
4  *      (c) 2006 Martin Mares <mj@ucw.cz>
5  *
6  *      This software may be freely distributed and used according to the terms
7  *      of the GNU Lesser General Public License.
8  */
9
10 #include "ucw/lib.h"
11 #include "ucw/fastbuf.h"
12 #include "ucw/lfs.h"
13 #include "ucw/conf.h"
14
15 #include <string.h>
16 #include <fcntl.h>
17 #include <unistd.h>
18
19 static uns trace;
20
21 #ifndef TEST
22
23 static struct cf_section fbatomic_config = {
24   CF_ITEMS {
25     CF_UNS("Trace", &trace)
26   }
27 };
28
29 static void CONSTRUCTOR fbatomic_init_config(void)
30 {
31   cf_declare_section("FBAtomic", &fbatomic_config, 1);
32 }
33
34 #endif
35
36 #define TRACE(m...) do { if(trace) msg(L_DEBUG, "FB_ATOMIC: " m); } while(0)
37
38 struct fb_atomic_file {
39   int fd;
40   int use_count;
41   int record_len;
42   uns locked;
43   byte name[1];
44 };
45
46 void
47 fbatomic_internal_write(struct fastbuf *f)
48 {
49   struct fb_atomic_file *af = FB_ATOMIC(f)->af;
50   int size = f->bptr - f->buffer;
51   if (size)
52     {
53       ASSERT(af->record_len < 0 || !(size % af->record_len));
54       int res = write(af->fd, f->buffer, size);
55       if (res < 0)
56         die("Error writing %s: %m", f->name);
57       if (res != size)
58         die("Unexpected partial write to %s: written only %d bytes of %d", f->name, res, size);
59       f->bptr = f->buffer;
60     }
61 }
62
63 static void
64 fbatomic_spout(struct fastbuf *f)
65 {
66   if (f->bptr < f->bufend)              /* Explicit flushes should be ignored */
67     return;
68
69   struct fb_atomic *F = FB_ATOMIC(f);
70   if (F->af->locked)
71     {
72       uns written = f->bptr - f->buffer;
73       uns size = f->bufend - f->buffer + F->slack_size;
74       F->slack_size *= 2;
75       TRACE("Reallocating buffer for atomic file %s with slack %d", f->name, F->slack_size);
76       f->buffer = xrealloc(f->buffer, size);
77       f->bufend = f->buffer + size;
78       f->bptr = f->buffer + written;
79       F->expected_max_bptr = f->bufend - F->slack_size;
80     }
81   else
82     fbatomic_internal_write(f);
83 }
84
85 static void
86 fbatomic_close(struct fastbuf *f)
87 {
88   struct fb_atomic_file *af = FB_ATOMIC(f)->af;
89   fbatomic_internal_write(f);   /* Need to flush explicitly, because the file can be locked */
90   if (!--af->use_count)
91     {
92       close(af->fd);
93       xfree(af);
94     }
95   xfree(f);
96 }
97
98 struct fastbuf *
99 fbatomic_open(const char *name, struct fastbuf *master, uns bufsize, int record_len)
100 {
101   struct fb_atomic *F = xmalloc_zero(sizeof(*F));
102   struct fastbuf *f = &F->fb;
103   struct fb_atomic_file *af;
104   if (master)
105     {
106       af = FB_ATOMIC(master)->af;
107       af->use_count++;
108       ASSERT(af->record_len == record_len);
109     }
110   else
111     {
112       af = xmalloc_zero(sizeof(*af) + strlen(name));
113       if ((af->fd = ucw_open(name, O_WRONLY | O_CREAT | O_TRUNC | O_APPEND, 0666)) < 0)
114         die("Cannot create %s: %m", name);
115       af->use_count = 1;
116       af->record_len = record_len;
117       af->locked = (record_len < 0);
118       strcpy(af->name, name);
119     }
120   F->af = af;
121   if (record_len > 0 && bufsize % record_len)
122     bufsize += record_len - (bufsize % record_len);
123   f->buffer = xmalloc(bufsize);
124   f->bufend = f->buffer + bufsize;
125   F->slack_size = (record_len < 0) ? -record_len : 0;
126   ASSERT(bufsize > F->slack_size);
127   F->expected_max_bptr = f->bufend - F->slack_size;
128   f->bptr = f->bstop = f->buffer;
129   f->name = af->name;
130   f->spout = fbatomic_spout;
131   f->close = fbatomic_close;
132   return f;
133 }
134
135 #ifdef TEST
136
137 int main(int argc UNUSED, char **argv UNUSED)
138 {
139   struct fastbuf *f, *g;
140
141   // Always trace in the test
142   trace = 1;
143
144   msg(L_INFO, "Testing block writes");
145   f = fbatomic_open("test", NULL, 16, 4);
146   for (u32 i=0; i<17; i++)
147     bwrite(f, &i, 4);
148   bclose(f);
149
150   msg(L_INFO, "Testing interleaved var-size writes");
151   f = fbatomic_open("test2", NULL, 23, -5);
152   g = fbatomic_open("test2", f, 23, -5);
153   for (int i=0; i<100; i++)
154     {
155       struct fastbuf *x = (i%2) ? g : f;
156       bprintf(x, "%c<%d>\n", "fg"[i%2], ((259309*i) % 1000000) >> (i % 8));
157       fbatomic_commit(x);
158     }
159   bclose(f);
160   bclose(g);
161
162   return 0;
163 }
164
165 #endif