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)
192 #define DHCP_COOKIE 0x63825363
193 #define DHCP_COOKIE_SIZE 4
196 * Process the bootp reply packet's vendor extensions.
197 * Vendor extensions are detailed in: http://www.faqs.org/rfcs/rfc1084.html
200 extract_vendor_options(struct bootp_packet *packet, struct boot_fspec_t *result)
204 __u8 *options = &packet->options[0];
206 memcpy(&cookie, &options[i], DHCP_COOKIE_SIZE);
208 if (cookie != DHCP_COOKIE) {
209 prom_printf("EEEK! cookie is fubar got %08x expected %08x\n",
210 cookie, DHCP_COOKIE);
214 i += DHCP_COOKIE_SIZE;
216 /* FIXME: It may be possible to run off the end of a packet here /if/
217 * it's malformed. :( */
218 while (options[i] != DHCP_END) {
219 __u8 tag = options[i++], len;
226 /* Clamp the maxium length of the memcpy() to the right size for
228 if (len > sizeof(value))
229 memcpy(&value, &options[i], sizeof(value));
231 memcpy(&value, &options[i], len);
235 DEBUG_F("tag=%2d, len=%2d, data=", tag, len);
237 for (j=0; j<len; j++)
238 prom_printf("%02x", options[i+j]);
245 if ((result->subnetmask == NULL ||
246 *(result->subnetmask) == '\x0') && value != 0) {
247 result->subnetmask = ipv4_to_str(value);
248 DEBUG_F("Storing %s as subnetmask from options\n",
253 if ((result->giaddr == NULL || *(result->giaddr) == '\x0')
255 result->giaddr = ipv4_to_str(value);
256 DEBUG_F("Storing %s as gateway from options\n",
266 * Check netinfo for ipv4 parameters and add them to the fspec iff the
267 * fspec has no existing value.
270 extract_netinfo_args(struct boot_fspec_t *result)
272 struct bootp_packet *packet;
274 /* Check to see if we can get the [scyg]iaddr fields from netinfo */
275 packet = prom_get_netinfo();
279 DEBUG_F("We have a boot packet\n");
280 DEBUG_F(" siaddr = <%x>\n", packet->siaddr);
281 DEBUG_F(" ciaddr = <%x>\n", packet->ciaddr);
282 DEBUG_F(" yiaddr = <%x>\n", packet->yiaddr);
283 DEBUG_F(" giaddr = <%x>\n", packet->giaddr);
285 /* Try to fallback to yiaddr if ciaddr is empty. Broken? */
286 if (packet->ciaddr == 0 && packet->yiaddr != 0)
287 packet->ciaddr = packet->yiaddr;
289 if ((result->siaddr == NULL || *(result->siaddr) == '\x0')
290 && packet->siaddr != 0)
291 result->siaddr = ipv4_to_str(packet->siaddr);
292 if ((result->ciaddr == NULL || *(result->ciaddr) == '\x0')
293 && packet->ciaddr != 0)
294 result->ciaddr = ipv4_to_str(packet->ciaddr);
295 if ((result->giaddr == NULL || *(result->giaddr) == '\x0')
296 && packet->giaddr != 0)
297 result->giaddr = ipv4_to_str(packet->giaddr);
299 extract_vendor_options(packet, result);
301 /* FIXME: Yck! if we /still/ do not have a gateway then "cheat" and use
302 * the server. This will be okay if the client and server are on
303 * the same IP network, if not then lets hope the server does ICMP
305 if (result->giaddr == NULL) {
306 result->giaddr = ipv4_to_str(packet->siaddr);
307 DEBUG_F("Forcing giaddr to siaddr <%s>\n", result->giaddr);
312 * Extract all the ipv6 arguments from the bootpath provided and fill result
313 * Syntax: ipv6,[dhcpv6[=diaddr,]]ciaddr=c_iaddr,giaddr=g_iaddr,siaddr=s_iaddr,
314 * filename=file_name,tftp-retries=tftp_retries,blksize=block_size
315 * Returns 1 on success, 0 on failure.
318 extract_ipv6_args(char *imagepath, struct boot_fspec_t *result)
325 /* Just allocate the max required size */
326 total_len = strlen(imagepath) + 1;
327 str = malloc(total_len);
331 if ((tmp = strstr(imagepath, "dhcpv6=")) != NULL)
332 result->dhcpv6 = scopy(&str, &tmp);
334 if ((tmp = strstr(imagepath, "ciaddr=")) != NULL)
335 result->ciaddr = scopy(&str, &tmp);
337 if ((tmp = strstr(imagepath, "giaddr=")) != NULL)
338 result->giaddr = scopy(&str, &tmp);
340 if ((tmp = strstr(imagepath, "siaddr=")) != NULL)
341 result->siaddr = scopy(&str, &tmp);
343 if ((tmp = strstr(imagepath, "filename=")) != NULL)
344 result->file = scopy(&str, &tmp);
346 if ((tmp = strstr(imagepath, "tftp-retries=")) != NULL)
347 result->tftp_retries = scopy(&str, &tmp);
349 if ((tmp = strstr(imagepath, "blksize=")) != NULL)
350 result->blksize = scopy(&str, &tmp);
356 * Extract all the arguments provided in the imagepath and fill it in result.
357 * Returns 1 on success, 0 on failure.
360 extract_netboot_args(char *imagepath, struct boot_fspec_t *result)
364 DEBUG_F("imagepath = %s\n", imagepath);
369 if (strstr(imagepath, TOK_IPV6))
370 ret = extract_ipv6_args(imagepath, result);
372 ret = extract_ipv4_args(imagepath, result);
373 extract_netinfo_args(result);
375 DEBUG_F("ipv6 = <%d>\n", result->is_ipv6);
376 DEBUG_F("siaddr = <%s>\n", result->siaddr);
377 DEBUG_F("file = <%s>\n", result->file);
378 DEBUG_F("ciaddr = <%s>\n", result->ciaddr);
379 DEBUG_F("giaddr = <%s>\n", result->giaddr);
380 DEBUG_F("bootp_retries = <%s>\n", result->bootp_retries);
381 DEBUG_F("tftp_retries = <%s>\n", result->tftp_retries);
382 DEBUG_F("addl_params = <%s>\n", result->addl_params);
383 DEBUG_F("dhcpv6 = <%s>\n", result->dhcpv6);
384 DEBUG_F("blksize = <%s>\n", result->blksize);
389 static char *netdev_path_to_dev(const char *path)
394 DEBUG_F("path = %s\n", path);
399 tmp = strchr(path, ':');
404 len = tmp - path + 1;
408 strncpy(dev, path, len);
414 /* This function follows the device path in the devtree and separates
415 the device name, partition number, and other datas (mostly file name)
416 the string passed in parameters is changed since 0 are put in place
417 of some separators to terminate the various strings.
419 when a default device is supplied imagepath will be assumed to be a
420 plain filename unless it contains a : otherwise if defaultdev is
421 NULL imagepath will be assumed to be a device path.
423 returns 1 on success 0 on failure.
426 - /pci@80000000/pci-bridge@d/ADPT,2930CU@2/@1:4
427 - /pci@80000000/pci-bridge@d/ADPT,2930CU@2/@1:4,/boot/vmlinux
429 - enet:10.0.0.1,/tftpboot/vmlinux
430 - enet:,/tftpboot/vmlinux
433 - arguments for obp-tftp open as specified in section 4.1 of
434 http://playground.sun.com/1275/practice/obp-tftp/tftp1_0.pdf
435 [bootp,]siaddr,filename,ciaddr,giaddr,bootp-retries,tftp-retries
436 ex: enet:bootp,10.0.0.11,bootme,10.0.0.12,10.0.0.1,5,5
437 Supported only if defdevice == NULL
439 - any other device path lacking a :
440 Unsupported examples:
441 - hd:2,\\:tbxi <- no filename will be detected due to the extra :
442 - enet:192.168.2.1,bootme,c-iaddr,g-iaddr,subnet-mask,bootp-retries,tftp-retries */
445 parse_device_path(char *imagepath, char *defdevice, int defpart,
446 char *deffile, struct boot_fspec_t *result)
451 int device_kind = -1;
453 DEBUG_F("imagepath = %s; defdevice %s; defpart %d, deffile %s\n",
454 imagepath, defdevice, defpart, deffile);
464 * Do preliminary checking for an iscsi device; it may appear as
465 * pure a network device (device_type == "network") if this is
466 * ISWI. This is the case on IBM systems doing an iscsi OFW
469 if (strstr(imagepath, TOK_ISCSI)) {
471 * get the virtual device information from the
472 * "nas-bootdevice" property.
474 if (prom_get_chosen("nas-bootdevice", bootdevice, BOOTDEVSZ)) {
475 DEBUG_F("reset boot-device to"
476 " /chosen/nas-bootdevice = %s\n", bootdevice);
477 device_kind = FILE_DEVICE_ISCSI;
478 ipath = strdup(bootdevice);
485 else if (!(ipath = strdup(imagepath)))
489 defdev = strdup(defdevice);
490 device_kind = prom_get_devtype(defdev);
491 } else if (device_kind == -1)
492 device_kind = prom_get_devtype(ipath);
495 * When an iscsi iqn is present, it may have embedded colons, so
496 * don't parse off anything.
498 if (device_kind != FILE_DEVICE_NET &&
499 device_kind != FILE_DEVICE_ISCSI &&
500 strchr(defdev, ':') != NULL) {
501 if ((ptr = strrchr(defdev, ':')) != NULL)
502 *ptr = 0; /* remove trailing : from defdevice if necessary */
505 /* This will not properly handle an obp-tftp argument list
506 * with elements after the filename; that is handled below.
508 if (device_kind != FILE_DEVICE_NET &&
509 device_kind != FILE_DEVICE_ISCSI &&
510 strchr(ipath, ':') != NULL) {
511 if ((ptr = strrchr(ipath, ',')) != NULL) {
512 char *colon = strrchr(ipath, ':');
513 /* If a ':' occurs *after* a ',', then we assume that there is
515 if (!colon || colon < ptr) {
516 result->file = strdup(ptr+1);
517 /* Trim the filename off */
523 if (device_kind == FILE_DEVICE_NET) {
524 if (strchr(ipath, ':')) {
525 if (extract_netboot_args(ipath, result) == 0)
528 /* If we didn't get a ':' then look only in netinfo */
529 extract_netinfo_args(result);
530 result->file = strdup(ipath);
534 result->dev = netdev_path_to_dev(ipath);
535 } else if (device_kind != FILE_DEVICE_ISCSI &&
536 (ptr = strrchr(ipath, ':')) != NULL) {
538 result->dev = strdup(ipath);
540 result->part = simple_strtol(ptr+1, NULL, 10);
541 } else if (!defdev) {
542 result->dev = strdup(ipath);
543 } else if (strlen(ipath)) {
544 result->file = strdup(ipath);
550 if (!result->dev && defdev)
551 result->dev = strdup(defdev);
553 if (result->part < 0)
554 result->part = defpart;
557 result->file = strdup(deffile);
567 file_block_open( struct boot_file_t* file,
568 struct boot_fspec_t* fspec,
571 struct partition_t* parts;
572 struct partition_t* p;
573 struct partition_t* found;
575 parts = partitions_lookup(fspec->dev);
580 prom_printf("partitions:\n");
582 prom_printf("no partitions found.\n");
584 for (p = parts; p && !found; p=p->next) {
585 DEBUG_F("number: %02d, start: 0x%08lx, length: 0x%08lx\n",
586 p->part_number, p->part_start, p->part_size );
587 if (partition == -1) {
588 file->fs = fs_open( file, p, fspec );
589 if (file->fs == NULL || fserrorno != FILE_ERR_OK)
592 partition = p->part_number;
596 if ((partition >= 0) && (partition == p->part_number))
600 prom_printf(" (match)\n");
604 /* Note: we don't skip when found is NULL since we can, in some
605 * cases, let OF figure out a default partition.
607 DEBUG_F( "Using OF defaults.. (found = %p)\n", found );
608 file->fs = fs_open( file, found, fspec );
612 partitions_free(parts);
618 file_net_open(struct boot_file_t* file, struct boot_fspec_t *fspec)
620 file->fs = fs_of_netboot;
621 return fs_of_netboot->open(file, NULL, fspec);
625 default_read( struct boot_file_t* file,
629 prom_printf("WARNING ! default_read called !\n");
634 default_seek( struct boot_file_t* file,
637 prom_printf("WARNING ! default_seek called !\n");
642 default_close( struct boot_file_t* file)
644 prom_printf("WARNING ! default_close called !\n");
648 static struct fs_t fs_default =
658 int open_file(struct boot_fspec_t* spec, struct boot_file_t* file)
662 memset(file, 0, sizeof(struct boot_file_t*));
663 file->fs = &fs_default;
665 DEBUG_F("dev_path = %s\nfile_name = %s\npartition = %d\n",
666 spec->dev, spec->file, spec->part);
668 result = prom_get_devtype(spec->dev);
670 file->device_kind = result;
674 switch(file->device_kind) {
675 case FILE_DEVICE_BLOCK:
676 DEBUG_F("device is a block device\n");
677 return file_block_open(file, spec, spec->part);
678 case FILE_DEVICE_ISCSI:
679 DEBUG_F("device is a iSCSI device\n");
680 return file_block_open(file, spec, spec->part);
681 case FILE_DEVICE_NET:
682 DEBUG_F("device is a network device\n");
683 return file_net_open(file, spec);
690 * c-file-style: "k&r"