forked from ish-app/ish
-
Notifications
You must be signed in to change notification settings - Fork 11
/
dir.c
118 lines (101 loc) · 3.4 KB
/
dir.c
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
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
#include <sys/stat.h>
#include <string.h>
#include "kernel/calls.h"
#include "kernel/errno.h"
#include "kernel/fs.h"
#include "fs/fd.h"
static unsigned long fd_telldir(struct fd *fd) {
unsigned long off = fd->offset;
if (fd->ops->telldir)
off = fd->ops->telldir(fd);
return off;
}
static void fd_seekdir(struct fd *fd, unsigned long off) {
fd->offset = off;
if (fd->ops->seekdir)
fd->ops->seekdir(fd, off);
}
struct linux_dirent_ {
dword_t inode;
dword_t offset;
word_t reclen;
char name[];
} __attribute__((packed));
struct linux_dirent64_ {
qword_t inode;
qword_t offset;
word_t reclen;
byte_t type;
char name[];
} __attribute__((packed));
size_t fill_dirent_32(void *dirent_data, ino_t inode, off_t_ offset, const char *name, int type) {
struct linux_dirent_ *dirent = dirent_data;
dirent->inode = inode;
dirent->offset = offset;
const size_t namelen = strlen(name) + 1; // Include null terminator
dirent->reclen = offsetof(struct linux_dirent_, name) + namelen + 1; // name, null terminator, type
memcpy(dirent->name, name, namelen);
*((char *) dirent + dirent->reclen - 1) = type;
return dirent->reclen;
}
size_t fill_dirent_64(void *dirent_data, ino_t inode, off_t_ offset, const char *name, int type) {
struct linux_dirent64_ *dirent = dirent_data;
dirent->inode = inode;
dirent->offset = offset;
const size_t namelen = strlen(name) + 1; // Include null terminator
dirent->reclen = offsetof(struct linux_dirent64_, name) + namelen; // name, null terminator
dirent->type = type;
memcpy(dirent->name, name, namelen);
return dirent->reclen;
}
int_t sys_getdents_common(fd_t f, addr_t dirents, dword_t count,
size_t (*fill_dirent)(void *, ino_t, off_t_, const char *, int)) {
STRACE("getdents(%d, %#x, %#x)", f, dirents, count);
struct fd *fd = f_get(f);
if (fd == NULL)
return _EBADF;
if (!S_ISDIR(fd->type) || fd->ops->readdir == NULL)
return _ENOTDIR;
dword_t orig_count = count;
long ptr;
int err;
int printed = 0;
while (true) {
ptr = fd_telldir(fd);
extern time_t boot_time;
fd->stat.atime = (dword_t)boot_time;
struct dir_entry entry;
err = fd->ops->readdir(fd, &entry);
if (err < 0)
return err;
if (err == 0)
break;
size_t max_reclen = sizeof(struct linux_dirent64_) + strlen(entry.name) + 4;
char dirent_data[max_reclen];
dirent_data[0] = 0;
ino_t inode = entry.inode;
off_t_ offset = fd_telldir(fd);
const char *name = entry.name;
int type = 0;
size_t reclen = fill_dirent(dirent_data, inode, offset, name, type);
if (printed < 20) {
STRACE(" {inode=%d, offset=%d, name=%s, type=%d, reclen=%d}",
inode, offset, name, type, reclen);
printed++;
}
if (reclen > count)
break;
if (user_write(dirents, dirent_data, reclen))
return _EFAULT;
dirents += reclen;
count -= reclen;
}
fd_seekdir(fd, ptr);
return orig_count - count;
}
int_t sys_getdents(fd_t f, addr_t dirents, uint_t count) {
return sys_getdents_common(f, dirents, count, fill_dirent_32);
}
int_t sys_getdents64(fd_t f, addr_t dirents, uint_t count) {
return sys_getdents_common(f, dirents, count, fill_dirent_64);
}