2009-06-10 Pavel Roskin <proski@gnu.org>
[grub2/bean.git] / disk / loopback.c
blob3cb2bf0ab0fb9d551ef0f48e820313550a1c6cd6
1 /* loopback.c - command to add loopback devices. */
2 /*
3 * GRUB -- GRand Unified Bootloader
4 * Copyright (C) 2005,2006,2007 Free Software Foundation, Inc.
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.
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.
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/>.
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>
27 struct grub_loopback
29 char *devname;
30 char *filename;
31 int has_partitions;
32 struct grub_loopback *next;
35 static struct grub_loopback *loopback_list;
37 static const struct grub_arg_option options[] =
39 {"delete", 'd', 0, "delete the loopback device entry", 0, 0},
40 {"partitions", 'p', 0, "simulate a hard drive with partitions", 0, 0},
41 {0, 0, 0, 0, 0, 0}
44 /* Delete the loopback device NAME. */
45 static grub_err_t
46 delete_loopback (const char *name)
48 struct grub_loopback *dev;
49 struct grub_loopback **prev;
51 /* Search for the device. */
52 for (dev = loopback_list, prev = &loopback_list;
53 dev;
54 prev = &dev->next, dev = dev->next)
55 if (grub_strcmp (dev->devname, name) == 0)
56 break;
58 if (! dev)
59 return grub_error (GRUB_ERR_BAD_DEVICE, "Device not found");
61 /* Remove the device from the list. */
62 *prev = dev->next;
64 grub_free (dev->devname);
65 grub_free (dev->filename);
66 grub_free (dev);
68 return 0;
71 /* The command to add and remove loopback devices. */
72 static grub_err_t
73 grub_cmd_loopback (grub_extcmd_t cmd, int argc, char **args)
75 struct grub_arg_list *state = state = cmd->state;
76 grub_file_t file;
77 struct grub_loopback *newdev;
79 if (argc < 1)
80 return grub_error (GRUB_ERR_BAD_ARGUMENT, "device name required");
82 /* Check if `-d' was used. */
83 if (state[0].set)
84 return delete_loopback (args[0]);
86 if (argc < 2)
87 return grub_error (GRUB_ERR_BAD_ARGUMENT, "file name required");
89 file = grub_file_open (args[1]);
90 if (! file)
91 return grub_errno;
93 /* Close the file, the only reason for opening it is validation. */
94 grub_file_close (file);
96 /* First try to replace the old device. */
97 for (newdev = loopback_list; newdev; newdev = newdev->next)
98 if (grub_strcmp (newdev->devname, args[0]) == 0)
99 break;
101 if (newdev)
103 char *newname = grub_strdup (args[1]);
104 if (! newname)
105 return grub_errno;
107 grub_free (newdev->filename);
108 newdev->filename = newname;
110 /* Set has_partitions when `--partitions' was used. */
111 newdev->has_partitions = state[1].set;
113 return 0;
116 /* Unable to replace it, make a new entry. */
117 newdev = grub_malloc (sizeof (struct grub_loopback));
118 if (! newdev)
119 return grub_errno;
121 newdev->devname = grub_strdup (args[0]);
122 if (! newdev->devname)
124 grub_free (newdev);
125 return grub_errno;
128 newdev->filename = grub_strdup (args[1]);
129 if (! newdev->filename)
131 grub_free (newdev->devname);
132 grub_free (newdev);
133 return grub_errno;
136 /* Set has_partitions when `--partitions' was used. */
137 newdev->has_partitions = state[1].set;
139 /* Add the new entry to the list. */
140 newdev->next = loopback_list;
141 loopback_list = newdev;
143 return 0;
147 static int
148 grub_loopback_iterate (int (*hook) (const char *name))
150 struct grub_loopback *d;
151 for (d = loopback_list; d; d = d->next)
153 if (hook (d->devname))
154 return 1;
156 return 0;
159 static grub_err_t
160 grub_loopback_open (const char *name, grub_disk_t disk)
162 grub_file_t file;
163 struct grub_loopback *dev;
165 for (dev = loopback_list; dev; dev = dev->next)
166 if (grub_strcmp (dev->devname, name) == 0)
167 break;
169 if (! dev)
170 return grub_error (GRUB_ERR_UNKNOWN_DEVICE, "Can't open device");
172 file = grub_file_open (dev->filename);
173 if (! file)
174 return grub_errno;
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 = (unsigned long) dev;
181 disk->has_partitions = dev->has_partitions;
182 disk->data = file;
184 return 0;
187 static void
188 grub_loopback_close (grub_disk_t disk)
190 grub_file_t file = (grub_file_t) disk->data;
192 grub_file_close (file);
195 static grub_err_t
196 grub_loopback_read (grub_disk_t disk, grub_disk_addr_t sector,
197 grub_size_t size, char *buf)
199 grub_file_t file = (grub_file_t) disk->data;
200 grub_off_t pos;
202 grub_file_seek (file, sector << GRUB_DISK_SECTOR_BITS);
204 grub_file_read (file, buf, size << GRUB_DISK_SECTOR_BITS);
205 if (grub_errno)
206 return grub_errno;
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 + size) << GRUB_DISK_SECTOR_BITS;
212 if (pos > file->size)
214 grub_size_t amount = pos - file->size;
215 grub_memset (buf + (size << GRUB_DISK_SECTOR_BITS) - amount, 0, amount);
218 return 0;
221 static grub_err_t
222 grub_loopback_write (grub_disk_t disk __attribute ((unused)),
223 grub_disk_addr_t sector __attribute ((unused)),
224 grub_size_t size __attribute ((unused)),
225 const char *buf __attribute ((unused)))
227 return GRUB_ERR_NOT_IMPLEMENTED_YET;
230 static struct grub_disk_dev grub_loopback_dev =
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
242 static grub_extcmd_t cmd;
244 GRUB_MOD_INIT(loop)
246 cmd = grub_register_extcmd ("loopback", grub_cmd_loopback,
247 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);
253 GRUB_MOD_FINI(loop)
255 grub_unregister_extcmd (cmd);
256 grub_disk_dev_unregister (&grub_loopback_dev);