1const std = @import("std");
2const uefi = std.os.uefi;
3const Guid = uefi.Guid;
4const Time = uefi.Time;
5const Status = uefi.Status;
6const cc = uefi.cc;
7
8pub const File = extern struct {
9 revision: u64,
10 _open: *const fn (*const File, **File, [*:0]const u16, OpenMode, Attributes) callconv(cc) Status,
11 _close: *const fn (*File) callconv(cc) Status,
12 _delete: *const fn (*File) callconv(cc) Status,
13 _read: *const fn (*File, *usize, [*]u8) callconv(cc) Status,
14 _write: *const fn (*File, *usize, [*]const u8) callconv(cc) Status,
15 _get_position: *const fn (*const File, *u64) callconv(cc) Status,
16 _set_position: *const fn (*File, u64) callconv(cc) Status,
17 _get_info: *const fn (*const File, *align(8) const Guid, *usize, ?[*]u8) callconv(cc) Status,
18 _set_info: *const fn (*File, *align(8) const Guid, usize, [*]const u8) callconv(cc) Status,
19 _flush: *const fn (*File) callconv(cc) Status,
20
21 pub const OpenError = uefi.UnexpectedError || error{
22 NotFound,
23 NoMedia,
24 MediaChanged,
25 DeviceError,
26 VolumeCorrupted,
27 WriteProtected,
28 AccessDenied,
29 OutOfResources,
30 VolumeFull,
31 InvalidParameter,
32 };
33 pub const CloseError = uefi.UnexpectedError;
34 pub const SeekError = uefi.UnexpectedError || error{
35 Unsupported,
36 DeviceError,
37 };
38 pub const ReadError = uefi.UnexpectedError || error{
39 NoMedia,
40 DeviceError,
41 VolumeCorrupted,
42 BufferTooSmall,
43 };
44 pub const WriteError = uefi.UnexpectedError || error{
45 Unsupported,
46 NoMedia,
47 DeviceError,
48 VolumeCorrupted,
49 WriteProtected,
50 AccessDenied,
51 VolumeFull,
52 };
53 pub const GetInfoSizeError = uefi.UnexpectedError || error{
54 Unsupported,
55 NoMedia,
56 DeviceError,
57 VolumeCorrupted,
58 };
59 pub const GetInfoError = GetInfoSizeError || error{
60 BufferTooSmall,
61 };
62 pub const SetInfoError = uefi.UnexpectedError || error{
63 Unsupported,
64 NoMedia,
65 DeviceError,
66 VolumeCorrupted,
67 WriteProtected,
68 AccessDenied,
69 VolumeFull,
70 BadBufferSize,
71 };
72 pub const FlushError = uefi.UnexpectedError || error{
73 DeviceError,
74 VolumeCorrupted,
75 WriteProtected,
76 AccessDenied,
77 VolumeFull,
78 };
79
80 pub fn open(
81 self: *const File,
82 file_name: [*:0]const u16,
83 mode: OpenMode,
84 create_attributes: Attributes,
85 ) OpenError!*File {
86 var new: *File = undefined;
87 switch (self._open(
88 self,
89 &new,
90 file_name,
91 mode,
92 create_attributes,
93 )) {
94 .success => return new,
95 .not_found => return error.NotFound,
96 .no_media => return error.NoMedia,
97 .media_changed => return error.MediaChanged,
98 .device_error => return error.DeviceError,
99 .volume_corrupted => return error.VolumeCorrupted,
100 .write_protected => return error.WriteProtected,
101 .access_denied => return error.AccessDenied,
102 .out_of_resources => return error.OutOfResources,
103 .volume_full => return error.VolumeFull,
104 .invalid_parameter => return error.InvalidParameter,
105 else => |status| return uefi.unexpectedStatus(status),
106 }
107 }
108
109 pub fn close(self: *File) CloseError!void {
110 switch (self._close(self)) {
111 .success => {},
112 else => |status| return uefi.unexpectedStatus(status),
113 }
114 }
115
116 /// Delete the file.
117 ///
118 /// Returns true if the file was deleted, false if the file was not deleted, which is a warning
119 /// according to the UEFI specification.
120 pub fn delete(self: *File) uefi.UnexpectedError!bool {
121 switch (self._delete(self)) {
122 .success => return true,
123 .warn_delete_failure => return false,
124 else => |status| return uefi.unexpectedStatus(status),
125 }
126 }
127
128 pub fn readSize(self: *File) ReadError!usize {
129 const zerobuf: [0]u8 = undefined;
130 var size: usize = 0;
131 switch (self._read(self, &size, &zerobuf)) {
132 .success, .buffer_too_small => return size,
133 .no_media => return error.NoMedia,
134 .device_error => return error.DeviceError,
135 .volume_corrupted => return error.VolumeCorrupted,
136 else => |status| return uefi.unexpectedStatus(status),
137 }
138 }
139
140 /// If `self` is a directory entry and `buffer` is too small to contain the
141 /// next entry, this function returns `Error.BufferTooSmall`. You can call
142 /// `readSize` before or after to determine how big the buffer should be to
143 /// call this function.
144 pub fn read(self: *File, buffer: []u8) ReadError!usize {
145 var size: usize = buffer.len;
146 switch (self._read(self, &size, buffer.ptr)) {
147 .success => return size,
148 .no_media => return error.NoMedia,
149 .device_error => return error.DeviceError,
150 .volume_corrupted => return error.VolumeCorrupted,
151 .buffer_too_small => return error.BufferTooSmall,
152 else => |status| return uefi.unexpectedStatus(status),
153 }
154 }
155
156 pub fn write(self: *File, buffer: []const u8) WriteError!usize {
157 var size: usize = buffer.len;
158 switch (self._write(self, &size, buffer.ptr)) {
159 .success => return size,
160 .unsupported => return error.Unsupported,
161 .no_media => return error.NoMedia,
162 .device_error => return error.DeviceError,
163 .volume_corrupted => return error.VolumeCorrupted,
164 .write_protected => return error.WriteProtected,
165 .access_denied => return error.AccessDenied,
166 .volume_full => return error.VolumeFull,
167 else => |status| return uefi.unexpectedStatus(status),
168 }
169 }
170
171 pub fn getPosition(self: *const File) SeekError!u64 {
172 var position: u64 = undefined;
173 switch (self._get_position(self, &position)) {
174 .success => return position,
175 .unsupported => return error.Unsupported,
176 .device_error => return error.DeviceError,
177 else => |status| return uefi.unexpectedStatus(status),
178 }
179 }
180
181 pub fn setPosition(self: *File, position: u64) SeekError!void {
182 switch (self._set_position(self, position)) {
183 .success => {},
184 .unsupported => return error.Unsupported,
185 .device_error => return error.DeviceError,
186 else => |status| return uefi.unexpectedStatus(status),
187 }
188 }
189
190 fn seekBy(self: *File, offset: i64) SeekError!void {
191 var pos = try self.getPosition();
192 const seek_back = offset < 0;
193 const amt = @abs(offset);
194 if (seek_back) {
195 pos += amt;
196 } else {
197 pos -= amt;
198 }
199 try self.setPosition(pos);
200 }
201
202 pub fn getInfoSize(self: *const File, comptime info: std.meta.Tag(Info)) GetInfoError!usize {
203 const InfoType = @FieldType(Info, @tagName(info));
204
205 var len: usize = 0;
206 switch (self._get_info(self, &InfoType.guid, &len, null)) {
207 .success, .buffer_too_small => return len,
208 .unsupported => return error.Unsupported,
209 .no_media => return error.NoMedia,
210 .device_error => return error.DeviceError,
211 .volume_corrupted => return error.VolumeCorrupted,
212 else => |status| return uefi.unexpectedStatus(status),
213 }
214 }
215
216 /// If `buffer` is too small to contain all of the info, this function returns
217 /// `Error.BufferTooSmall`. You should call `getInfoSize` first to determine
218 /// how big the buffer should be to safely call this function.
219 pub fn getInfo(
220 self: *const File,
221 comptime info: std.meta.Tag(Info),
222 buffer: []align(@alignOf(@FieldType(Info, @tagName(info)))) u8,
223 ) GetInfoError!*@FieldType(Info, @tagName(info)) {
224 const InfoType = @FieldType(Info, @tagName(info));
225
226 var len = buffer.len;
227 switch (self._get_info(
228 self,
229 &InfoType.guid,
230 &len,
231 buffer.ptr,
232 )) {
233 .success => return @as(*InfoType, @ptrCast(buffer.ptr)),
234 .buffer_too_small => return error.BufferTooSmall,
235 .unsupported => return error.Unsupported,
236 .no_media => return error.NoMedia,
237 .device_error => return error.DeviceError,
238 .volume_corrupted => return error.VolumeCorrupted,
239 else => |status| return uefi.unexpectedStatus(status),
240 }
241 }
242
243 pub fn setInfo(
244 self: *File,
245 comptime info: std.meta.Tag(Info),
246 data: *const @FieldType(Info, @tagName(info)),
247 ) SetInfoError!void {
248 const InfoType = @FieldType(Info, @tagName(info));
249
250 const attached_str: [*:0]const u16 = switch (info) {
251 .file => data.getFileName(),
252 .file_system, .volume_label => data.getVolumeLabel(),
253 };
254 const attached_str_len = std.mem.sliceTo(attached_str, 0).len;
255
256 // add the length (not +1 for sentinel) because `@sizeOf(InfoType)`
257 // already contains the first utf16 char
258 const len = @sizeOf(InfoType) + (attached_str_len * 2);
259
260 switch (self._set_info(self, &InfoType.guid, len, @ptrCast(data))) {
261 .success => {},
262 .unsupported => return error.Unsupported,
263 .no_media => return error.NoMedia,
264 .device_error => return error.DeviceError,
265 .volume_corrupted => return error.VolumeCorrupted,
266 .write_protected => return error.WriteProtected,
267 .access_denied => return error.AccessDenied,
268 .volume_full => return error.VolumeFull,
269 .bad_buffer_size => return error.BadBufferSize,
270 else => |status| return uefi.unexpectedStatus(status),
271 }
272 }
273
274 pub fn flush(self: *File) FlushError!void {
275 switch (self._flush(self)) {
276 .success => {},
277 .device_error => return error.DeviceError,
278 .volume_corrupted => return error.VolumeCorrupted,
279 .write_protected => return error.WriteProtected,
280 .access_denied => return error.AccessDenied,
281 .volume_full => return error.VolumeFull,
282 else => |status| return uefi.unexpectedStatus(status),
283 }
284 }
285
286 pub const OpenMode = enum(u64) {
287 pub const Bits = packed struct(u64) {
288 // 0x0000000000000001
289 read: bool = false,
290 // 0x0000000000000002
291 write: bool = false,
292 _pad: u61 = 0,
293 // 0x8000000000000000
294 create: bool = false,
295 };
296
297 read = @bitCast(Bits{ .read = true }),
298 read_write = @bitCast(Bits{ .read = true, .write = true }),
299 read_write_create = @bitCast(Bits{ .read = true, .write = true, .create = true }),
300 };
301
302 pub const Attributes = packed struct(u64) {
303 // 0x0000000000000001
304 read_only: bool = false,
305 // 0x0000000000000002
306 hidden: bool = false,
307 // 0x0000000000000004
308 system: bool = false,
309 // 0x0000000000000008
310 reserved: bool = false,
311 // 0x0000000000000010
312 directory: bool = false,
313 // 0x0000000000000020
314 archive: bool = false,
315 _pad: u58 = 0,
316 };
317
318 pub const Info = union(enum) {
319 file: Info.File,
320 file_system: FileSystem,
321 volume_label: VolumeLabel,
322
323 pub const File = extern struct {
324 size: u64,
325 file_size: u64,
326 physical_size: u64,
327 create_time: Time,
328 last_access_time: Time,
329 modification_time: Time,
330 attribute: Attributes,
331 _file_name: u16,
332
333 pub fn getFileName(self: *const Info.File) [*:0]const u16 {
334 return @as([*:0]const u16, @ptrCast(&self._file_name));
335 }
336
337 pub const guid align(8) = Guid{
338 .time_low = 0x09576e92,
339 .time_mid = 0x6d3f,
340 .time_high_and_version = 0x11d2,
341 .clock_seq_high_and_reserved = 0x8e,
342 .clock_seq_low = 0x39,
343 .node = [_]u8{ 0x00, 0xa0, 0xc9, 0x69, 0x72, 0x3b },
344 };
345 };
346
347 pub const FileSystem = extern struct {
348 size: u64,
349 read_only: bool,
350 volume_size: u64,
351 free_space: u64,
352 block_size: u32,
353 _volume_label: u16,
354
355 pub fn getVolumeLabel(self: *const FileSystem) [*:0]const u16 {
356 return @as([*:0]const u16, @ptrCast(&self._volume_label));
357 }
358
359 pub const guid align(8) = Guid{
360 .time_low = 0x09576e93,
361 .time_mid = 0x6d3f,
362 .time_high_and_version = 0x11d2,
363 .clock_seq_high_and_reserved = 0x8e,
364 .clock_seq_low = 0x39,
365 .node = [_]u8{ 0x00, 0xa0, 0xc9, 0x69, 0x72, 0x3b },
366 };
367 };
368
369 pub const VolumeLabel = extern struct {
370 _volume_label: u16,
371
372 pub fn getVolumeLabel(self: *const VolumeLabel) [*:0]const u16 {
373 return @as([*:0]const u16, @ptrCast(&self._volume_label));
374 }
375
376 pub const guid align(8) = Guid{
377 .time_low = 0xdb47d7d3,
378 .time_mid = 0xfe81,
379 .time_high_and_version = 0x11d3,
380 .clock_seq_high_and_reserved = 0x9a,
381 .clock_seq_low = 0x35,
382 .node = [_]u8{ 0x00, 0x90, 0x27, 0x3f, 0xc1, 0x4d },
383 };
384 };
385 };
386
387 const end_of_file: u64 = 0xffffffffffffffff;
388};