X-Git-Url: http://git.ozlabs.org/?p=yaboot.git;a=blobdiff_plain;f=second%2Ffile.c;h=5d5ccbdf10bd2df10b687fb2d5ae867025a74772;hp=4a44ba36c9458c37776e635060ffa440ae993e31;hb=8e64db245a2d402dc89fa1f68a3789203815b238;hpb=d0e5fbe697b5cf916bf70c1bae736c82cbbc876b;ds=sidebyside diff --git a/second/file.c b/second/file.c index 4a44ba3..5d5ccbd 100644 --- a/second/file.c +++ b/second/file.c @@ -1,7 +1,7 @@ /* * file.c - Filesystem related interfaces * - * Copyright (C) 2001 Ethan Benson + * Copyright (C) 2001, 2002 Ethan Benson * * parse_device_path() * @@ -36,12 +36,219 @@ #include "errors.h" #include "debug.h" -extern char bootdevice[1024]; +extern char bootdevice[]; + +/* Convert __u32 into std, dotted quad string, leaks like a sive :( */ +static char * +ipv4_to_str(__u32 ip) +{ + char *buf = malloc(sizeof("000.000.000.000")); + + sprintf(buf,"%u.%u.%u.%u", + (ip & 0xff000000) >> 24, (ip & 0x00ff0000) >> 16, + (ip & 0x0000ff00) >> 8, (ip & 0x000000ff)); + + return buf; +} + +/* + * Copy the string from source to dest till newline or comma(,) is seen + * in the source. + * Move source and dest pointers respectively. + * Returns pointer to the start of the string that has just been copied. + */ +static char * +scopy(char **dest, char **source) +{ + char *ret = *dest; + + if (!**source) + return NULL; + + while (**source != ',' && **source != '\0') + *(*dest)++ = *(*source)++; + if (**source != '\0') + *(*source)++; + **dest = '\0'; + *(*dest)++; + return ret; +} + +/* + * Extract all the ipv4 arguments from the bootpath provided and fill result + * Returns 1 on success, 0 on failure. + */ +static int +extract_ipv4_args(char *imagepath, struct boot_fspec_t *result) +{ + char *tmp, *args, *str, *start; + + args = strrchr(imagepath, ':'); + if (!args) + return 1; + + start = args; /* used to see if we read any optional parameters */ + + /* The obp-tftp device arguments should be at the end of + * the argument list. Skip over any extra arguments (promiscuous, + * speed, duplex, bootp, rarp). + */ + + tmp = strstr(args, "promiscuous"); + if (tmp && tmp > args) + args = tmp + strlen("promiscuous"); + + tmp = strstr(args, "speed="); + if (tmp && tmp > args) + args = tmp + strlen("speed="); + + tmp = strstr(args, "duplex="); + if (tmp && tmp > args) + args = tmp + strlen("duplex="); + + tmp = strstr(args, "bootp"); + if (tmp && tmp > args) + args = tmp + strlen("bootp"); + + tmp = strstr(args, "rarp"); + if (tmp && tmp > args) + args = tmp + strlen("rarp"); + + if (args != start) /* we read some parameters, so go past the next comma(,) */ + args = strchr(args, ','); + if (!args) + return 1; + + str = malloc(strlen(args) + 1); /*long enough to hold all strings */ + if (!str) + return 0; + + if (args[-1] != ':') + args++; /* If comma(,) is not immediately followed by ':' then go past the , */ + + /* + * read the arguments in order: siaddr,filename,ciaddr,giaddr, + * bootp-retries,tftp-retries,addl_prameters + */ + result->siaddr = scopy(&str, &args); + result->file = scopy(&str, &args); + result->ciaddr = scopy(&str, &args); + result->giaddr = scopy(&str, &args); + result->bootp_retries = scopy(&str, &args); + result->tftp_retries = scopy(&str, &args); + if (*args) { + result->addl_params = strdup(args); + if (!result->addl_params) + return 0; + } + return 1; +} + +/* + * Check netinfo for ipv4 parameters and add them to the fspec iff the + * fspec has no existing value. + * + * Returns 1 on success, 0 on failure. + */ +static int +extract_netinfo_args(struct boot_fspec_t *result) +{ + struct bootp_packet *packet; + + /* Check to see if we can get the [scyg]iaddr fields from netinfo */ + packet = prom_get_netinfo(); + if (!packet) + return 0; + + DEBUG_F("We have a boot packet\n"); + DEBUG_F(" siaddr = <%x>\n", packet->siaddr); + DEBUG_F(" ciaddr = <%x>\n", packet->ciaddr); + DEBUG_F(" yiaddr = <%x>\n", packet->yiaddr); + DEBUG_F(" giaddr = <%x>\n", packet->giaddr); + + /* Try to fallback to yiaddr if ciaddr is empty. Broken? */ + if (packet->ciaddr == 0 && packet->yiaddr != 0) + packet->ciaddr = packet->yiaddr; + + if ((result->siaddr == NULL || *(result->siaddr) == NULL) + && packet->siaddr != 0) + result->siaddr = ipv4_to_str(packet->siaddr); + if ((result->ciaddr == NULL || *(result->ciaddr) == NULL) + && packet->ciaddr != 0) + result->ciaddr = ipv4_to_str(packet->ciaddr); + if ((result->giaddr == NULL || *(result->giaddr) == NULL) + && packet->giaddr != 0) + result->giaddr = ipv4_to_str(packet->giaddr); + + /* FIXME: Yck! if we /still/ do not have a gateway then "cheat" and use + * the server. This will be okay if the client and server are on + * the same IP network, if not then lets hope the server does ICMP + * redirections */ + if (result->giaddr == NULL) { + result->giaddr = ipv4_to_str(packet->siaddr); + DEBUG_F("Forcing giaddr to siaddr <%s>\n", result->giaddr); + } + + return 1; +} + +/* + * Extract all the arguments provided in the imagepath and fill it in result. + * Returns 1 on success, 0 on failure. + */ +static int +extract_netboot_args(char *imagepath, struct boot_fspec_t *result) +{ + int ret; + + DEBUG_F("imagepath = %s\n", imagepath); + + if (!imagepath) + return 1; + + ret = extract_ipv4_args(imagepath, result); + ret |= extract_netinfo_args(result); + + DEBUG_F("siaddr = <%s>\n", result->siaddr); + DEBUG_F("file = <%s>\n", result->file); + DEBUG_F("ciaddr = <%s>\n", result->ciaddr); + DEBUG_F("giaddr = <%s>\n", result->giaddr); + DEBUG_F("bootp_retries = <%s>\n", result->bootp_retries); + DEBUG_F("tftp_retries = <%s>\n", result->tftp_retries); + DEBUG_F("addl_params = <%s>\n", result->addl_params); + + return ret; +} + +static char *netdev_path_to_dev(const char *path) +{ + char *dev, *tmp; + size_t len; + + DEBUG_F("path = %s\n", path); + + if (!path) + return NULL; + + tmp = strchr(path, ':'); + if (!tmp) + return strdup(path); + tmp++; + + len = tmp - path + 1; + + dev = malloc(len); + if (dev) { + strncpy(dev, path, len); + dev[len - 1] = '\0'; + } + return dev; +} /* This function follows the device path in the devtree and separates the device name, partition number, and other datas (mostly file name) the string passed in parameters is changed since 0 are put in place - of some separators to terminate the various strings. + of some separators to terminate the various strings. when a default device is supplied imagepath will be assumed to be a plain filename unless it contains a : otherwise if defaultdev is @@ -57,6 +264,10 @@ extern char bootdevice[1024]; - enet:,/tftpboot/vmlinux - enet:bootp - enet:0 + - arguments for obp-tftp open as specified in section 4.1 of + http://playground.sun.com/1275/practice/obp-tftp/tftp1_0.pdf + [bootp,]siaddr,filename,ciaddr,giaddr,bootp-retries,tftp-retries + ex: enet:bootp,10.0.0.11,bootme,10.0.0.12,10.0.0.1,5,5 Supported only if defdevice == NULL - disc - any other device path lacking a : @@ -71,6 +282,10 @@ parse_device_path(char *imagepath, char *defdevice, int defpart, char *ptr; char *ipath = NULL; char *defdev = NULL; + int device_kind = -1; + + DEBUG_F("imagepath = %s; defdevice %s; defpart %d, deffile %s\n", + imagepath, defdevice, defpart, deffile); result->dev = NULL; result->part = -1; @@ -78,23 +293,55 @@ parse_device_path(char *imagepath, char *defdevice, int defpart, if (!imagepath) return 0; - else - ipath = strdup(imagepath); - if (defdevice) + /* + * Do preliminary checking for an iscsi device; it may appear as + * pure a network device (device_type == "network") if this is + * ISWI. This is the case on IBM systems doing an iscsi OFW + * boot. + */ + if (strstr(imagepath, TOK_ISCSI)) { + /* + * get the virtual device information from the + * "nas-bootdevice" property. + */ + if (prom_get_chosen("nas-bootdevice", bootdevice, BOOTDEVSZ)) { + DEBUG_F("reset boot-device to" + " /chosen/nas-bootdevice = %s\n", bootdevice); + device_kind = FILE_DEVICE_ISCSI; + ipath = strdup(bootdevice); + if (!ipath) + return 0; + } + else + return 0; + } + else if (!(ipath = strdup(imagepath))) + return 0; + + if (defdevice) { defdev = strdup(defdevice); + device_kind = prom_get_devtype(defdev); + } else if (device_kind == -1) + device_kind = prom_get_devtype(ipath); - if (defdev) { - if (!strstr(defdev, "ethernet") && !strstr(defdev, "enet")) { - if ((ptr = strrchr(defdev, ':')) != NULL) - *ptr = 0; /* remove trailing : from defdevice if necessary */ - } + /* + * When an iscsi iqn is present, it may have embedded colons, so + * don't parse off anything. + */ + if (device_kind != FILE_DEVICE_NET && + device_kind != FILE_DEVICE_ISCSI && + strchr(defdev, ':') != NULL) { + if ((ptr = strrchr(defdev, ':')) != NULL) + *ptr = 0; /* remove trailing : from defdevice if necessary */ } - /* if there is no : then there is no filename or partition. must - use strrchr() since enet:,10.0.0.1,file is legal */ - - if (strchr(ipath, ':') != NULL) { + /* This will not properly handle an obp-tftp argument list + * with elements after the filename; that is handled below. + */ + if (device_kind != FILE_DEVICE_NET && + device_kind != FILE_DEVICE_ISCSI && + strchr(ipath, ':') != NULL) { if ((ptr = strrchr(ipath, ',')) != NULL) { char *colon = strrchr(ipath, ':'); /* If a ':' occurs *after* a ',', then we assume that there is @@ -107,54 +354,61 @@ parse_device_path(char *imagepath, char *defdevice, int defpart, } } - if (strstr(ipath, "ethernet") || strstr(ipath, "enet")) - if ((ptr = strstr(ipath, "bootp")) != NULL) { /* `n' key booting boots enet:bootp */ - *ptr = 0; - result->dev = strdup(ipath); - } else - result->dev = strdup(ipath); - else if ((ptr = strchr(ipath, ':')) != NULL) { + if (device_kind == FILE_DEVICE_NET) { + if (strchr(ipath, ':')) { + if (extract_netboot_args(ipath, result) == 0) + return 0; + } else { + /* If we didn't get a ':' then look only in netinfo */ + extract_netinfo_args(result); + result->file = strdup(ipath); + } + + if (!defdev) + result->dev = netdev_path_to_dev(ipath); + } else if (device_kind != FILE_DEVICE_ISCSI && + (ptr = strrchr(ipath, ':')) != NULL) { *ptr = 0; result->dev = strdup(ipath); if (*(ptr+1)) result->part = simple_strtol(ptr+1, NULL, 10); } else if (!defdev) { - result->dev = strdup(ipath); + result->dev = strdup(ipath); } else if (strlen(ipath)) { result->file = strdup(ipath); } else { + free(defdev); return 0; } if (!result->dev && defdev) result->dev = strdup(defdev); - + if (result->part < 0) result->part = defpart; - + if (!result->file) result->file = strdup(deffile); free(ipath); if (defdev) - free(defdev); + free(defdev); return 1; } static int file_block_open( struct boot_file_t* file, - const char* dev_name, - const char* file_name, + struct boot_fspec_t* fspec, int partition) { struct partition_t* parts; struct partition_t* p; struct partition_t* found; - - parts = partitions_lookup(dev_name); + + parts = partitions_lookup(fspec->dev); found = NULL; - + #if DEBUG if (parts) prom_printf("partitions:\n"); @@ -165,25 +419,29 @@ file_block_open( struct boot_file_t* file, DEBUG_F("number: %02d, start: 0x%08lx, length: 0x%08lx\n", p->part_number, p->part_start, p->part_size ); if (partition == -1) { - file->fs = fs_open( file, dev_name, p, file_name ); - if (file->fs != FILE_ERR_OK) - goto bail; + file->fs = fs_open( file, p, fspec ); + if (file->fs == NULL || fserrorno != FILE_ERR_OK) + continue; + else { + partition = p->part_number; + goto done; + } } if ((partition >= 0) && (partition == p->part_number)) found = p; #if DEBUG if (found) prom_printf(" (match)\n"); -#endif +#endif } /* Note: we don't skip when found is NULL since we can, in some * cases, let OF figure out a default partition. */ DEBUG_F( "Using OF defaults.. (found = %p)\n", found ); - file->fs = fs_open( file, dev_name, found, file_name ); + file->fs = fs_open( file, found, fspec ); -bail: +done: if (parts) partitions_free(parts); @@ -191,12 +449,10 @@ bail: } static int -file_net_open( struct boot_file_t* file, - const char* dev_name, - const char* file_name) +file_net_open(struct boot_file_t* file, struct boot_fspec_t *fspec) { file->fs = fs_of_netboot; - return fs_of_netboot->open(file, dev_name, NULL, file_name); + return fs_of_netboot->open(file, NULL, fspec); } static int @@ -233,10 +489,10 @@ static struct fs_t fs_default = }; -int open_file(const struct boot_fspec_t* spec, struct boot_file_t* file) +int open_file(struct boot_fspec_t* spec, struct boot_file_t* file) { int result; - + memset(file, 0, sizeof(struct boot_file_t*)); file->fs = &fs_default; @@ -248,19 +504,19 @@ int open_file(const struct boot_fspec_t* spec, struct boot_file_t* file) file->device_kind = result; else return result; - + switch(file->device_kind) { case FILE_DEVICE_BLOCK: DEBUG_F("device is a block device\n"); - return file_block_open(file, spec->dev, spec->file, spec->part); + return file_block_open(file, spec, spec->part); case FILE_DEVICE_NET: DEBUG_F("device is a network device\n"); - return file_net_open(file, spec->dev, spec->file); + return file_net_open(file, spec); } return 0; } -/* +/* * Local variables: * c-file-style: "k&r" * c-basic-offset: 5