2 * file.c - Filesystem related interfaces
4 * Copyright (C) 2001, 2002 Ethan Benson
8 * Copyright (C) 2001 Colin Walters
10 * Copyright (C) 1999 Benjamin Herrenschmidt
12 * This program is free software; you can redistribute it and/or modify
13 * it under the terms of the GNU General Public License as published by
14 * the Free Software Foundation; either version 2 of the License, or
15 * (at your option) any later version.
17 * This program is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 * GNU General Public License for more details.
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, write to the Free Software
24 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
34 #include "partition.h"
39 extern char bootdevice[];
41 /* Convert __u32 into std, dotted quad string, leaks like a sive :( */
45 char *buf = malloc(sizeof("000.000.000.000"));
47 sprintf(buf,"%u.%u.%u.%u",
48 (ip & 0xff000000) >> 24, (ip & 0x00ff0000) >> 16,
49 (ip & 0x0000ff00) >> 8, (ip & 0x000000ff));
54 /* Ensure the string arg is a plausible IPv4 address */
55 static char * is_valid_ipv4_str(char *str)
60 char *ptr=str, *endptr;
65 for (i=0; i<4; i++, ptr = ++endptr) {
66 tmp = strtol(ptr, &endptr, 10);
67 if ((tmp & 0xff) != tmp)
70 /* If we reach the end of the string but we're not in the 4th octet
71 * we have an invalid IP */
72 if (*endptr == '\x0' && i!=3)
75 /* If we have anything other than a NULL or '.' we have an invlaid
77 if (*endptr != '\x0' && *endptr != '.')
80 ip += (tmp << (24-(i*8)));
83 if (ip == 0 || ip == ~0u)
91 * Copy the string from source to dest till newline or comma(,) is seen
93 * Move source and dest pointers respectively.
94 * Returns pointer to the start of the string that has just been copied.
97 scopy(char **dest, char **source)
104 while (**source != ',' && **source != '\0')
105 *(*dest)++ = *(*source)++;
106 if (**source != '\0')
114 * Extract all the ipv4 arguments from the bootpath provided and fill result
115 * Returns 1 on success, 0 on failure.
118 extract_ipv4_args(char *imagepath, struct boot_fspec_t *result)
120 char *tmp, *args, *str, *start;
122 args = strrchr(imagepath, ':');
126 start = args; /* used to see if we read any optional parameters */
128 /* The obp-tftp device arguments should be at the end of
129 * the argument list. Skip over any extra arguments (promiscuous,
130 * speed, duplex, bootp, rarp).
133 tmp = strstr(args, "promiscuous");
134 if (tmp && tmp > args)
135 args = tmp + strlen("promiscuous");
137 tmp = strstr(args, "speed=");
138 if (tmp && tmp > args)
139 args = tmp + strlen("speed=");
141 tmp = strstr(args, "duplex=");
142 if (tmp && tmp > args)
143 args = tmp + strlen("duplex=");
145 tmp = strstr(args, "bootp");
146 if (tmp && tmp > args)
147 args = tmp + strlen("bootp");
149 tmp = strstr(args, "rarp");
150 if (tmp && tmp > args)
151 args = tmp + strlen("rarp");
153 if (args != start) /* we read some parameters, so go past the next comma(,) */
154 args = strchr(args, ',');
158 str = malloc(strlen(args) + 1); /*long enough to hold all strings */
163 args++; /* If comma(,) is not immediately followed by ':' then go past the , */
166 * read the arguments in order: siaddr,filename,ciaddr,giaddr,
167 * bootp-retries,tftp-retries,addl_prameters
169 result->siaddr = is_valid_ipv4_str(scopy(&str, &args));
170 result->file = scopy(&str, &args);
171 result->ciaddr = is_valid_ipv4_str(scopy(&str, &args));
172 result->giaddr = is_valid_ipv4_str(scopy(&str, &args));
173 result->bootp_retries = scopy(&str, &args);
174 result->tftp_retries = scopy(&str, &args);
175 result->subnetmask = is_valid_ipv4_str(scopy(&str, &args));
177 result->addl_params = strdup(args);
178 if (!result->addl_params)
193 #define DHCP_COOKIE 0x63825363
194 #define DHCP_COOKIE_SIZE 4
197 * Process the bootp reply packet's vendor extensions.
198 * Vendor extensions are detailed in: http://www.faqs.org/rfcs/rfc1084.html
201 extract_vendor_options(struct bootp_packet *packet, struct boot_fspec_t *result)
205 __u8 *options = &packet->options[0];
207 memcpy(&cookie, &options[i], DHCP_COOKIE_SIZE);
209 if (cookie != DHCP_COOKIE) {
210 prom_printf("EEEK! cookie is fubar got %08x expected %08x\n",
211 cookie, DHCP_COOKIE);
215 i += DHCP_COOKIE_SIZE;
217 /* FIXME: It may be possible to run off the end of a packet here /if/
218 * it's malformed. :( */
219 while (options[i] != DHCP_END) {
220 __u8 tag = options[i++], len;
227 memcpy(&value, &options[i], len);
231 DEBUG_F("tag=%2d, len=%2d, data=", tag, len);
233 for (j=0; j<len; j++)
234 prom_printf("%02x", options[i+j]);
241 if ((result->subnetmask == NULL ||
242 *(result->subnetmask) == '\x0') && value != 0) {
243 result->subnetmask = ipv4_to_str(value);
244 DEBUG_F("Storing %s as subnetmask from options\n",
249 if ((result->giaddr == NULL || *(result->giaddr) == '\x0')
251 result->giaddr = ipv4_to_str(value);
252 DEBUG_F("Storing %s as gateway from options\n",
262 * Check netinfo for ipv4 parameters and add them to the fspec iff the
263 * fspec has no existing value.
265 * Returns 1 on success, 0 on failure.
268 extract_netinfo_args(struct boot_fspec_t *result)
270 struct bootp_packet *packet;
272 /* Check to see if we can get the [scyg]iaddr fields from netinfo */
273 packet = prom_get_netinfo();
277 DEBUG_F("We have a boot packet\n");
278 DEBUG_F(" siaddr = <%x>\n", packet->siaddr);
279 DEBUG_F(" ciaddr = <%x>\n", packet->ciaddr);
280 DEBUG_F(" yiaddr = <%x>\n", packet->yiaddr);
281 DEBUG_F(" giaddr = <%x>\n", packet->giaddr);
283 /* Try to fallback to yiaddr if ciaddr is empty. Broken? */
284 if (packet->ciaddr == 0 && packet->yiaddr != 0)
285 packet->ciaddr = packet->yiaddr;
287 if ((result->siaddr == NULL || *(result->siaddr) == '\x0')
288 && packet->siaddr != 0)
289 result->siaddr = ipv4_to_str(packet->siaddr);
290 if ((result->ciaddr == NULL || *(result->ciaddr) == '\x0')
291 && packet->ciaddr != 0)
292 result->ciaddr = ipv4_to_str(packet->ciaddr);
293 if ((result->giaddr == NULL || *(result->giaddr) == '\x0')
294 && packet->giaddr != 0)
295 result->giaddr = ipv4_to_str(packet->giaddr);
297 extract_vendor_options(packet, result);
299 /* FIXME: Yck! if we /still/ do not have a gateway then "cheat" and use
300 * the server. This will be okay if the client and server are on
301 * the same IP network, if not then lets hope the server does ICMP
303 if (result->giaddr == NULL) {
304 result->giaddr = ipv4_to_str(packet->siaddr);
305 DEBUG_F("Forcing giaddr to siaddr <%s>\n", result->giaddr);
312 * Extract all the arguments provided in the imagepath and fill it in result.
313 * Returns 1 on success, 0 on failure.
316 extract_netboot_args(char *imagepath, struct boot_fspec_t *result)
320 DEBUG_F("imagepath = %s\n", imagepath);
325 ret = extract_ipv4_args(imagepath, result);
326 ret |= extract_netinfo_args(result);
328 DEBUG_F("siaddr = <%s>\n", result->siaddr);
329 DEBUG_F("file = <%s>\n", result->file);
330 DEBUG_F("ciaddr = <%s>\n", result->ciaddr);
331 DEBUG_F("giaddr = <%s>\n", result->giaddr);
332 DEBUG_F("bootp_retries = <%s>\n", result->bootp_retries);
333 DEBUG_F("tftp_retries = <%s>\n", result->tftp_retries);
334 DEBUG_F("addl_params = <%s>\n", result->addl_params);
339 static char *netdev_path_to_dev(const char *path)
344 DEBUG_F("path = %s\n", path);
349 tmp = strchr(path, ':');
354 len = tmp - path + 1;
358 strncpy(dev, path, len);
364 /* This function follows the device path in the devtree and separates
365 the device name, partition number, and other datas (mostly file name)
366 the string passed in parameters is changed since 0 are put in place
367 of some separators to terminate the various strings.
369 when a default device is supplied imagepath will be assumed to be a
370 plain filename unless it contains a : otherwise if defaultdev is
371 NULL imagepath will be assumed to be a device path.
373 returns 1 on success 0 on failure.
376 - /pci@80000000/pci-bridge@d/ADPT,2930CU@2/@1:4
377 - /pci@80000000/pci-bridge@d/ADPT,2930CU@2/@1:4,/boot/vmlinux
379 - enet:10.0.0.1,/tftpboot/vmlinux
380 - enet:,/tftpboot/vmlinux
383 - arguments for obp-tftp open as specified in section 4.1 of
384 http://playground.sun.com/1275/practice/obp-tftp/tftp1_0.pdf
385 [bootp,]siaddr,filename,ciaddr,giaddr,bootp-retries,tftp-retries
386 ex: enet:bootp,10.0.0.11,bootme,10.0.0.12,10.0.0.1,5,5
387 Supported only if defdevice == NULL
389 - any other device path lacking a :
390 Unsupported examples:
391 - hd:2,\\:tbxi <- no filename will be detected due to the extra :
392 - enet:192.168.2.1,bootme,c-iaddr,g-iaddr,subnet-mask,bootp-retries,tftp-retries */
395 parse_device_path(char *imagepath, char *defdevice, int defpart,
396 char *deffile, struct boot_fspec_t *result)
401 int device_kind = -1;
403 DEBUG_F("imagepath = %s; defdevice %s; defpart %d, deffile %s\n",
404 imagepath, defdevice, defpart, deffile);
414 * Do preliminary checking for an iscsi device; it may appear as
415 * pure a network device (device_type == "network") if this is
416 * ISWI. This is the case on IBM systems doing an iscsi OFW
419 if (strstr(imagepath, TOK_ISCSI)) {
421 * get the virtual device information from the
422 * "nas-bootdevice" property.
424 if (prom_get_chosen("nas-bootdevice", bootdevice, BOOTDEVSZ)) {
425 DEBUG_F("reset boot-device to"
426 " /chosen/nas-bootdevice = %s\n", bootdevice);
427 device_kind = FILE_DEVICE_ISCSI;
428 ipath = strdup(bootdevice);
435 else if (!(ipath = strdup(imagepath)))
439 defdev = strdup(defdevice);
440 device_kind = prom_get_devtype(defdev);
441 } else if (device_kind == -1)
442 device_kind = prom_get_devtype(ipath);
445 * When an iscsi iqn is present, it may have embedded colons, so
446 * don't parse off anything.
448 if (device_kind != FILE_DEVICE_NET &&
449 device_kind != FILE_DEVICE_ISCSI &&
450 strchr(defdev, ':') != NULL) {
451 if ((ptr = strrchr(defdev, ':')) != NULL)
452 *ptr = 0; /* remove trailing : from defdevice if necessary */
455 /* This will not properly handle an obp-tftp argument list
456 * with elements after the filename; that is handled below.
458 if (device_kind != FILE_DEVICE_NET &&
459 device_kind != FILE_DEVICE_ISCSI &&
460 strchr(ipath, ':') != NULL) {
461 if ((ptr = strrchr(ipath, ',')) != NULL) {
462 char *colon = strrchr(ipath, ':');
463 /* If a ':' occurs *after* a ',', then we assume that there is
465 if (!colon || colon < ptr) {
466 result->file = strdup(ptr+1);
467 /* Trim the filename off */
473 if (device_kind == FILE_DEVICE_NET) {
474 if (strchr(ipath, ':')) {
475 if (extract_netboot_args(ipath, result) == 0)
478 /* If we didn't get a ':' then look only in netinfo */
479 extract_netinfo_args(result);
480 result->file = strdup(ipath);
484 result->dev = netdev_path_to_dev(ipath);
485 } else if (device_kind != FILE_DEVICE_ISCSI &&
486 (ptr = strrchr(ipath, ':')) != NULL) {
488 result->dev = strdup(ipath);
490 result->part = simple_strtol(ptr+1, NULL, 10);
491 } else if (!defdev) {
492 result->dev = strdup(ipath);
493 } else if (strlen(ipath)) {
494 result->file = strdup(ipath);
500 if (!result->dev && defdev)
501 result->dev = strdup(defdev);
503 if (result->part < 0)
504 result->part = defpart;
507 result->file = strdup(deffile);
517 file_block_open( struct boot_file_t* file,
518 struct boot_fspec_t* fspec,
521 struct partition_t* parts;
522 struct partition_t* p;
523 struct partition_t* found;
525 parts = partitions_lookup(fspec->dev);
530 prom_printf("partitions:\n");
532 prom_printf("no partitions found.\n");
534 for (p = parts; p && !found; p=p->next) {
535 DEBUG_F("number: %02d, start: 0x%08lx, length: 0x%08lx\n",
536 p->part_number, p->part_start, p->part_size );
537 if (partition == -1) {
538 file->fs = fs_open( file, p, fspec );
539 if (file->fs == NULL || fserrorno != FILE_ERR_OK)
542 partition = p->part_number;
546 if ((partition >= 0) && (partition == p->part_number))
550 prom_printf(" (match)\n");
554 /* Note: we don't skip when found is NULL since we can, in some
555 * cases, let OF figure out a default partition.
557 DEBUG_F( "Using OF defaults.. (found = %p)\n", found );
558 file->fs = fs_open( file, found, fspec );
562 partitions_free(parts);
568 file_net_open(struct boot_file_t* file, struct boot_fspec_t *fspec)
570 file->fs = fs_of_netboot;
571 return fs_of_netboot->open(file, NULL, fspec);
575 default_read( struct boot_file_t* file,
579 prom_printf("WARNING ! default_read called !\n");
584 default_seek( struct boot_file_t* file,
587 prom_printf("WARNING ! default_seek called !\n");
592 default_close( struct boot_file_t* file)
594 prom_printf("WARNING ! default_close called !\n");
598 static struct fs_t fs_default =
608 int open_file(struct boot_fspec_t* spec, struct boot_file_t* file)
612 memset(file, 0, sizeof(struct boot_file_t*));
613 file->fs = &fs_default;
615 DEBUG_F("dev_path = %s\nfile_name = %s\npartition = %d\n",
616 spec->dev, spec->file, spec->part);
618 result = prom_get_devtype(spec->dev);
620 file->device_kind = result;
624 switch(file->device_kind) {
625 case FILE_DEVICE_BLOCK:
626 DEBUG_F("device is a block device\n");
627 return file_block_open(file, spec, spec->part);
628 case FILE_DEVICE_NET:
629 DEBUG_F("device is a network device\n");
630 return file_net_open(file, spec);
637 * c-file-style: "k&r"