]> git.proxmox.com Git - grub2.git/blob - disk/loopback.c
2005-11-13 Marco Gerards <mgerards@xs4all.nl>
[grub2.git] / disk / loopback.c
1 /* loopback.c - command to add loopback devices. */
2 /*
3 * GRUB -- GRand Unified Bootloader
4 * Copyright (C) 2005 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 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 GRUB; if not, write to the Free Software
18 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19 */
20
21 #include <grub/normal.h>
22 #include <grub/dl.h>
23 #include <grub/arg.h>
24 #include <grub/misc.h>
25 #include <grub/file.h>
26 #include <grub/disk.h>
27 #include <grub/mm.h>
28
29 struct grub_loopback
30 {
31 char *devname;
32 char *filename;
33 int has_partitions;
34 struct grub_loopback *next;
35 };
36
37 static struct grub_loopback *loopback_list;
38
39 static const struct grub_arg_option options[] =
40 {
41 {"delete", 'd', 0, "delete the loopback device entry", 0, 0},
42 {"partitions", 'p', 0, "set that the drive has partitions to"
43 " simulate a harddrive", 0, 0},
44 {0, 0, 0, 0, 0, 0}
45 };
46
47 /* Delete the loopback device NAME. */
48 static grub_err_t
49 delete_loopback (const char *name)
50 {
51 struct grub_loopback *dev;
52 struct grub_loopback **prev;
53
54 /* Search for the device. */
55 for (dev = loopback_list, prev = &loopback_list;
56 dev; prev = &dev->next, dev = dev->next)
57 if (!grub_strcmp (dev->devname, name))
58 break;
59 if (!dev)
60 return grub_error (GRUB_ERR_BAD_DEVICE, "Device not found");
61
62 /* Remove the device from the list. */
63 *prev = dev->next;
64
65 grub_free (dev->devname);
66 grub_free (dev->filename);
67 grub_free (dev);
68
69 return 0;
70 }
71
72 /* The command to add and remove loopback devices. */
73 static grub_err_t
74 grub_cmd_loopback (struct grub_arg_list *state,
75 int argc, char **args)
76 {
77 grub_file_t file;
78 struct grub_loopback *newdev;
79
80 if (argc < 1)
81 return grub_error (GRUB_ERR_BAD_ARGUMENT, "device name required");
82
83 /* Check if `-d' was used. */
84 if (state[0].set)
85 return delete_loopback (args[0]);
86
87 if (argc < 2)
88 return grub_error (GRUB_ERR_BAD_ARGUMENT, "file name required");
89
90 file = grub_file_open (args[1]);
91 if (! file)
92 return grub_errno;
93
94 /* Close the file, the only reason for opening it is validation. */
95 grub_file_close (file);
96
97 /* First try to replace the old device. */
98 for (newdev = loopback_list; newdev; newdev = newdev->next)
99 if (!grub_strcmp (newdev->devname, args[0]))
100 break;
101 if (newdev)
102 {
103 char *newname = grub_strdup (args[1]);
104 if (!newname)
105 return grub_errno;
106
107 grub_free (newdev->filename);
108 newdev->filename = newname;
109
110 /* Set has_partitions when `--partitions' was used. */
111 newdev->has_partitions = state[1].set;
112
113 return 0;
114 }
115
116 /* Unable to replace it, make a new entry. */
117 newdev = grub_malloc (sizeof (struct grub_loopback));
118 if (!newdev)
119 return grub_errno;
120
121 newdev->devname = grub_strdup (args[0]);
122 if (!newdev->devname)
123 {
124 grub_free (newdev);
125 return grub_errno;
126 }
127
128 newdev->filename = grub_strdup (args[1]);
129 if (!newdev->devname)
130 {
131 grub_free (newdev->devname);
132 grub_free (newdev);
133 return grub_errno;
134 }
135
136 /* Set has_partitions when `--partitions' was used. */
137 newdev->has_partitions = state[1].set;
138
139 /* Add the new entry to the list. */
140 newdev->next = loopback_list;
141 loopback_list = newdev;
142
143 return 0;
144 }
145
146 \f
147 static int
148 grub_loopback_iterate (int (*hook) (const char *name))
149 {
150 struct grub_loopback *d;
151 for (d = loopback_list; d; d = d->next)
152 {
153 if (hook (d->devname))
154 return 1;
155 }
156 return 0;
157 }
158
159 static grub_err_t
160 grub_loopback_open (const char *name, grub_disk_t disk)
161 {
162 grub_file_t file;
163 struct grub_loopback *dev;
164
165 for (dev = loopback_list; dev; dev = dev->next)
166 if (!grub_strcmp (dev->devname, name))
167 break;
168
169 if (! dev)
170 return grub_error (GRUB_ERR_UNKNOWN_DEVICE, "Can't open device");
171
172 file = grub_file_open (dev->filename);
173 if (! file)
174 return grub_errno;
175
176 /* Use the filesize for the disk size, round up to a complete sector. */
177 disk->total_sectors = ((file->size + GRUB_DISK_SECTOR_SIZE - 1)
178 / GRUB_DISK_SECTOR_SIZE);
179 disk->id = (int) dev;
180
181 disk->has_partitions = dev->has_partitions;
182 disk->data = file;
183
184 return 0;
185 }
186
187 static void
188 grub_loopback_close (grub_disk_t disk)
189 {
190 grub_file_t file = (grub_file_t) disk->data;
191
192 grub_file_close (file);
193 }
194
195 static grub_err_t
196 grub_loopback_read (grub_disk_t disk, unsigned long sector,
197 unsigned long size, char *buf)
198 {
199 grub_file_t file = (grub_file_t) disk->data;
200 long pos;
201
202 grub_file_seek (file, sector * GRUB_DISK_SECTOR_SIZE);
203
204 grub_file_read (file, buf, size * GRUB_DISK_SECTOR_SIZE);
205 if (grub_errno)
206 return grub_errno;
207
208 /* In case there is more data read than there is available, in case
209 of files that are not a multiple of GRUB_DISK_SECTOR_SIZE, fill
210 the rest with zeros. */
211 pos = sector * GRUB_DISK_SECTOR_SIZE + size * GRUB_DISK_SECTOR_SIZE;
212 if (pos > file->size)
213 {
214 unsigned long amount = pos - file->size;
215 grub_memset (buf + pos - amount, amount, 0);
216 }
217
218 return 0;
219 }
220
221 static grub_err_t
222 grub_loopback_write (grub_disk_t disk __attribute ((unused)),
223 unsigned long sector __attribute ((unused)),
224 unsigned long size __attribute ((unused)),
225 const char *buf __attribute ((unused)))
226 {
227 return GRUB_ERR_NOT_IMPLEMENTED_YET;
228 }
229
230 static struct grub_disk_dev grub_loopback_dev =
231 {
232 .name = "loopback",
233 .id = GRUB_DISK_DEVICE_LOOPBACK_ID,
234 .iterate = grub_loopback_iterate,
235 .open = grub_loopback_open,
236 .close = grub_loopback_close,
237 .read = grub_loopback_read,
238 .write = grub_loopback_write,
239 .next = 0
240 };
241
242 \f
243
244 GRUB_MOD_INIT(loop)
245 {
246 (void)mod; /* To stop warning. */
247 grub_register_command ("loopback", grub_cmd_loopback, GRUB_COMMAND_FLAG_BOTH,
248 "loopback [-d|-p] DEVICENAME FILE",
249 "Make a device of a file.", options);
250 grub_disk_dev_register (&grub_loopback_dev);
251 }
252
253 GRUB_MOD_FINI(loop)
254 {
255 grub_unregister_command ("loopback");
256 grub_disk_dev_unregister (&grub_loopback_dev);
257 }
258