Landin library reference source

platform/posix/linux.ldn

1--  platform/posix on Linux, over platform/linux's system calls.  A call's
2--  failure is minus its errno in its result, so the errno is read from the
3--  result itself and no cell is shared between threads.  The same calls
4--  serve a program linked with --link=libc, whose C library this module
5--  never calls.
6
7import core/text
8import platform/linux
9
10fixed if compiler.os == linux then
11    --  The flags `openat` takes for `options`.
12    flags_of: (options: open_options) -> (flags: i32) =
13        flags = match options.access
14            read_only: linux.o_rdonly
15            write_only: linux.o_wronly
16            read_write: linux.o_rdwr
17        end match
18        if options.create then flags = flags | linux.o_creat end if
19        if options.exclusive then flags = flags | linux.o_excl end if
20        if options.truncate then flags = flags | linux.o_trunc end if
21        if options.append then flags = flags | linux.o_append end if
22        if options.directory then flags = flags | linux.o_directory end if
23    end flags_of
24
25    system_open: (at: descriptor, path: ptr u8, options: open_options,
26                  permissions: u32) -> (opened: descriptor) ! errno =
27        result: i32 = linux.openat(i32(at), path, flags_of(options),
28                                   permissions)
29        fail errno_of(-result) when result < 0
30        opened = descriptor(result)
31    end system_open
32
33    system_read: (source: descriptor, into: []mut u8)
34                 -> (count: usize) ! errno =
35        mut spare: u8 = 0
36        base: ptr mut u8 = if lenof into > 0 then addr into[0]
37                           else addr spare end if
38        result: isize = linux.read(i32(source), base, lenof into)
39        fail errno_of(i32(-result)) when result < 0
40        count = usize(result)
41    end system_read
42
43    system_write: (to: descriptor, bytes: []u8) -> (count: usize) ! errno =
44        spare: u8 = 0
45        base: ptr u8 = if lenof bytes > 0 then addr bytes[0]
46                       else addr spare end if
47        result: isize = linux.write(i32(to), base, lenof bytes)
48        fail errno_of(i32(-result)) when result < 0
49        count = usize(result)
50    end system_write
51
52    system_close: (closing: descriptor) -> none ! errno =
53        result: i32 = linux.close(i32(closing))
54        fail errno_of(-result) when result < 0
55    end system_close
56
57    --  statx's record is the kernel's own on every architecture, so it
58    --  needs no layout check.
59    system_status: (at: descriptor, path: ptr u8, follow: bool)
60                   -> (result: status) ! errno =
61        mut record: linux.file_status = zeroed
62        flags: i32 = if follow then 0 else linux.at_symlink_nofollow end if
63        outcome: i32 = linux.statx(i32(at), path, flags,
64                                   linux.statx_type | linux.statx_ino
65                                   | linux.statx_size,
66                                   addr record)
67        fail errno_of(-outcome) when outcome < 0
68        result = (device: (u64(record.device_major) << 32)
69                           | u64(record.device_minor),
70                  inode: record.inode, kind: kind_of_mode(u32(record.mode)),
71                  size: record.size)
72    end system_status
73
74    system_open_directory: (at: descriptor, path: ptr u8)
75                           -> (opened: directory_stream) ! errno =
76        result: i32 = linux.openat(i32(at), path,
77                                   linux.o_rdonly | linux.o_directory, 0)
78        fail errno_of(-result) when result < 0
79        opened = (descriptor: descriptor(result), stream: 0, filled: 0,
80                  cursor: 0, finished: false)
81    end system_open_directory
82
83    --  A record's little-endian u16 at `at`.
84    record_u16: (bytes: []u8, at: usize) -> (value: usize) =
85        value = usize(bytes[at]) | (usize(bytes[at + 1]) << 8)
86    end record_u16
87
88    --  The next record in the buffer, refilling it from getdents64 when
89    --  every record in it has been read.  A record is checked to lie
90    --  within what the kernel filled before any byte of it is read, and
91    --  its name to end within it, so a buffer's last record is never read
92    --  past; one that does not is `unlisted`, which no errno describes.
93    system_read_directory: (inout opened: directory_stream, buffer: []mut u8)
94                           -> (item: directory_entry from buffer) ! errno =
95        loop do
96            if opened.cursor >= opened.filled then
97                if opened.finished then
98                    item = (name: text.host_over(buffer[0..<0]),
99                            kind: unknown_kind)
100                    return
101                end if
102                got: isize = linux.getdents64(i32(opened.descriptor),
103                                              addr buffer[0], lenof buffer)
104                fail errno_of(i32(-got)) when got < 0
105                fail unlisted when usize(got) > lenof buffer
106                if got == 0 then
107                    opened.finished = true
108                else
109                    opened.filled = usize(got)
110                    opened.cursor = 0
111                end if
112            else
113                at: usize = opened.cursor
114                --  The fixed part is 19 bytes and the name at least one
115                --  byte and its NUL.
116                fail unlisted when opened.filled - at < 21
117                length: usize = record_u16(buffer, at + 16)
118                fail unlisted when length < 21
119                  or length > opened.filled - at
120                opened.cursor = at + length
121                mut stop: usize = at + 19
122                while stop < at + length and buffer[stop] <> 0 do
123                    inc stop
124                end while
125                fail unlisted when stop == at + length
126                item = (name: text.host_over(buffer[at + 19..<stop]),
127                        kind: kind_of_type(buffer[at + 18]))
128                return
129            end if
130        end loop
131    end system_read_directory
132
133    system_close_directory: (opened: directory_stream) -> none ! errno =
134        try system_close(opened.descriptor)
135    end system_close_directory
136
137    system_make_directory: (at: descriptor, path: ptr u8, permissions: u32)
138                           -> none ! errno =
139        result: i32 = linux.mkdirat(i32(at), path, permissions)
140        fail errno_of(-result) when result < 0
141    end system_make_directory
142
143    system_remove: (at: descriptor, path: ptr u8, directory: bool)
144                   -> none ! errno =
145        result: i32 = linux.unlinkat(i32(at), path,
146                                     if directory then linux.at_removedir
147                                     else 0 end if)
148        fail errno_of(-result) when result < 0
149    end system_remove
150
151    system_map: (length: usize) -> (base: ptr mut u8) ! errno =
152        result: i64 = linux.mmap(0, length, linux.prot_read | linux.prot_write,
153                                 linux.map_private | linux.map_anonymous, -1,
154                                 0)
155        fail errno_of(i32(-result)) when result < 0
156        base = ptr(usize(result))
157    end system_map
158
159    system_unmap: (base: ptr mut u8, length: usize) -> none ! errno =
160        result: i32 = linux.munmap(usize(base), length)
161        fail errno_of(-result) when result < 0
162    end system_unmap
163
164    system_page_size: () -> (bytes: usize) =
165        bytes = linux.page_size()
166    end system_page_size
167
168    system_exit: (status: i32) -> noreturn =
169        linux.exit_group(status)
170    end system_exit
171
172    --  arm64 and RV64 have no `poll` call, so every architecture makes
173    --  `ppoll`, with the milliseconds as its time limit.
174    system_poll: (entries: []mut poll_entry, milliseconds: i32)
175                 -> (ready: usize) ! errno =
176        mut spare: linux.poll_record = zeroed
177        records: ptr mut linux.poll_record =
178            if lenof entries > 0 then ptr(usize(addr entries[0]))
179            else addr spare end if
180        mut limit: linux.timespec = zeroed
181        limit.seconds = i64(milliseconds / 1000)
182        limit.nanoseconds = i64(milliseconds % 1000) * 1000000
183        bound: linux.poll_limit = if milliseconds < 0 then linux.no_limit
184                                  else addr limit end if
185        result: i32 = linux.ppoll(records, lenof entries, bound)
186        fail errno_of(-result) when result < 0
187        ready = usize(result)
188    end system_poll
189end if