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.
266 extract_netinfo_args(struct boot_fspec_t *result)
268 struct bootp_packet *packet;
270 /* Check to see if we can get the [scyg]iaddr fields from netinfo */
271 packet = prom_get_netinfo();
275 DEBUG_F("We have a boot packet\n");
276 DEBUG_F(" siaddr = <%x>\n", packet->siaddr);
277 DEBUG_F(" ciaddr = <%x>\n", packet->ciaddr);
278 DEBUG_F(" yiaddr = <%x>\n", packet->yiaddr);
279 DEBUG_F(" giaddr = <%x>\n", packet->giaddr);
281 /* Try to fallback to yiaddr if ciaddr is empty. Broken? */
282 if (packet->ciaddr == 0 && packet->yiaddr != 0)
283 packet->ciaddr = packet->yiaddr;
285 if ((result->siaddr == NULL || *(result->siaddr) == '\x0')
286 && packet->siaddr != 0)
287 result->siaddr = ipv4_to_str(packet->siaddr);
288 if ((result->ciaddr == NULL || *(result->ciaddr) == '\x0')
289 && packet->ciaddr != 0)
290 result->ciaddr = ipv4_to_str(packet->ciaddr);
291 if ((result->giaddr == NULL || *(result->giaddr) == '\x0')
292 && packet->giaddr != 0)
293 result->giaddr = ipv4_to_str(packet->giaddr);
295 extract_vendor_options(packet, result);
297 /* FIXME: Yck! if we /still/ do not have a gateway then "cheat" and use
298 * the server. This will be okay if the client and server are on
299 * the same IP network, if not then lets hope the server does ICMP
301 if (result->giaddr == NULL) {
302 result->giaddr = ipv4_to_str(packet->siaddr);
303 DEBUG_F("Forcing giaddr to siaddr <%s>\n", result->giaddr);
308 * Extract all the ipv6 arguments from the bootpath provided and fill result
309 * Syntax: ipv6,[dhcpv6[=diaddr,]]ciaddr=c_iaddr,giaddr=g_iaddr,siaddr=s_iaddr,
310 * filename=file_name,tftp-retries=tftp_retries,blksize=block_size
311 * Returns 1 on success, 0 on failure.
314 extract_ipv6_args(char *imagepath, struct boot_fspec_t *result)
321 /* Just allocate the max required size */
322 total_len = strlen(imagepath) + 1;
323 str = malloc(total_len);
327 if ((tmp = strstr(imagepath, "dhcpv6=")) != NULL)
328 result->dhcpv6 = scopy(&str, &tmp);
330 if ((tmp = strstr(imagepath, "ciaddr=")) != NULL)
331 result->ciaddr = scopy(&str, &tmp);
333 if ((tmp = strstr(imagepath, "giaddr=")) != NULL)
334 result->giaddr = scopy(&str, &tmp);
336 if ((tmp = strstr(imagepath, "siaddr=")) != NULL)
337 result->siaddr = scopy(&str, &tmp);
339 if ((tmp = strstr(imagepath, "filename=")) != NULL)
340 result->file = scopy(&str, &tmp);
342 if ((tmp = strstr(imagepath, "tftp-retries=")) != NULL)
343 result->tftp_retries = scopy(&str, &tmp);
345 if ((tmp = strstr(imagepath, "blksize=")) != NULL)
346 result->blksize = scopy(&str, &tmp);
352 * Extract all the arguments provided in the imagepath and fill it in result.
353 * Returns 1 on success, 0 on failure.
356 extract_netboot_args(char *imagepath, struct boot_fspec_t *result)
360 DEBUG_F("imagepath = %s\n", imagepath);
365 if (strstr(imagepath, TOK_IPV6))
366 ret = extract_ipv6_args(imagepath, result);
368 ret = extract_ipv4_args(imagepath, result);
369 extract_netinfo_args(result);
371 DEBUG_F("ipv6 = <%d>\n", result->is_ipv6);
372 DEBUG_F("siaddr = <%s>\n", result->siaddr);
373 DEBUG_F("file = <%s>\n", result->file);
374 DEBUG_F("ciaddr = <%s>\n", result->ciaddr);
375 DEBUG_F("giaddr = <%s>\n", result->giaddr);
376 DEBUG_F("bootp_retries = <%s>\n", result->bootp_retries);
377 DEBUG_F("tftp_retries = <%s>\n", result->tftp_retries);
378 DEBUG_F("addl_params = <%s>\n", result->addl_params);
379 DEBUG_F("dhcpv6 = <%s>\n", result->dhcpv6);
380 DEBUG_F("blksize = <%s>\n", result->blksize);
385 static char *netdev_path_to_dev(const char *path)
390 DEBUG_F("path = %s\n", path);
395 tmp = strchr(path, ':');
400 len = tmp - path + 1;
404 strncpy(dev, path, len);
410 /* This function follows the device path in the devtree and separates
411 the device name, partition number, and other datas (mostly file name)
412 the string passed in parameters is changed since 0 are put in place
413 of some separators to terminate the various strings.
415 when a default device is supplied imagepath will be assumed to be a
416 plain filename unless it contains a : otherwise if defaultdev is
417 NULL imagepath will be assumed to be a device path.
419 returns 1 on success 0 on failure.
422 - /pci@80000000/pci-bridge@d/ADPT,2930CU@2/@1:4
423 - /pci@80000000/pci-bridge@d/ADPT,2930CU@2/@1:4,/boot/vmlinux
425 - enet:10.0.0.1,/tftpboot/vmlinux
426 - enet:,/tftpboot/vmlinux
429 - arguments for obp-tftp open as specified in section 4.1 of
430 http://playground.sun.com/1275/practice/obp-tftp/tftp1_0.pdf
431 [bootp,]siaddr,filename,ciaddr,giaddr,bootp-retries,tftp-retries
432 ex: enet:bootp,10.0.0.11,bootme,10.0.0.12,10.0.0.1,5,5
433 Supported only if defdevice == NULL
435 - any other device path lacking a :
436 Unsupported examples:
437 - hd:2,\\:tbxi <- no filename will be detected due to the extra :
438 - enet:192.168.2.1,bootme,c-iaddr,g-iaddr,subnet-mask,bootp-retries,tftp-retries */
441 parse_device_path(char *imagepath, char *defdevice, int defpart,
442 char *deffile, struct boot_fspec_t *result)
447 int device_kind = -1;
449 DEBUG_F("imagepath = %s; defdevice %s; defpart %d, deffile %s\n",
450 imagepath, defdevice, defpart, deffile);
460 * Do preliminary checking for an iscsi device; it may appear as
461 * pure a network device (device_type == "network") if this is
462 * ISWI. This is the case on IBM systems doing an iscsi OFW
465 if (strstr(imagepath, TOK_ISCSI)) {
467 * get the virtual device information from the
468 * "nas-bootdevice" property.
470 if (prom_get_chosen("nas-bootdevice", bootdevice, BOOTDEVSZ)) {
471 DEBUG_F("reset boot-device to"
472 " /chosen/nas-bootdevice = %s\n", bootdevice);
473 device_kind = FILE_DEVICE_ISCSI;
474 ipath = strdup(bootdevice);
481 else if (!(ipath = strdup(imagepath)))
485 defdev = strdup(defdevice);
486 device_kind = prom_get_devtype(defdev);
487 } else if (device_kind == -1)
488 device_kind = prom_get_devtype(ipath);
491 * When an iscsi iqn is present, it may have embedded colons, so
492 * don't parse off anything.
494 if (device_kind != FILE_DEVICE_NET &&
495 device_kind != FILE_DEVICE_ISCSI &&
496 strchr(defdev, ':') != NULL) {
497 if ((ptr = strrchr(defdev, ':')) != NULL)
498 *ptr = 0; /* remove trailing : from defdevice if necessary */
501 /* This will not properly handle an obp-tftp argument list
502 * with elements after the filename; that is handled below.
504 if (device_kind != FILE_DEVICE_NET &&
505 device_kind != FILE_DEVICE_ISCSI &&
506 strchr(ipath, ':') != NULL) {
507 if ((ptr = strrchr(ipath, ',')) != NULL) {
508 char *colon = strrchr(ipath, ':');
509 /* If a ':' occurs *after* a ',', then we assume that there is
511 if (!colon || colon < ptr) {
512 result->file = strdup(ptr+1);
513 /* Trim the filename off */
519 if (device_kind == FILE_DEVICE_NET) {
520 if (strchr(ipath, ':')) {
521 if (extract_netboot_args(ipath, result) == 0)
524 /* If we didn't get a ':' then look only in netinfo */
525 extract_netinfo_args(result);
526 result->file = strdup(ipath);
530 result->dev = netdev_path_to_dev(ipath);
531 } else if (device_kind != FILE_DEVICE_ISCSI &&
532 (ptr = strrchr(ipath, ':')) != NULL) {
534 result->dev = strdup(ipath);
536 result->part = simple_strtol(ptr+1, NULL, 10);
537 } else if (!defdev) {
538 result->dev = strdup(ipath);
539 } else if (strlen(ipath)) {
540 result->file = strdup(ipath);
546 if (!result->dev && defdev)
547 result->dev = strdup(defdev);
549 if (result->part < 0)
550 result->part = defpart;
553 result->file = strdup(deffile);
563 file_block_open( struct boot_file_t* file,
564 struct boot_fspec_t* fspec,
567 struct partition_t* parts;
568 struct partition_t* p;
569 struct partition_t* found;
571 parts = partitions_lookup(fspec->dev);
576 prom_printf("partitions:\n");
578 prom_printf("no partitions found.\n");
580 for (p = parts; p && !found; p=p->next) {
581 DEBUG_F("number: %02d, start: 0x%08lx, length: 0x%08lx\n",
582 p->part_number, p->part_start, p->part_size );
583 if (partition == -1) {
584 file->fs = fs_open( file, p, fspec );
585 if (file->fs == NULL || fserrorno != FILE_ERR_OK)
588 partition = p->part_number;
592 if ((partition >= 0) && (partition == p->part_number))
596 prom_printf(" (match)\n");
600 /* Note: we don't skip when found is NULL since we can, in some
601 * cases, let OF figure out a default partition.
603 DEBUG_F( "Using OF defaults.. (found = %p)\n", found );
604 file->fs = fs_open( file, found, fspec );
608 partitions_free(parts);
614 file_net_open(struct boot_file_t* file, struct boot_fspec_t *fspec)
616 file->fs = fs_of_netboot;
617 return fs_of_netboot->open(file, NULL, fspec);
621 default_read( struct boot_file_t* file,
625 prom_printf("WARNING ! default_read called !\n");
630 default_seek( struct boot_file_t* file,
633 prom_printf("WARNING ! default_seek called !\n");
638 default_close( struct boot_file_t* file)
640 prom_printf("WARNING ! default_close called !\n");
644 static struct fs_t fs_default =
654 int open_file(struct boot_fspec_t* spec, struct boot_file_t* file)
658 memset(file, 0, sizeof(struct boot_file_t*));
659 file->fs = &fs_default;
661 DEBUG_F("dev_path = %s\nfile_name = %s\npartition = %d\n",
662 spec->dev, spec->file, spec->part);
664 result = prom_get_devtype(spec->dev);
666 file->device_kind = result;
670 switch(file->device_kind) {
671 case FILE_DEVICE_BLOCK:
672 DEBUG_F("device is a block device\n");
673 return file_block_open(file, spec, spec->part);
674 case FILE_DEVICE_NET:
675 DEBUG_F("device is a network device\n");
676 return file_net_open(file, spec);
683 * c-file-style: "k&r"