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};