Imported Debian patch 0.5.3-1
[debian/efibootmgr] / src / lib / efivars_procfs.c
1 /*
2   efivars_procfs.[ch] - Manipulates EFI variables as exported in /proc/efi/vars
3
4   Copyright (C) 2001,2003 Dell Computer Corporation <Matt_Domsch@dell.com>
5
6     This program is free software; you can redistribute it and/or modify
7     it under the terms of the GNU General Public License as published by
8     the Free Software Foundation; either version 2 of the License, or
9     (at your option) any later version.
10
11     This program is distributed in the hope that it will be useful,
12     but WITHOUT ANY WARRANTY; without even the implied warranty of
13     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14     GNU General Public License for more details.
15
16     You should have received a copy of the GNU General Public License
17     along with this program; if not, write to the Free Software
18     Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19  */
20
21 #include <stdio.h>
22 #include <stdlib.h>
23 #include <string.h>
24 #include <errno.h>
25 #include <stdint.h>
26 #include <sys/stat.h>
27 #include <unistd.h>
28 #include <dirent.h>
29 #include <sys/types.h>
30 #include <fcntl.h>
31
32 #include "efi.h"
33 #include "efichar.h"
34 #include "efibootmgr.h"
35 #include "efivars_procfs.h"
36
37 static efi_status_t
38 procfs_read_variable(const char *name, efi_variable_t *var)
39 {
40         char filename[PATH_MAX];
41         int fd;
42         size_t readsize;
43         if (!name || !var) return EFI_INVALID_PARAMETER;
44
45         snprintf(filename, PATH_MAX-1, "%s/%s", PROCFS_DIR_EFI_VARS,name);
46         fd = open(filename, O_RDONLY);
47         if (fd == -1) {
48                 return EFI_NOT_FOUND;
49         }
50         readsize = read(fd, var, sizeof(*var));
51         if (readsize != sizeof(*var)) {
52                 close(fd);
53                 return EFI_INVALID_PARAMETER;
54         }
55         close(fd);
56         return var->Status;
57 }
58
59 /**
60  * select_variable_names()
61  * @d - dirent to compare against
62  *
63  * This ignores "." and ".." entries, and selects all others.
64  */
65
66 static int
67 select_variable_names(const struct dirent *d)
68 {
69         if (!strcmp(d->d_name, ".") ||
70             !strcmp(d->d_name, ".."))
71                 return 0;
72         return 1;
73 }
74
75 /**
76  * find_write_victim()
77  * @var - variable to be written
78  * @file - name of file to open for writing @var is returned.
79  *
80  * This ignores "." and ".." entries, and selects all others.
81  */
82 static char *
83 find_write_victim(efi_variable_t *var, char file[PATH_MAX])
84 {
85         struct dirent **namelist = NULL;
86         int i, n, found=0;
87         char testname[PATH_MAX], *p;
88
89         memset(testname, 0, sizeof(testname));
90         n = scandir(PROCFS_DIR_EFI_VARS, &namelist,
91                     select_variable_names, alphasort);
92         if (n < 0)
93                 return NULL;
94
95         p = testname;
96         efichar_to_char(p, var->VariableName, PATH_MAX);
97         p += strlen(p);
98         p += sprintf(p, "-");
99         efi_guid_unparse(&var->VendorGuid, p);
100
101         for (i=0; i<n; i++) {
102                 if (namelist[i] &&
103                     strncmp(testname, namelist[i]->d_name, sizeof(testname))) {
104                         found++;
105                         sprintf(file, "%s/%s", PROCFS_DIR_EFI_VARS,
106                                 namelist[i]->d_name);
107                         break;
108                 }
109         }
110
111         while (n--) {
112                 if (namelist[n]) {
113                         free(namelist[n]);
114                         namelist[n] = NULL;
115                 }
116         }
117         free(namelist);
118
119         if (!found) return NULL;
120         return file;
121 }
122
123
124 static efi_status_t
125 procfs_write_variable(efi_variable_t *var)
126 {
127         int fd;
128         size_t writesize;
129         char buffer[PATH_MAX], name[PATH_MAX], *p = NULL;
130
131         if (!var) return EFI_INVALID_PARAMETER;
132         memset(buffer, 0, sizeof(buffer));
133         memset(name, 0, sizeof(name));
134
135         p = find_write_victim(var, name);
136         if (!p) return EFI_INVALID_PARAMETER;
137
138         fd = open(name, O_WRONLY);
139         if (fd == -1) {
140                 sprintf(buffer, "write_variable():open(%s)", name);
141                 perror(buffer);
142                 return EFI_INVALID_PARAMETER;
143         }
144         writesize = write(fd, var, sizeof(*var));
145         if (writesize != sizeof(*var)) {
146                 close(fd);
147                 return EFI_INVALID_PARAMETER;
148
149         }
150         close(fd);
151         return EFI_SUCCESS;
152 }
153
154 static efi_status_t
155 procfs_delete_variable(efi_variable_t *var)
156 {
157         if (!var) return EFI_INVALID_PARAMETER;
158         var->DataSize = 0;
159         var->Attributes = 0;
160         return procfs_write_variable(var);
161
162 }
163
164 static efi_status_t
165 procfs_edit_variable(const char *unused, efi_variable_t *var)
166 {
167         if (!var) return EFI_INVALID_PARAMETER;
168         return procfs_write_variable(var);
169
170 }
171
172 struct efivar_kernel_calls procfs_kernel_calls = {
173         .read = procfs_read_variable,
174         .edit = procfs_edit_variable,
175         .create = procfs_write_variable,
176         .delete = procfs_delete_variable,
177         .path = PROCFS_DIR_EFI_VARS,
178 };