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