Fix file system racing and ext2 directory insertion (#58)
[lunaix-os.git] / lunaix-os / kernel / fs / ext2 / file.c
1 #include <lunaix/mm/valloc.h>
2 #include <lunaix/mm/page.h>
3 #include "ext2.h"
4
5 #define blkpos(e_sb, fpos) ((fpos) / (e_sb)->block_size)
6 #define blkoff(e_sb, fpos) ((fpos) % (e_sb)->block_size)
7
8 int
9 ext2_open_inode(struct v_inode* inode, struct v_file* file)
10 {
11     int errno = 0;
12     struct ext2_file* e_file;
13
14     e_file = valloc(sizeof(*e_file));
15     
16     file->data = e_file;
17
18     if (check_directory_node(inode)) {
19         errno = ext2dr_open(inode, file);
20         goto done;
21     }
22
23     // XXX anything for regular file?
24
25 done:
26     if (!errno) {
27         return 0;
28     }
29     
30     vfree(e_file);
31     file->data = NULL;
32     return errno;
33 }
34
35 int
36 ext2_close_inode(struct v_file* file)
37 {
38     ext2ino_update(file->inode);
39
40     if (check_directory_node(file->inode)) {
41         ext2dr_close(file->inode, file);
42     }
43
44     vfree(file->data);
45     file->data = NULL;
46     return 0;
47 }
48
49 int
50 ext2_sync_inode(struct v_inode* inode)
51 {
52     // TODO
53     // a modification to an inode may involves multiple
54     //  blkbuf scattering among different groups.
55     // For now, we just sync everything, until we figure out
56     //  a way to track each dirtied blkbuf w.r.t inode
57     ext2ino_resizing(inode, inode->fsize);
58     blkbuf_syncall(inode->sb->blks, false);
59
60     return 0;
61 }
62
63 int
64 ext2_file_sync(struct v_file* file)
65 {
66     return ext2_sync_inode(file->inode);
67 }
68
69 int
70 ext2_seek_inode(struct v_file* file, size_t offset)
71 {
72     if (check_directory_node(file->inode)) {
73         return ext2dr_seek(file, offset);
74     }
75
76     // nothing to do, seek on file pos handled by vfs
77     return 0;
78 }
79
80 int
81 ext2_inode_read(struct v_inode *inode, 
82                 void *buffer, size_t len, size_t fpos)
83 {
84     struct ext2_sbinfo* e_sb;
85     struct ext2_iterator iter;
86     struct ext2b_inode* b_ino;
87     struct ext2_inode* e_ino;
88     unsigned int off;
89     unsigned int end;
90     unsigned int sz = 0, blksz, movsz;
91     
92     e_sb  = EXT2_SB(inode->sb);
93     e_ino = EXT2_INO(inode);
94     b_ino = e_ino->ino;
95     blksz = e_sb->block_size;
96     end = fpos + len;
97
98     ext2db_itbegin(&iter, inode, DBIT_MODE_ISIZE);
99     ext2db_itffw(&iter, fpos / blksz);
100
101     while (fpos < end && ext2db_itnext(&iter)) {
102         off = fpos % blksz;
103         movsz = MIN(end - fpos, blksz - off);
104         
105         memcpy(buffer, offset(iter.data, off), movsz);
106
107         buffer = offset(buffer, movsz);
108         fpos += movsz;
109         sz += movsz;
110     }
111
112     ext2db_itend(&iter);
113     return itstate_sel(&iter, MIN(sz, e_ino->isize));
114 }
115
116 int
117 ext2_inode_read_page(struct v_inode *inode, void *buffer, size_t fpos)
118 {
119     struct ext2_sbinfo* e_sb;
120     struct ext2_iterator iter;
121     struct ext2_inode*  e_ino;
122     struct ext2b_inode* b_ino;
123     unsigned int blk_start, n, 
124                  transfer_sz, total_sz = 0;
125
126     assert(!va_offset(fpos));
127
128     e_sb = EXT2_SB(inode->sb);
129     e_ino = EXT2_INO(inode);
130     b_ino = e_ino->ino;
131     
132     blk_start = fpos / e_sb->block_size;
133     n = PAGE_SIZE / e_sb->block_size;
134     transfer_sz = MIN(PAGE_SIZE, e_sb->block_size);
135
136     ext2db_itbegin(&iter, inode, DBIT_MODE_ISIZE);
137     ext2db_itffw(&iter, blk_start);
138
139     while (n-- && ext2db_itnext(&iter)) 
140     {
141         memcpy(buffer, iter.data, transfer_sz);
142         buffer = offset(buffer, transfer_sz);
143         total_sz += transfer_sz;
144     }
145     
146     ext2db_itend(&iter);
147     return itstate_sel(&iter, MIN(total_sz, e_ino->isize));
148 }
149
150 int
151 ext2_inode_write(struct v_inode *inode, 
152                  void *buffer, size_t len, size_t fpos)
153 {
154     int errno;
155     unsigned int acc, blk_off, end, size;
156     struct ext2_sbinfo* e_sb;
157     bbuf_t buf;
158
159     e_sb  = EXT2_SB(inode->sb);
160
161     acc = 0;
162     end = fpos + len;
163     while (fpos < end) {
164         errno = ext2db_acquire(inode, blkpos(e_sb, fpos), &buf);
165         if (errno) {
166             return errno;
167         }
168
169         blk_off = blkoff(e_sb, fpos);
170         size = e_sb->block_size - blk_off;
171
172         memcpy(offset(blkbuf_data(buf), blk_off), buffer, size);
173         buffer = offset(buffer, size);
174
175         fsblock_dirty(buf);
176         fsblock_put(buf);
177
178         fpos += size;
179         acc += size;
180     }
181
182     return (int)acc;
183 }
184
185 int
186 ext2_inode_write_page(struct v_inode *inode, void *buffer, size_t fpos)
187 {
188     return ext2_inode_write(inode, buffer, PAGE_SIZE, fpos);
189 }
190
191 #define SYMLNK_INPLACE \
192         sizeof(((struct ext2b_inode*)0)->i_block_arr)
193
194 static inline int
195 __readlink_symlink(struct v_inode *this, char* path)
196 {
197     size_t size;
198     char* link = NULL;
199     int errno;
200     bbuf_t buf;
201     struct ext2_inode* e_ino;
202     
203     e_ino = EXT2_INO(this);
204     size  = e_ino->isize;
205     if (size <= SYMLNK_INPLACE) {
206         link = (char*) e_ino->ino->i_block_arr;
207         strncpy(path, link, size);
208     }
209     else {
210         buf = ext2db_get(this, 0);
211         if (blkbuf_errbuf(buf)) {
212             return EIO;
213         }
214
215         link = blkbuf_data(buf);
216         strncpy(path, link, size);
217
218         fsblock_put(buf);
219     }
220
221     return 0;
222 }
223
224 int
225 ext2_get_symlink(struct v_inode *this, const char **path_out)
226 {
227     int errno;
228     size_t size;
229     char* symlink;
230     struct ext2_inode* e_ino;
231     
232     e_ino = EXT2_INO(this);
233     size  = e_ino->isize;
234
235     if (!size) {
236         return ENOENT;
237     }
238
239     if (!e_ino->symlink) {
240         symlink = valloc(size);
241         if ((errno = __readlink_symlink(this, symlink))) {
242             vfree(symlink);
243             return errno;
244         }
245
246         e_ino->symlink = symlink;
247     }
248
249     *path_out = e_ino->symlink;
250
251     return size;
252 }
253
254 int
255 ext2_set_symlink(struct v_inode *this, const char *target)
256 {
257     int errno = 0;
258     bbuf_t buf = NULL;
259     char* link;
260     size_t size, new_len;
261     struct ext2_inode* e_ino;
262     
263     e_ino = EXT2_INO(this);
264     size = e_ino->isize;
265     new_len = strlen(target);
266
267     if (new_len > this->sb->blksize) {
268         return ENAMETOOLONG;
269     }
270
271     if (size != new_len) {
272         vfree_safe(e_ino->symlink);
273         e_ino->symlink = valloc(new_len);
274     }
275     
276     link = (char*) e_ino->ino->i_block_arr;
277
278     // if new size is shrinked to inplace range
279     if (size > SYMLNK_INPLACE && new_len <= SYMLNK_INPLACE) 
280     {
281         ext2db_free_pos(this, 0);
282     }
283     
284     // if new size is too big to fit inpalce
285     if (new_len > SYMLNK_INPLACE) {
286
287         // repurpose the i_block array back to normal
288         if (size <= SYMLNK_INPLACE) {
289             memset(link, 0, SYMLNK_INPLACE);
290         }
291
292         errno = ext2db_acquire(this, 0, &buf);
293         if (errno) {
294             goto done;
295         }
296
297         link = blkbuf_data(buf);
298     }
299
300     strncpy(e_ino->symlink, target, new_len);
301     strncpy(link, target, new_len);
302
303     ext2ino_update(this);
304     ext2ino_resizing(this, new_len);
305
306     if (buf) {
307         fsblock_put(buf);
308     }
309
310 done:
311     return errno;
312 }