1 /* VTreeFS - file.c - file and directory I/O */
6 #define GETDENTS_BUFSIZ 4096
8 static char *buf
= NULL
;
9 static size_t bufsize
= 0;
12 * Initialize the main buffer used for I/O. Return OK or an error code.
18 /* A default buffer size, for at least getdents. */
19 if (size
< GETDENTS_BUFSIZ
)
20 size
= GETDENTS_BUFSIZ
;
22 if ((buf
= malloc(size
)) == NULL
)
30 * Free up the I/O buffer.
46 fs_read(ino_t ino_nr
, struct fsdriver_data
* data
, size_t bytes
,
47 off_t pos
, int __unused call
)
53 /* Try to get inode by its inode number. */
54 if ((node
= find_inode(ino_nr
)) == NULL
)
57 /* Check whether the node is a regular file. */
58 if (!S_ISREG(node
->i_stat
.mode
))
61 /* For deleted files or with no read hook, feign an empty file. */
62 if (is_inode_deleted(node
) || vtreefs_hooks
->read_hook
== NULL
)
69 * Call the read hook to fill the result buffer, repeatedly for as long
70 * as 1) the request is not yet fully completed, and 2) the read hook
71 * fills the entire buffer.
73 for (off
= 0; off
< bytes
; ) {
74 /* Get the next result chunk by calling the read hook. */
79 len
= vtreefs_hooks
->read_hook(node
, buf
, chunk
, pos
,
80 get_inode_cbdata(node
));
82 /* Copy any resulting data to user space. */
84 r
= fsdriver_copyout(data
, off
, buf
, len
);
86 r
= len
; /* EOF or error */
89 * If an error occurred, but we already produced some output,
90 * return a partial result. Otherwise return the error.
93 return (off
> 0) ? (ssize_t
)off
: r
;
98 if ((size_t)len
< bufsize
)
109 fs_write(ino_t ino_nr
, struct fsdriver_data
* data
, size_t bytes
, off_t pos
,
116 if ((node
= find_inode(ino_nr
)) == NULL
)
119 if (!S_ISREG(node
->i_stat
.mode
))
122 if (is_inode_deleted(node
) || vtreefs_hooks
->write_hook
== NULL
)
132 * Call the write hook to process the incoming data, repeatedly for as
133 * long as 1) the request is not yet fully completed, and 2) the write
134 * hook processes at least some of the given data.
136 for (off
= 0; off
< bytes
; ) {
141 /* Copy the data from user space. */
142 r
= fsdriver_copyin(data
, off
, buf
, chunk
);
144 /* Call the write hook for the chunk. */
146 r
= vtreefs_hooks
->write_hook(node
, buf
, chunk
, pos
,
147 get_inode_cbdata(node
));
150 * If an error occurred, but we already processed some input,
151 * return a partial result. Otherwise return the error.
154 return (off
> 0) ? (ssize_t
)off
: r
;
170 fs_trunc(ino_t ino_nr
, off_t start_pos
, off_t end_pos
)
174 if ((node
= find_inode(ino_nr
)) == NULL
)
177 if (!S_ISREG(node
->i_stat
.mode
))
180 if (is_inode_deleted(node
) || vtreefs_hooks
->trunc_hook
== NULL
)
183 /* TODO: translate this case into all-zeroes write callbacks. */
187 return vtreefs_hooks
->trunc_hook(node
, start_pos
,
188 get_inode_cbdata(node
));
192 * Retrieve directory entries.
195 fs_getdents(ino_t ino_nr
, struct fsdriver_data
* data
, size_t bytes
,
198 struct fsdriver_dentry fsdentry
;
199 struct inode
*node
, *child
;
202 int r
, skip
, get_next
, indexed
;
204 if (*posp
>= ULONG_MAX
)
207 if ((node
= find_inode(ino_nr
)) == NULL
)
210 indexed
= node
->i_indexed
;
214 /* Call the getdents hook, if any, to "refresh" the directory. */
215 if (!is_inode_deleted(node
) && vtreefs_hooks
->getdents_hook
!= NULL
) {
216 r
= vtreefs_hooks
->getdents_hook(node
, get_inode_cbdata(node
));
224 fsdriver_dentry_init(&fsdentry
, data
, bytes
, buf
, bufsize
);
227 /* Determine which inode and name to use for this entry. */
234 } else if (pos
== 1) {
235 /* The ".." entry. */
236 child
= get_parent_inode(node
);
240 } else if (pos
- 2 < indexed
) {
241 /* All indexed entries. */
242 child
= get_inode_by_index(node
, pos
- 2);
245 * If there is no inode with this particular index,
246 * continue with the next index number.
248 if (child
== NULL
) continue;
250 name
= child
->i_name
;
252 /* All non-indexed entries. */
254 * If this is the first loop iteration, first get to
255 * the non-indexed child identified by the current
258 if (get_next
== FALSE
) {
259 skip
= pos
- indexed
- 2;
260 child
= get_first_inode(node
);
262 /* Skip indexed children. */
263 while (child
!= NULL
&&
264 child
->i_index
!= NO_INDEX
)
265 child
= get_next_inode(child
);
267 /* Skip to the right position. */
268 while (child
!= NULL
&& skip
-- > 0)
269 child
= get_next_inode(child
);
273 child
= get_next_inode(child
);
275 /* No more children? Then stop. */
279 assert(!is_inode_deleted(child
));
281 name
= child
->i_name
;
284 /* Add the directory entry to the output. */
285 r
= fsdriver_dentry_add(&fsdentry
,
286 (ino_t
)get_inode_number(child
), name
, strlen(name
),
287 IFTODT(child
->i_stat
.mode
));
294 return fsdriver_dentry_finish(&fsdentry
);