Compile fix
[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                         const char*             dev_name,
291                         const char*             file_name,
292                         int                     partition)
293 {
294      struct partition_t*        parts;
295      struct partition_t*        p;
296      struct partition_t*        found;
297
298      parts = partitions_lookup(dev_name);
299      found = NULL;
300
301 #if DEBUG
302      if (parts)
303           prom_printf("partitions:\n");
304      else
305           prom_printf("no partitions found.\n");
306 #endif
307      for (p = parts; p && !found; p=p->next) {
308           DEBUG_F("number: %02d, start: 0x%08lx, length: 0x%08lx\n",
309                   p->part_number, p->part_start, p->part_size );
310           if (partition == -1) {
311                file->fs = fs_open( file, dev_name, p, file_name );
312                if (file->fs == NULL || fserrorno != FILE_ERR_OK)
313                     continue;
314                else {
315                     partition = p->part_number;
316                     goto done;
317                }
318           }
319           if ((partition >= 0) && (partition == p->part_number))
320                found = p;
321 #if DEBUG
322           if (found)
323                prom_printf(" (match)\n");
324 #endif
325      }
326
327      /* Note: we don't skip when found is NULL since we can, in some
328       * cases, let OF figure out a default partition.
329       */
330      DEBUG_F( "Using OF defaults.. (found = %p)\n", found );
331      file->fs = fs_open( file, dev_name, found, file_name );
332
333 done:
334      if (parts)
335           partitions_free(parts);
336
337      return fserrorno;
338 }
339
340 static int
341 file_net_open(  struct boot_file_t*     file,
342                 const char*             dev_name,
343                 const char*             file_name)
344 {
345      file->fs = fs_of_netboot;
346      return fs_of_netboot->open(file, dev_name, NULL, file_name);
347 }
348
349 static int
350 default_read(   struct boot_file_t*     file,
351                 unsigned int            size,
352                 void*                   buffer)
353 {
354      prom_printf("WARNING ! default_read called !\n");
355      return FILE_ERR_EOF;
356 }
357
358 static int
359 default_seek(   struct boot_file_t*     file,
360                 unsigned int            newpos)
361 {
362      prom_printf("WARNING ! default_seek called !\n");
363      return FILE_ERR_EOF;
364 }
365
366 static int
367 default_close(  struct boot_file_t*     file)
368 {
369      prom_printf("WARNING ! default_close called !\n");
370      return FILE_ERR_OK;
371 }
372
373 static struct fs_t fs_default =
374 {
375      "defaults",
376      NULL,
377      default_read,
378      default_seek,
379      default_close
380 };
381
382
383 int open_file(const struct boot_fspec_t* spec, struct boot_file_t* file)
384 {
385      int result;
386
387      memset(file, 0, sizeof(struct boot_file_t*));
388      file->fs        = &fs_default;
389
390      DEBUG_F("dev_path = %s\nfile_name = %s\npartition = %d\n",
391              spec->dev, spec->file, spec->part);
392
393      result = prom_get_devtype(spec->dev);
394      if (result > 0)
395           file->device_kind = result;
396      else
397           return result;
398
399      switch(file->device_kind) {
400      case FILE_DEVICE_BLOCK:
401           DEBUG_F("device is a block device\n");
402           return file_block_open(file, spec->dev, spec->file, spec->part);
403      case FILE_DEVICE_NET:
404           DEBUG_F("device is a network device\n");
405           return file_net_open(file, spec->dev, spec->file);
406      }
407      return 0;
408 }
409
410 /*
411  * Local variables:
412  * c-file-style: "k&r"
413  * c-basic-offset: 5
414  * End:
415  */