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 until the end of string 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: vtag,siaddr,filename,ciaddr,giaddr,
167 * bootp-retries,tftp-retries,addl_prameters
169 if ((tmp = strstr(imagepath, "vtag=")) != NULL) {
170 result->vtag = scopy(&str, &tmp);
174 result->siaddr = is_valid_ipv4_str(scopy(&str, &args));
175 result->file = scopy(&str, &args);
176 result->ciaddr = is_valid_ipv4_str(scopy(&str, &args));
177 result->giaddr = is_valid_ipv4_str(scopy(&str, &args));
178 result->bootp_retries = scopy(&str, &args);
179 result->tftp_retries = scopy(&str, &args);
180 result->subnetmask = is_valid_ipv4_str(scopy(&str, &args));
182 result->addl_params = strdup(args);
183 if (!result->addl_params)
197 #define DHCP_COOKIE 0x63825363
198 #define DHCP_COOKIE_SIZE 4
201 * Process the bootp reply packet's vendor extensions.
202 * Vendor extensions are detailed in: http://www.faqs.org/rfcs/rfc1084.html
205 extract_vendor_options(struct bootp_packet *packet, struct boot_fspec_t *result)
209 __u8 *options = &packet->options[0];
211 memcpy(&cookie, &options[i], DHCP_COOKIE_SIZE);
213 if (cookie != DHCP_COOKIE) {
214 prom_printf("EEEK! cookie is fubar got %08x expected %08x\n",
215 cookie, DHCP_COOKIE);
219 i += DHCP_COOKIE_SIZE;
221 /* FIXME: It may be possible to run off the end of a packet here /if/
222 * it's malformed. :( */
223 while (options[i] != DHCP_END) {
224 __u8 tag = options[i++], len;
231 /* Clamp the maxium length of the memcpy() to the right size for
233 if (len > sizeof(value))
234 memcpy(&value, &options[i], sizeof(value));
236 memcpy(&value, &options[i], len);
240 DEBUG_F("tag=%2d, len=%2d, data=", tag, len);
242 for (j=0; j<len; j++)
243 prom_printf("%02x", options[i+j]);
250 if ((result->subnetmask == NULL ||
251 *(result->subnetmask) == '\x0') && value != 0) {
252 result->subnetmask = ipv4_to_str(value);
253 DEBUG_F("Storing %s as subnetmask from options\n",
258 if ((result->giaddr == NULL || *(result->giaddr) == '\x0')
260 result->giaddr = ipv4_to_str(value);
261 DEBUG_F("Storing %s as gateway from options\n",
271 * Check netinfo for ipv4 parameters and add them to the fspec iff the
272 * fspec has no existing value.
275 extract_netinfo_args(struct boot_fspec_t *result)
277 struct bootp_packet *packet;
279 /* Check to see if we can get the [scyg]iaddr fields from netinfo */
280 packet = prom_get_netinfo();
284 DEBUG_F("We have a boot packet\n");
285 DEBUG_F(" siaddr = <%x>\n", packet->siaddr);
286 DEBUG_F(" ciaddr = <%x>\n", packet->ciaddr);
287 DEBUG_F(" yiaddr = <%x>\n", packet->yiaddr);
288 DEBUG_F(" giaddr = <%x>\n", packet->giaddr);
290 /* Try to fallback to yiaddr if ciaddr is empty. Broken? */
291 if (packet->ciaddr == 0 && packet->yiaddr != 0)
292 packet->ciaddr = packet->yiaddr;
294 if ((result->siaddr == NULL || *(result->siaddr) == '\x0')
295 && packet->siaddr != 0)
296 result->siaddr = ipv4_to_str(packet->siaddr);
297 if ((result->ciaddr == NULL || *(result->ciaddr) == '\x0')
298 && packet->ciaddr != 0)
299 result->ciaddr = ipv4_to_str(packet->ciaddr);
300 if ((result->giaddr == NULL || *(result->giaddr) == '\x0')
301 && packet->giaddr != 0)
302 result->giaddr = ipv4_to_str(packet->giaddr);
304 extract_vendor_options(packet, result);
306 /* FIXME: Yck! if we /still/ do not have a gateway then "cheat" and use
307 * the server. This will be okay if the client and server are on
308 * the same IP network, if not then lets hope the server does ICMP
310 if (result->giaddr == NULL) {
311 result->giaddr = ipv4_to_str(packet->siaddr);
312 DEBUG_F("Forcing giaddr to siaddr <%s>\n", result->giaddr);
317 * Extract all the ipv6 arguments from the bootpath provided and fill result
318 * Syntax: ipv6,[dhcpv6[=diaddr,]]ciaddr=c_iaddr,giaddr=g_iaddr,siaddr=s_iaddr,
319 * filename=file_name,tftp-retries=tftp_retries,blksize=block_size
320 * Returns 1 on success, 0 on failure.
323 extract_ipv6_args(char *imagepath, struct boot_fspec_t *result)
330 /* Just allocate the max required size */
331 total_len = strlen(imagepath) + 1;
332 str = malloc(total_len);
336 if ((tmp = strstr(imagepath, "dhcpv6=")) != NULL)
337 result->dhcpv6 = scopy(&str, &tmp);
339 if ((tmp = strstr(imagepath, "vtag=")) != NULL)
340 result->vtag = scopy(&str, &tmp);
342 if ((tmp = strstr(imagepath, "ciaddr=")) != NULL)
343 result->ciaddr = scopy(&str, &tmp);
345 if ((tmp = strstr(imagepath, "giaddr=")) != NULL)
346 result->giaddr = scopy(&str, &tmp);
348 if ((tmp = strstr(imagepath, "siaddr=")) != NULL)
349 result->siaddr = scopy(&str, &tmp);
351 if ((tmp = strstr(imagepath, "filename=")) != NULL)
352 result->file = scopy(&str, &tmp);
354 if ((tmp = strstr(imagepath, "tftp-retries=")) != NULL)
355 result->tftp_retries = scopy(&str, &tmp);
357 if ((tmp = strstr(imagepath, "blksize=")) != NULL)
358 result->blksize = scopy(&str, &tmp);
364 * Extract all the arguments provided in the imagepath and fill it in result.
365 * Returns 1 on success, 0 on failure.
368 extract_netboot_args(char *imagepath, struct boot_fspec_t *result)
372 DEBUG_F("imagepath = %s\n", imagepath);
377 if (strstr(imagepath, TOK_IPV6))
378 ret = extract_ipv6_args(imagepath, result);
380 ret = extract_ipv4_args(imagepath, result);
381 extract_netinfo_args(result);
383 DEBUG_F("ipv6 = <%d>\n", result->is_ipv6);
384 DEBUG_F("siaddr = <%s>\n", result->siaddr);
385 DEBUG_F("file = <%s>\n", result->file);
386 DEBUG_F("ciaddr = <%s>\n", result->ciaddr);
387 DEBUG_F("giaddr = <%s>\n", result->giaddr);
388 DEBUG_F("bootp_retries = <%s>\n", result->bootp_retries);
389 DEBUG_F("tftp_retries = <%s>\n", result->tftp_retries);
390 DEBUG_F("addl_params = <%s>\n", result->addl_params);
391 DEBUG_F("dhcpv6 = <%s>\n", result->dhcpv6);
392 DEBUG_F("blksize = <%s>\n", result->blksize);
397 static char *netdev_path_to_dev(const char *path)
402 DEBUG_F("path = %s\n", path);
407 tmp = strchr(path, ':');
412 len = tmp - path + 1;
416 strncpy(dev, path, len);
422 /* This function follows the device path in the devtree and separates
423 the device name, partition number, and other datas (mostly file name)
424 the string passed in parameters is changed since 0 are put in place
425 of some separators to terminate the various strings.
427 when a default device is supplied imagepath will be assumed to be a
428 plain filename unless it contains a : otherwise if defaultdev is
429 NULL imagepath will be assumed to be a device path.
431 returns 1 on success 0 on failure.
434 - /pci@80000000/pci-bridge@d/ADPT,2930CU@2/@1:4
435 - /pci@80000000/pci-bridge@d/ADPT,2930CU@2/@1:4,/boot/vmlinux
437 - enet:10.0.0.1,/tftpboot/vmlinux
438 - enet:,/tftpboot/vmlinux
441 - arguments for obp-tftp open as specified in section 4.1 of
442 http://playground.sun.com/1275/practice/obp-tftp/tftp1_0.pdf
443 [bootp,]siaddr,filename,ciaddr,giaddr,bootp-retries,tftp-retries
444 ex: enet:bootp,10.0.0.11,bootme,10.0.0.12,10.0.0.1,5,5
445 Supported only if defdevice == NULL
447 - any other device path lacking a :
448 Unsupported examples:
449 - hd:2,\\:tbxi <- no filename will be detected due to the extra :
450 - enet:192.168.2.1,bootme,c-iaddr,g-iaddr,subnet-mask,bootp-retries,tftp-retries */
453 parse_device_path(char *imagepath, char *defdevice, int defpart,
454 char *deffile, struct boot_fspec_t *result)
459 int device_kind = -1;
461 DEBUG_F("imagepath = %s; defdevice %s; defpart %d, deffile %s\n",
462 imagepath, defdevice, defpart, deffile);
472 * Do preliminary checking for an iscsi device; it may appear as
473 * pure a network device (device_type == "network") if this is
474 * ISWI. This is the case on IBM systems doing an iscsi OFW
477 if (strstr(imagepath, TOK_ISCSI)) {
479 * get the virtual device information from the
480 * "nas-bootdevice" property.
482 if (prom_get_chosen("nas-bootdevice", bootdevice, BOOTDEVSZ)) {
483 DEBUG_F("reset boot-device to"
484 " /chosen/nas-bootdevice = %s\n", bootdevice);
485 device_kind = FILE_DEVICE_ISCSI;
486 ipath = strdup(bootdevice);
493 else if (!(ipath = strdup(imagepath)))
497 defdev = strdup(defdevice);
498 device_kind = prom_get_devtype(defdev);
499 } else if (device_kind == -1)
500 device_kind = prom_get_devtype(ipath);
503 * When an iscsi iqn is present, it may have embedded colons, so
504 * don't parse off anything.
506 if (device_kind != FILE_DEVICE_NET &&
507 device_kind != FILE_DEVICE_ISCSI &&
508 strchr(defdev, ':') != NULL) {
509 if ((ptr = strrchr(defdev, ':')) != NULL)
510 *ptr = 0; /* remove trailing : from defdevice if necessary */
513 /* This will not properly handle an obp-tftp argument list
514 * with elements after the filename; that is handled below.
516 if (device_kind != FILE_DEVICE_NET &&
517 device_kind != FILE_DEVICE_ISCSI &&
518 strchr(ipath, ':') != NULL) {
519 if ((ptr = strrchr(ipath, ',')) != NULL) {
520 char *colon = strrchr(ipath, ':');
521 /* If a ':' occurs *after* a ',', then we assume that there is
523 if (!colon || colon < ptr) {
524 result->file = strdup(ptr+1);
525 /* Trim the filename off */
531 if (device_kind == FILE_DEVICE_NET) {
532 if (strchr(ipath, ':')) {
533 if (extract_netboot_args(ipath, result) == 0)
536 /* If we didn't get a ':' then look only in netinfo */
537 extract_netinfo_args(result);
538 result->file = strdup(ipath);
542 result->dev = netdev_path_to_dev(ipath);
543 } else if (device_kind != FILE_DEVICE_ISCSI &&
544 (ptr = strrchr(ipath, ':')) != NULL) {
546 result->dev = strdup(ipath);
548 result->part = simple_strtol(ptr+1, NULL, 10);
549 } else if (!defdev) {
550 result->dev = strdup(ipath);
551 } else if (strlen(ipath)) {
552 result->file = strdup(ipath);
558 if (!result->dev && defdev)
559 result->dev = strdup(defdev);
561 if (result->part < 0)
562 result->part = defpart;
565 result->file = strdup(deffile);
575 file_block_open( struct boot_file_t* file,
576 struct boot_fspec_t* fspec,
579 struct partition_t* parts;
580 struct partition_t* p;
581 struct partition_t* found;
583 parts = partitions_lookup(fspec->dev);
588 prom_printf("partitions:\n");
590 prom_printf("no partitions found.\n");
592 for (p = parts; p && !found; p=p->next) {
593 DEBUG_F("number: %02d, start: 0x%08lx, length: 0x%08lx\n",
594 p->part_number, p->part_start, p->part_size );
595 if (partition == -1) {
596 file->fs = fs_open( file, p, fspec );
597 if (file->fs == NULL || fserrorno != FILE_ERR_OK)
600 partition = p->part_number;
604 if ((partition >= 0) && (partition == p->part_number))
608 prom_printf(" (match)\n");
612 /* Note: we don't skip when found is NULL since we can, in some
613 * cases, let OF figure out a default partition.
615 DEBUG_F( "Using OF defaults.. (found = %p)\n", found );
616 file->fs = fs_open( file, found, fspec );
620 partitions_free(parts);
626 file_net_open(struct boot_file_t* file, struct boot_fspec_t *fspec)
628 file->fs = fs_of_netboot;
629 return fs_of_netboot->open(file, NULL, fspec);
633 default_read( struct boot_file_t* file,
637 prom_printf("WARNING ! default_read called !\n");
642 default_seek( struct boot_file_t* file,
645 prom_printf("WARNING ! default_seek called !\n");
650 default_close( struct boot_file_t* file)
652 prom_printf("WARNING ! default_close called !\n");
656 static struct fs_t fs_default =
666 int open_file(struct boot_fspec_t* spec, struct boot_file_t* file)
670 memset(file, 0, sizeof(struct boot_file_t*));
671 file->fs = &fs_default;
673 DEBUG_F("dev_path = %s\nfile_name = %s\npartition = %d\n",
674 spec->dev, spec->file, spec->part);
676 result = prom_get_devtype(spec->dev);
678 file->device_kind = result;
682 switch(file->device_kind) {
683 case FILE_DEVICE_BLOCK:
684 DEBUG_F("device is a block device\n");
685 return file_block_open(file, spec, spec->part);
686 case FILE_DEVICE_ISCSI:
687 DEBUG_F("device is a iSCSI device\n");
688 return file_block_open(file, spec, spec->part);
689 case FILE_DEVICE_NET:
690 DEBUG_F("device is a network device\n");
691 return file_net_open(file, spec);
698 * c-file-style: "k&r"