blob: 4054dd18ca24a3746990f3c19bc527a4ee7bfab7 [file] [log] [blame] [raw]
/*
* file.c - Filesystem related interfaces
*
* Copyright (C) 2001, 2002 Ethan Benson
*
* parse_device_path()
*
* Copyright (C) 2001 Colin Walters
*
* Copyright (C) 1999 Benjamin Herrenschmidt
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#include "ctype.h"
#include "types.h"
#include "stddef.h"
#include "stdlib.h"
#include "file.h"
#include "prom.h"
#include "string.h"
#include "partition.h"
#include "fs.h"
#include "errors.h"
#include "debug.h"
extern char bootdevice[];
static char *netdev_path_to_filename(const char *path)
{
char *tmp, *args, *filename;
size_t len;
DEBUG_F("path = %s\n", path);
if (!path)
return NULL;
args = strrchr(path, ':');
if (!args)
return NULL;
/* 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");
args = strchr(args, ',');
if (!args)
return NULL;
tmp = args;
tmp--;
/* If the preceding character is ':' then there were no
* non-obp-tftp arguments and we know we're right up to the
* filename. Otherwise, we must advance args once more.
*/
args++;
if (*tmp != ':') {
args = strchr(args, ',');
if (!args)
return NULL;
args++;
}
/* filename may be empty; e.g. enet:192.168.1.1,,192.168.1.2 */
if (*args == ',') {
DEBUG_F("null filename\n");
return NULL;
}
/* Now see whether there are more args following the filename. */
tmp = strchr(args, ',');
if (!tmp)
len = strlen(args) + 1;
else
len = tmp - args + 1;
filename = malloc(len);
if (!filename)
return NULL;
strncpy(filename, args, len);
filename[len - 1] = '\0';
DEBUG_F("filename = %s\n", filename);
return filename;
}
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.
when a default device is supplied imagepath will be assumed to be a
plain filename unless it contains a : otherwise if defaultdev is
NULL imagepath will be assumed to be a device path.
returns 1 on success 0 on failure.
Supported examples:
- /pci@80000000/pci-bridge@d/ADPT,2930CU@2/@1:4
- /pci@80000000/pci-bridge@d/ADPT,2930CU@2/@1:4,/boot/vmlinux
- hd:3,/boot/vmlinux
- enet:10.0.0.1,/tftpboot/vmlinux
- enet:,/tftpboot/vmlinux
- enet:bootp
- enet:0
Supported only if defdevice == NULL
- disc
- any other device path lacking a :
Unsupported examples:
- hd:2,\\:tbxi <- no filename will be detected due to the extra :
- enet:192.168.2.1,bootme,c-iaddr,g-iaddr,subnet-mask,bootp-retries,tftp-retries */
int
parse_device_path(char *imagepath, char *defdevice, int defpart,
char *deffile, struct boot_fspec_t *result)
{
char *ptr;
char *ipath = NULL;
char *defdev = NULL;
int device_kind = -1;
result->dev = NULL;
result->part = -1;
result->file = NULL;
if (!imagepath)
return 0;
/*
* 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);
/*
* 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 */
}
/* 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
no filename */
if (!colon || colon < ptr) {
result->file = strdup(ptr+1);
/* Trim the filename off */
*ptr = 0;
}
}
}
if (device_kind == FILE_DEVICE_NET) {
if (strchr(ipath, ':'))
result->file = netdev_path_to_filename(ipath);
else
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);
} 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);
return 1;
}
static int
file_block_open( struct boot_file_t* file,
const char* dev_name,
const char* file_name,
int partition)
{
struct partition_t* parts;
struct partition_t* p;
struct partition_t* found;
parts = partitions_lookup(dev_name);
found = NULL;
#if DEBUG
if (parts)
prom_printf("partitions:\n");
else
prom_printf("no partitions found.\n");
#endif
for (p = parts; p && !found; p=p->next) {
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 == 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
}
/* 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 );
done:
if (parts)
partitions_free(parts);
return fserrorno;
}
static int
file_net_open( struct boot_file_t* file,
const char* dev_name,
const char* file_name)
{
file->fs = fs_of_netboot;
return fs_of_netboot->open(file, dev_name, NULL, file_name);
}
static int
default_read( struct boot_file_t* file,
unsigned int size,
void* buffer)
{
prom_printf("WARNING ! default_read called !\n");
return FILE_ERR_EOF;
}
static int
default_seek( struct boot_file_t* file,
unsigned int newpos)
{
prom_printf("WARNING ! default_seek called !\n");
return FILE_ERR_EOF;
}
static int
default_close( struct boot_file_t* file)
{
prom_printf("WARNING ! default_close called !\n");
return FILE_ERR_OK;
}
static struct fs_t fs_default =
{
"defaults",
NULL,
default_read,
default_seek,
default_close
};
int open_file(const struct boot_fspec_t* spec, struct boot_file_t* file)
{
int result;
memset(file, 0, sizeof(struct boot_file_t*));
file->fs = &fs_default;
DEBUG_F("dev_path = %s\nfile_name = %s\npartition = %d\n",
spec->dev, spec->file, spec->part);
result = prom_get_devtype(spec->dev);
if (result > 0)
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);
case FILE_DEVICE_NET:
DEBUG_F("device is a network device\n");
return file_net_open(file, spec->dev, spec->file);
}
return 0;
}
/*
* Local variables:
* c-file-style: "k&r"
* c-basic-offset: 5
* End:
*/