]> glassweightruler.freedombox.rocks Git - Ventoy.git/blob - GRUB2/MOD_SRC/grub-2.04/grub-core/disk/loopback.c
keep up with 1.0.67 (#1464)
[Ventoy.git] / GRUB2 / MOD_SRC / grub-2.04 / grub-core / disk / loopback.c
1 /* loopback.c - command to add loopback devices. */
2 /*
3 * GRUB -- GRand Unified Bootloader
4 * Copyright (C) 2005,2006,2007 Free Software Foundation, Inc.
5 *
6 * GRUB 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 3 of the License, or
9 * (at your option) any later version.
10 *
11 * GRUB 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 GRUB. If not, see <http://www.gnu.org/licenses/>.
18 */
19
20 #include <grub/dl.h>
21 #include <grub/misc.h>
22 #include <grub/file.h>
23 #include <grub/disk.h>
24 #include <grub/mm.h>
25 #include <grub/extcmd.h>
26 #include <grub/i18n.h>
27
28 GRUB_MOD_LICENSE ("GPLv3+");
29
30 struct grub_loopback
31 {
32 char *devname;
33 grub_file_t file;
34 struct grub_loopback *next;
35 unsigned long id;
36 grub_off_t skip;
37 };
38
39 static struct grub_loopback *loopback_list;
40 static unsigned long last_id = 0;
41
42 static const struct grub_arg_option options[] =
43 {
44 /* TRANSLATORS: The disk is simply removed from the list of available ones,
45 not wiped, avoid to scare user. */
46 {"delete", 'd', 0, N_("Delete the specified loopback drive."), 0, 0},
47 {"skip", 's', 0, "skip sectors of the file.", "SECTORS", ARG_TYPE_INT },
48 {0, 0, 0, 0, 0, 0}
49 };
50
51 /* Delete the loopback device NAME. */
52 static grub_err_t
53 delete_loopback (const char *name)
54 {
55 struct grub_loopback *dev;
56 struct grub_loopback **prev;
57
58 /* Search for the device. */
59 for (dev = loopback_list, prev = &loopback_list;
60 dev;
61 prev = &dev->next, dev = dev->next)
62 if (grub_strcmp (dev->devname, name) == 0)
63 break;
64
65 if (! dev)
66 return grub_error (GRUB_ERR_BAD_DEVICE, "device not found");
67
68 /* Remove the device from the list. */
69 *prev = dev->next;
70
71 grub_free (dev->devname);
72 grub_file_close (dev->file);
73 grub_free (dev);
74
75 return 0;
76 }
77
78 /* The command to add and remove loopback devices. */
79 static grub_err_t
80 grub_cmd_loopback (grub_extcmd_context_t ctxt, int argc, char **args)
81 {
82 struct grub_arg_list *state = ctxt->state;
83 grub_file_t file;
84 struct grub_loopback *newdev;
85 grub_err_t ret;
86 grub_off_t skip = 0;
87
88 if (argc < 1)
89 return grub_error (GRUB_ERR_BAD_ARGUMENT, "device name required");
90
91 /* Check if `-d' was used. */
92 if (state[0].set)
93 return delete_loopback (args[0]);
94
95 if (state[1].set)
96 skip = (grub_off_t)grub_strtoull(state[1].arg, NULL, 10);
97
98 if (argc < 2)
99 return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("filename expected"));
100
101 file = grub_file_open (args[1], GRUB_FILE_TYPE_LOOPBACK
102 | GRUB_FILE_TYPE_NO_DECOMPRESS);
103 if (! file)
104 return grub_errno;
105
106 /* First try to replace the old device. */
107 for (newdev = loopback_list; newdev; newdev = newdev->next)
108 if (grub_strcmp (newdev->devname, args[0]) == 0)
109 break;
110
111 if (newdev)
112 {
113 grub_file_close (newdev->file);
114 newdev->file = file;
115 newdev->skip = skip;
116
117 return 0;
118 }
119
120 /* Unable to replace it, make a new entry. */
121 newdev = grub_malloc (sizeof (struct grub_loopback));
122 if (! newdev)
123 goto fail;
124
125 newdev->devname = grub_strdup (args[0]);
126 if (! newdev->devname)
127 {
128 grub_free (newdev);
129 goto fail;
130 }
131
132 newdev->file = file;
133 newdev->skip = skip;
134 newdev->id = last_id++;
135
136 /* Add the new entry to the list. */
137 newdev->next = loopback_list;
138 loopback_list = newdev;
139
140 return 0;
141
142 fail:
143 ret = grub_errno;
144 grub_file_close (file);
145 return ret;
146 }
147
148 \f
149 static int
150 grub_loopback_iterate (grub_disk_dev_iterate_hook_t hook, void *hook_data,
151 grub_disk_pull_t pull)
152 {
153 struct grub_loopback *d;
154 if (pull != GRUB_DISK_PULL_NONE)
155 return 0;
156 for (d = loopback_list; d; d = d->next)
157 {
158 if (hook (d->devname, hook_data))
159 return 1;
160 }
161 return 0;
162 }
163
164 static grub_err_t
165 grub_loopback_open (const char *name, grub_disk_t disk)
166 {
167 struct grub_loopback *dev;
168
169 for (dev = loopback_list; dev; dev = dev->next)
170 if (grub_strcmp (dev->devname, name) == 0)
171 break;
172
173 if (! dev)
174 return grub_error (GRUB_ERR_UNKNOWN_DEVICE, "can't open device");
175
176 /* Use the filesize for the disk size, round up to a complete sector. */
177 if (dev->file->size != GRUB_FILE_SIZE_UNKNOWN)
178 disk->total_sectors = ((dev->file->size + GRUB_DISK_SECTOR_SIZE - 1)
179 / GRUB_DISK_SECTOR_SIZE);
180 else
181 disk->total_sectors = GRUB_DISK_SIZE_UNKNOWN;
182 /* Avoid reading more than 512M. */
183 disk->max_agglomerate = 1 << (29 - GRUB_DISK_SECTOR_BITS
184 - GRUB_DISK_CACHE_BITS);
185
186 disk->id = dev->id;
187
188 disk->data = dev;
189
190 return 0;
191 }
192
193 static grub_err_t
194 grub_loopback_read (grub_disk_t disk, grub_disk_addr_t sector,
195 grub_size_t size, char *buf)
196 {
197 grub_file_t file = ((struct grub_loopback *) disk->data)->file;
198 grub_off_t skip = ((struct grub_loopback *) disk->data)->skip;
199 grub_off_t pos;
200
201 grub_file_seek (file, (sector + skip) << GRUB_DISK_SECTOR_BITS);
202
203 grub_file_read (file, buf, size << GRUB_DISK_SECTOR_BITS);
204 if (grub_errno)
205 return grub_errno;
206
207 /* In case there is more data read than there is available, in case
208 of files that are not a multiple of GRUB_DISK_SECTOR_SIZE, fill
209 the rest with zeros. */
210 pos = (sector + skip + size) << GRUB_DISK_SECTOR_BITS;
211 if (pos > file->size)
212 {
213 grub_size_t amount = pos - file->size;
214 grub_memset (buf + (size << GRUB_DISK_SECTOR_BITS) - amount, 0, amount);
215 }
216
217 return 0;
218 }
219
220 static grub_err_t
221 grub_loopback_write (grub_disk_t disk __attribute ((unused)),
222 grub_disk_addr_t sector __attribute ((unused)),
223 grub_size_t size __attribute ((unused)),
224 const char *buf __attribute ((unused)))
225 {
226 return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET,
227 "loopback write is not supported");
228 }
229
230 static struct grub_disk_dev grub_loopback_dev =
231 {
232 .name = "loopback",
233 .id = GRUB_DISK_DEVICE_LOOPBACK_ID,
234 .disk_iterate = grub_loopback_iterate,
235 .disk_open = grub_loopback_open,
236 .disk_read = grub_loopback_read,
237 .disk_write = grub_loopback_write,
238 .next = 0
239 };
240
241 static grub_extcmd_t cmd;
242
243 GRUB_MOD_INIT(loopback)
244 {
245 cmd = grub_register_extcmd ("loopback", grub_cmd_loopback, 0,
246 N_("[-d] DEVICENAME FILE."),
247 /* TRANSLATORS: The file itself is not destroyed
248 or transformed into drive. */
249 N_("Make a virtual drive from a file."), options);
250 grub_disk_dev_register (&grub_loopback_dev);
251 }
252
253 GRUB_MOD_FINI(loopback)
254 {
255 grub_unregister_extcmd (cmd);
256 grub_disk_dev_unregister (&grub_loopback_dev);
257 }