Prepare for netboot fix(following patch). No Functional change.
[yaboot.git] / second / file.c
1 /*
2  *  file.c - Filesystem related interfaces
3  *
4  *  Copyright (C) 2001, 2002 Ethan Benson
5  *
6  *  parse_device_path()
7  *
8  *  Copyright (C) 2001 Colin Walters
9  *
10  *  Copyright (C) 1999 Benjamin Herrenschmidt
11  *
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.
16  *
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.
21  *
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.
25  */
26
27 #include "ctype.h"
28 #include "types.h"
29 #include "stddef.h"
30 #include "stdlib.h"
31 #include "file.h"
32 #include "prom.h"
33 #include "string.h"
34 #include "partition.h"
35 #include "fs.h"
36 #include "errors.h"
37 #include "debug.h"
38
39 extern char bootdevice[];
40
41 static char *netdev_path_to_filename(const char *path)
42 {
43      char *tmp, *args, *filename;
44      size_t len;
45
46      DEBUG_F("path = %s\n", path);
47
48      if (!path)
49           return NULL;
50
51      args = strrchr(path, ':');
52      if (!args)
53           return NULL;
54
55      /* The obp-tftp device arguments should be at the end of
56       * the argument list.  Skip over any extra arguments (promiscuous,
57       * speed, duplex, bootp, rarp).
58       */
59
60      tmp = strstr(args, "promiscuous");
61      if (tmp && tmp > args)
62           args = tmp + strlen("promiscuous");
63
64      tmp = strstr(args, "speed=");
65      if (tmp && tmp > args)
66           args = tmp + strlen("speed=");
67
68      tmp = strstr(args, "duplex=");
69      if (tmp && tmp > args)
70           args = tmp + strlen("duplex=");
71
72      tmp = strstr(args, "bootp");
73      if (tmp && tmp > args)
74           args = tmp + strlen("bootp");
75
76      tmp = strstr(args, "rarp");
77      if (tmp && tmp > args)
78           args = tmp + strlen("rarp");
79
80      args = strchr(args, ',');
81      if (!args)
82           return NULL;
83
84      tmp = args;
85      tmp--;
86      /* If the preceding character is ':' then there were no
87       * non-obp-tftp arguments and we know we're right up to the
88       * filename.  Otherwise, we must advance args once more.
89       */
90      args++;
91      if (*tmp != ':') {
92           args = strchr(args, ',');
93           if (!args)
94                return NULL;
95           args++;
96      }
97
98      /* filename may be empty; e.g. enet:192.168.1.1,,192.168.1.2 */
99      if (*args == ',') {
100           DEBUG_F("null filename\n");
101           return NULL;
102      }
103
104      /* Now see whether there are more args following the filename. */
105      tmp = strchr(args, ',');
106      if (!tmp)
107           len = strlen(args) + 1;
108      else
109           len = tmp - args + 1;
110
111      filename = malloc(len);
112      if (!filename)
113           return NULL;
114
115      strncpy(filename, args, len);
116      filename[len - 1] = '\0';
117
118      DEBUG_F("filename = %s\n", filename);
119      return filename;
120 }
121
122 static char *netdev_path_to_dev(const char *path)
123 {
124      char *dev, *tmp;
125      size_t len;
126
127      DEBUG_F("path = %s\n", path);
128
129      if (!path)
130           return NULL;
131
132      tmp = strchr(path, ':');
133      if (!tmp)
134           return strdup(path);
135      tmp++;
136
137      len = tmp - path + 1;
138
139      dev = malloc(len);
140      if (dev) {
141           strncpy(dev, path, len);
142           dev[len - 1] = '\0';
143      }
144      return dev;
145 }
146
147 /* This function follows the device path in the devtree and separates
148    the device name, partition number, and other datas (mostly file name)
149    the string passed in parameters is changed since 0 are put in place
150    of some separators to terminate the various strings.
151
152    when a default device is supplied imagepath will be assumed to be a
153    plain filename unless it contains a : otherwise if defaultdev is
154    NULL imagepath will be assumed to be a device path.
155
156    returns 1 on success 0 on failure.
157
158    Supported examples:
159     - /pci@80000000/pci-bridge@d/ADPT,2930CU@2/@1:4
160     - /pci@80000000/pci-bridge@d/ADPT,2930CU@2/@1:4,/boot/vmlinux
161     - hd:3,/boot/vmlinux
162     - enet:10.0.0.1,/tftpboot/vmlinux
163     - enet:,/tftpboot/vmlinux
164     - enet:bootp
165     - enet:0
166    Supported only if defdevice == NULL
167     - disc
168     - any other device path lacking a :
169    Unsupported examples:
170     - hd:2,\\:tbxi <- no filename will be detected due to the extra :
171     - enet:192.168.2.1,bootme,c-iaddr,g-iaddr,subnet-mask,bootp-retries,tftp-retries */
172
173 int
174 parse_device_path(char *imagepath, char *defdevice, int defpart,
175                   char *deffile, struct boot_fspec_t *result)
176 {
177      char *ptr;
178      char *ipath = NULL;
179      char *defdev = NULL;
180      int device_kind = -1;
181
182      result->dev = NULL;
183      result->part = -1;
184      result->file = NULL;
185
186      if (!imagepath)
187           return 0;
188
189       /*
190        * Do preliminary checking for an iscsi device; it may appear as
191        * pure a network device (device_type == "network") if this is
192        * ISWI.  This is the case on IBM systems doing an iscsi OFW
193        * boot.
194        */
195      if (strstr(imagepath, TOK_ISCSI)) {
196           /*
197            * get the virtual device information from the
198            * "nas-bootdevice" property.
199            */
200           if (prom_get_chosen("nas-bootdevice", bootdevice, BOOTDEVSZ)) {
201                DEBUG_F("reset boot-device to"
202                        " /chosen/nas-bootdevice = %s\n", bootdevice);
203                device_kind = FILE_DEVICE_ISCSI;
204                ipath = strdup(bootdevice);
205                if (!ipath)
206                     return 0;
207           }
208           else
209                return 0;
210      }
211      else if (!(ipath = strdup(imagepath)))
212           return 0;
213
214      if (defdevice) {
215           defdev = strdup(defdevice);
216           device_kind = prom_get_devtype(defdev);
217      } else if (device_kind == -1)
218           device_kind = prom_get_devtype(ipath);
219
220      /*
221       * When an iscsi iqn is present, it may have embedded colons, so
222       * don't parse off anything.
223       */
224      if (device_kind != FILE_DEVICE_NET &&
225          device_kind != FILE_DEVICE_ISCSI &&
226          strchr(defdev, ':') != NULL) {
227            if ((ptr = strrchr(defdev, ':')) != NULL)
228                 *ptr = 0; /* remove trailing : from defdevice if necessary */
229      }
230
231      /* This will not properly handle an obp-tftp argument list
232       * with elements after the filename; that is handled below.
233       */
234      if (device_kind != FILE_DEVICE_NET &&
235          device_kind != FILE_DEVICE_ISCSI &&
236          strchr(ipath, ':') != NULL) {
237           if ((ptr = strrchr(ipath, ',')) != NULL) {
238                char *colon = strrchr(ipath, ':');
239                /* If a ':' occurs *after* a ',', then we assume that there is
240                   no filename */
241                if (!colon || colon < ptr) {
242                     result->file = strdup(ptr+1);
243                     /* Trim the filename off */
244                     *ptr = 0;
245                }
246           }
247      }
248
249      if (device_kind == FILE_DEVICE_NET) {
250           if (strchr(ipath, ':'))
251                result->file = netdev_path_to_filename(ipath);
252           else
253                result->file = strdup(ipath);
254
255           if (!defdev)
256                result->dev = netdev_path_to_dev(ipath);
257      } else if (device_kind != FILE_DEVICE_ISCSI &&
258                 (ptr = strrchr(ipath, ':')) != NULL) {
259           *ptr = 0;
260           result->dev = strdup(ipath);
261           if (*(ptr+1))
262                result->part = simple_strtol(ptr+1, NULL, 10);
263      } else if (!defdev) {
264           result->dev = strdup(ipath);
265      } else if (strlen(ipath)) {
266           result->file = strdup(ipath);
267      } else {
268           free(defdev);
269           return 0;
270      }
271
272      if (!result->dev && defdev)
273           result->dev = strdup(defdev);
274
275      if (result->part < 0)
276           result->part = defpart;
277
278      if (!result->file)
279           result->file = strdup(deffile);
280
281      free(ipath);
282      if (defdev)
283           free(defdev);
284      return 1;
285 }
286
287
288 static int
289 file_block_open(        struct boot_file_t*     file,
290                         struct boot_fspec_t*    fspec,
291                         int                     partition)
292 {
293      struct partition_t*        parts;
294      struct partition_t*        p;
295      struct partition_t*        found;
296
297      parts = partitions_lookup(fspec->dev);
298      found = NULL;
299
300 #if DEBUG
301      if (parts)
302           prom_printf("partitions:\n");
303      else
304           prom_printf("no partitions found.\n");
305 #endif
306      for (p = parts; p && !found; p=p->next) {
307           DEBUG_F("number: %02d, start: 0x%08lx, length: 0x%08lx\n",
308                   p->part_number, p->part_start, p->part_size );
309           if (partition == -1) {
310                file->fs = fs_open( file, p, fspec );
311                if (file->fs == NULL || fserrorno != FILE_ERR_OK)
312                     continue;
313                else {
314                     partition = p->part_number;
315                     goto done;
316                }
317           }
318           if ((partition >= 0) && (partition == p->part_number))
319                found = p;
320 #if DEBUG
321           if (found)
322                prom_printf(" (match)\n");
323 #endif
324      }
325
326      /* Note: we don't skip when found is NULL since we can, in some
327       * cases, let OF figure out a default partition.
328       */
329      DEBUG_F( "Using OF defaults.. (found = %p)\n", found );
330      file->fs = fs_open( file, found, fspec );
331
332 done:
333      if (parts)
334           partitions_free(parts);
335
336      return fserrorno;
337 }
338
339 static int
340 file_net_open(struct boot_file_t* file, struct boot_fspec_t *fspec)
341 {
342      file->fs = fs_of_netboot;
343      return fs_of_netboot->open(file, NULL, fspec);
344 }
345
346 static int
347 default_read(   struct boot_file_t*     file,
348                 unsigned int            size,
349                 void*                   buffer)
350 {
351      prom_printf("WARNING ! default_read called !\n");
352      return FILE_ERR_EOF;
353 }
354
355 static int
356 default_seek(   struct boot_file_t*     file,
357                 unsigned int            newpos)
358 {
359      prom_printf("WARNING ! default_seek called !\n");
360      return FILE_ERR_EOF;
361 }
362
363 static int
364 default_close(  struct boot_file_t*     file)
365 {
366      prom_printf("WARNING ! default_close called !\n");
367      return FILE_ERR_OK;
368 }
369
370 static struct fs_t fs_default =
371 {
372      "defaults",
373      NULL,
374      default_read,
375      default_seek,
376      default_close
377 };
378
379
380 int open_file(struct boot_fspec_t* spec, struct boot_file_t* file)
381 {
382      int result;
383
384      memset(file, 0, sizeof(struct boot_file_t*));
385      file->fs        = &fs_default;
386
387      DEBUG_F("dev_path = %s\nfile_name = %s\npartition = %d\n",
388              spec->dev, spec->file, spec->part);
389
390      result = prom_get_devtype(spec->dev);
391      if (result > 0)
392           file->device_kind = result;
393      else
394           return result;
395
396      switch(file->device_kind) {
397      case FILE_DEVICE_BLOCK:
398           DEBUG_F("device is a block device\n");
399           return file_block_open(file, spec, spec->part);
400      case FILE_DEVICE_NET:
401           DEBUG_F("device is a network device\n");
402           return file_net_open(file, spec);
403      }
404      return 0;
405 }
406
407 /*
408  * Local variables:
409  * c-file-style: "k&r"
410  * c-basic-offset: 5
411  * End:
412  */