summaryrefslogtreecommitdiff
path: root/minix/lib/libfsdriver/dentry.c
blob: 76d068bec5034d118ad2cb0b32c239231449a334 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99

#include "fsdriver.h"
#include <sys/dirent.h>

/*
 * Initialize a directory entry listing.
 */
void
fsdriver_dentry_init(struct fsdriver_dentry * __restrict dentry,
	const struct fsdriver_data * __restrict data, size_t bytes,
	char * __restrict buf, size_t bufsize)
{

	dentry->data = data;
	dentry->data_size = bytes;
	dentry->data_off = 0;
	dentry->buf = buf;
	dentry->buf_size = bufsize;
	dentry->buf_off = 0;
}

/*
 * Add an entry to a directory entry listing.  Return the entry size if it was
 * added, zero if no more entries could be added and the listing should stop,
 * or an error code in case of an error.
 */
ssize_t
fsdriver_dentry_add(struct fsdriver_dentry * __restrict dentry, ino_t ino_nr,
	const char * __restrict name, size_t namelen, unsigned int type)
{
	struct dirent *dirent;
	size_t len, used;
	int r;

	/* We could do several things here, but it should never happen.. */
	if (namelen > MAXNAMLEN)
		panic("fsdriver: directory entry name excessively long");

	len = _DIRENT_RECLEN(dirent, namelen);

	if (dentry->data_off + dentry->buf_off + len > dentry->data_size) {
		if (dentry->data_off == 0 && dentry->buf_off == 0)
			return EINVAL;

		return 0;
	}

	if (dentry->buf_off + len > dentry->buf_size) {
		if (dentry->buf_off == 0)
			panic("fsdriver: getdents buffer too small");

		if ((r = fsdriver_copyout(dentry->data, dentry->data_off,
		    dentry->buf, dentry->buf_off)) != OK)
			return r;

		dentry->data_off += dentry->buf_off;
		dentry->buf_off = 0;
	}

	dirent = (struct dirent *)&dentry->buf[dentry->buf_off];
	dirent->d_fileno = ino_nr;
	dirent->d_reclen = len;
	dirent->d_namlen = namelen;
	dirent->d_type = type;
	memcpy(dirent->d_name, name, namelen);

	/*
	 * Null-terminate the name, and zero out any alignment bytes after it,
	 * so as not to leak any data.
	 */
	used = _DIRENT_NAMEOFF(dirent) + namelen;
	if (used >= len)
		panic("fsdriver: inconsistency in dirent record");
	memset(&dirent->d_name[namelen], 0, len - used);

	dentry->buf_off += len;

	return len;
}

/*
 * Finish a directory entry listing operation.  Return the total number of
 * bytes copied to the caller, or an error code in case of an error.
 */
ssize_t
fsdriver_dentry_finish(struct fsdriver_dentry *dentry)
{
	int r;

	if (dentry->buf_off > 0) {
		if ((r = fsdriver_copyout(dentry->data, dentry->data_off,
		    dentry->buf, dentry->buf_off)) != OK)
			return r;

		dentry->data_off += dentry->buf_off;
	}

	return dentry->data_off;
}