Fix netboot fucntionality to use the parameters specified.
[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 /*
42  * Copy the string from source to dest till newline or comma(,) is seen
43  * in the source.
44  * Move source and dest pointers respectively.
45  * Returns pointer to the start of the string that has just been copied.
46  */
47 static char *
48 scopy(char **dest, char **source)
49 {
50      char *ret = *dest;
51
52      if (!**source)
53           return NULL;
54
55      while (**source != ',' && **source != '\0')
56           *(*dest)++ = *(*source)++;
57      if (**source != '\0')
58           *(*source)++;
59      **dest = '\0';
60      *(*dest)++;
61      return ret;
62 }
63
64 /*
65  * Extract all the arguments provided in the imagepath and fill it in result.
66  * Returns 1 on success, 0 on failure.
67  */
68 static int
69 extract_args_from_netdev_path(char *imagepath, struct boot_fspec_t *result)
70 {
71      char *tmp, *args, *str, *start;
72
73      DEBUG_F("imagepath = %s\n", imagepath);
74
75      if (!imagepath)
76           return 1;
77
78      args = strrchr(imagepath, ':');
79      if (!args)
80           return 1;
81
82      start = args; /* used to see if we read any optional parameters */
83
84      /* The obp-tftp device arguments should be at the end of
85       * the argument list.  Skip over any extra arguments (promiscuous,
86       * speed, duplex, bootp, rarp).
87       */
88
89      tmp = strstr(args, "promiscuous");
90      if (tmp && tmp > args)
91           args = tmp + strlen("promiscuous");
92
93      tmp = strstr(args, "speed=");
94      if (tmp && tmp > args)
95           args = tmp + strlen("speed=");
96
97      tmp = strstr(args, "duplex=");
98      if (tmp && tmp > args)
99           args = tmp + strlen("duplex=");
100
101      tmp = strstr(args, "bootp");
102      if (tmp && tmp > args)
103           args = tmp + strlen("bootp");
104
105      tmp = strstr(args, "rarp");
106      if (tmp && tmp > args)
107           args = tmp + strlen("rarp");
108
109      if (args != start) /* we read some parameters, so go past the next comma(,) */
110           args = strchr(args, ',');
111      if (!args)
112           return 1;
113
114      str = malloc(strlen(args) + 1); /*long enough to hold all strings */
115      if (!str)
116           return 0;
117
118      if (args[-1] != ':')
119           args++; /* If comma(,) is not immediately followed by ':' then go past the , */
120
121      /*
122       * read the arguments in order: siaddr,filename,ciaddr,giaddr,
123       * bootp-retries,tftp-retries,addl_prameters
124       */
125      result->siaddr = scopy(&str, &args);
126      result->file = scopy(&str, &args);
127      result->ciaddr = scopy(&str, &args);
128      result->giaddr = scopy(&str, &args);
129      result->bootp_retries = scopy(&str, &args);
130      result->tftp_retries = scopy(&str, &args);
131      if (*args) {
132           result->addl_params = strdup(args);
133           if (!result->addl_params)
134                 return 0;
135      }
136
137      DEBUG_F("siaddr = <%s>\n", result->siaddr);
138      DEBUG_F("file = <%s>\n", result->file);
139      DEBUG_F("ciaddr = <%s>\n", result->ciaddr);
140      DEBUG_F("giaddr = <%s>\n", result->giaddr);
141      DEBUG_F("bootp_retries = <%s>\n", result->bootp_retries);
142      DEBUG_F("tftp_retries = <%s>\n", result->tftp_retries);
143      DEBUG_F("addl_params = <%s>\n", result->addl_params);
144      return 1;
145 }
146
147 static char *netdev_path_to_dev(const char *path)
148 {
149      char *dev, *tmp;
150      size_t len;
151
152      DEBUG_F("path = %s\n", path);
153
154      if (!path)
155           return NULL;
156
157      tmp = strchr(path, ':');
158      if (!tmp)
159           return strdup(path);
160      tmp++;
161
162      len = tmp - path + 1;
163
164      dev = malloc(len);
165      if (dev) {
166           strncpy(dev, path, len);
167           dev[len - 1] = '\0';
168      }
169      return dev;
170 }
171
172 /* This function follows the device path in the devtree and separates
173    the device name, partition number, and other datas (mostly file name)
174    the string passed in parameters is changed since 0 are put in place
175    of some separators to terminate the various strings.
176
177    when a default device is supplied imagepath will be assumed to be a
178    plain filename unless it contains a : otherwise if defaultdev is
179    NULL imagepath will be assumed to be a device path.
180
181    returns 1 on success 0 on failure.
182
183    Supported examples:
184     - /pci@80000000/pci-bridge@d/ADPT,2930CU@2/@1:4
185     - /pci@80000000/pci-bridge@d/ADPT,2930CU@2/@1:4,/boot/vmlinux
186     - hd:3,/boot/vmlinux
187     - enet:10.0.0.1,/tftpboot/vmlinux
188     - enet:,/tftpboot/vmlinux
189     - enet:bootp
190     - enet:0
191     - arguments for obp-tftp open as specified in section 4.1 of
192       http://playground.sun.com/1275/practice/obp-tftp/tftp1_0.pdf
193       [bootp,]siaddr,filename,ciaddr,giaddr,bootp-retries,tftp-retries
194       ex: enet:bootp,10.0.0.11,bootme,10.0.0.12,10.0.0.1,5,5
195    Supported only if defdevice == NULL
196     - disc
197     - any other device path lacking a :
198    Unsupported examples:
199     - hd:2,\\:tbxi <- no filename will be detected due to the extra :
200     - enet:192.168.2.1,bootme,c-iaddr,g-iaddr,subnet-mask,bootp-retries,tftp-retries */
201
202 int
203 parse_device_path(char *imagepath, char *defdevice, int defpart,
204                   char *deffile, struct boot_fspec_t *result)
205 {
206      char *ptr;
207      char *ipath = NULL;
208      char *defdev = NULL;
209      int device_kind = -1;
210
211      DEBUG_F("imagepath = %s; defdevice %s; defpart %d, deffile %s\n",
212                 imagepath, defdevice, defpart, deffile);
213
214      result->dev = NULL;
215      result->part = -1;
216      result->file = NULL;
217
218      if (!imagepath)
219           return 0;
220
221       /*
222        * Do preliminary checking for an iscsi device; it may appear as
223        * pure a network device (device_type == "network") if this is
224        * ISWI.  This is the case on IBM systems doing an iscsi OFW
225        * boot.
226        */
227      if (strstr(imagepath, TOK_ISCSI)) {
228           /*
229            * get the virtual device information from the
230            * "nas-bootdevice" property.
231            */
232           if (prom_get_chosen("nas-bootdevice", bootdevice, BOOTDEVSZ)) {
233                DEBUG_F("reset boot-device to"
234                        " /chosen/nas-bootdevice = %s\n", bootdevice);
235                device_kind = FILE_DEVICE_ISCSI;
236                ipath = strdup(bootdevice);
237                if (!ipath)
238                     return 0;
239           }
240           else
241                return 0;
242      }
243      else if (!(ipath = strdup(imagepath)))
244           return 0;
245
246      if (defdevice) {
247           defdev = strdup(defdevice);
248           device_kind = prom_get_devtype(defdev);
249      } else if (device_kind == -1)
250           device_kind = prom_get_devtype(ipath);
251
252      /*
253       * When an iscsi iqn is present, it may have embedded colons, so
254       * don't parse off anything.
255       */
256      if (device_kind != FILE_DEVICE_NET &&
257          device_kind != FILE_DEVICE_ISCSI &&
258          strchr(defdev, ':') != NULL) {
259            if ((ptr = strrchr(defdev, ':')) != NULL)
260                 *ptr = 0; /* remove trailing : from defdevice if necessary */
261      }
262
263      /* This will not properly handle an obp-tftp argument list
264       * with elements after the filename; that is handled below.
265       */
266      if (device_kind != FILE_DEVICE_NET &&
267          device_kind != FILE_DEVICE_ISCSI &&
268          strchr(ipath, ':') != NULL) {
269           if ((ptr = strrchr(ipath, ',')) != NULL) {
270                char *colon = strrchr(ipath, ':');
271                /* If a ':' occurs *after* a ',', then we assume that there is
272                   no filename */
273                if (!colon || colon < ptr) {
274                     result->file = strdup(ptr+1);
275                     /* Trim the filename off */
276                     *ptr = 0;
277                }
278           }
279      }
280
281      if (device_kind == FILE_DEVICE_NET) {
282           if (strchr(ipath, ':')) {
283                if (extract_args_from_netdev_path(ipath, result) == 0)
284                    return 0;
285           } else
286                result->file = strdup(ipath);
287
288           if (!defdev)
289                result->dev = netdev_path_to_dev(ipath);
290      } else if (device_kind != FILE_DEVICE_ISCSI &&
291                 (ptr = strrchr(ipath, ':')) != NULL) {
292           *ptr = 0;
293           result->dev = strdup(ipath);
294           if (*(ptr+1))
295                result->part = simple_strtol(ptr+1, NULL, 10);
296      } else if (!defdev) {
297           result->dev = strdup(ipath);
298      } else if (strlen(ipath)) {
299           result->file = strdup(ipath);
300      } else {
301           free(defdev);
302           return 0;
303      }
304
305      if (!result->dev && defdev)
306           result->dev = strdup(defdev);
307
308      if (result->part < 0)
309           result->part = defpart;
310
311      if (!result->file)
312           result->file = strdup(deffile);
313
314      free(ipath);
315      if (defdev)
316           free(defdev);
317      return 1;
318 }
319
320
321 static int
322 file_block_open(        struct boot_file_t*     file,
323                         struct boot_fspec_t*    fspec,
324                         int                     partition)
325 {
326      struct partition_t*        parts;
327      struct partition_t*        p;
328      struct partition_t*        found;
329
330      parts = partitions_lookup(fspec->dev);
331      found = NULL;
332
333 #if DEBUG
334      if (parts)
335           prom_printf("partitions:\n");
336      else
337           prom_printf("no partitions found.\n");
338 #endif
339      for (p = parts; p && !found; p=p->next) {
340           DEBUG_F("number: %02d, start: 0x%08lx, length: 0x%08lx\n",
341                   p->part_number, p->part_start, p->part_size );
342           if (partition == -1) {
343                file->fs = fs_open( file, p, fspec );
344                if (file->fs == NULL || fserrorno != FILE_ERR_OK)
345                     continue;
346                else {
347                     partition = p->part_number;
348                     goto done;
349                }
350           }
351           if ((partition >= 0) && (partition == p->part_number))
352                found = p;
353 #if DEBUG
354           if (found)
355                prom_printf(" (match)\n");
356 #endif
357      }
358
359      /* Note: we don't skip when found is NULL since we can, in some
360       * cases, let OF figure out a default partition.
361       */
362      DEBUG_F( "Using OF defaults.. (found = %p)\n", found );
363      file->fs = fs_open( file, found, fspec );
364
365 done:
366      if (parts)
367           partitions_free(parts);
368
369      return fserrorno;
370 }
371
372 static int
373 file_net_open(struct boot_file_t* file, struct boot_fspec_t *fspec)
374 {
375      file->fs = fs_of_netboot;
376      return fs_of_netboot->open(file, NULL, fspec);
377 }
378
379 static int
380 default_read(   struct boot_file_t*     file,
381                 unsigned int            size,
382                 void*                   buffer)
383 {
384      prom_printf("WARNING ! default_read called !\n");
385      return FILE_ERR_EOF;
386 }
387
388 static int
389 default_seek(   struct boot_file_t*     file,
390                 unsigned int            newpos)
391 {
392      prom_printf("WARNING ! default_seek called !\n");
393      return FILE_ERR_EOF;
394 }
395
396 static int
397 default_close(  struct boot_file_t*     file)
398 {
399      prom_printf("WARNING ! default_close called !\n");
400      return FILE_ERR_OK;
401 }
402
403 static struct fs_t fs_default =
404 {
405      "defaults",
406      NULL,
407      default_read,
408      default_seek,
409      default_close
410 };
411
412
413 int open_file(struct boot_fspec_t* spec, struct boot_file_t* file)
414 {
415      int result;
416
417      memset(file, 0, sizeof(struct boot_file_t*));
418      file->fs        = &fs_default;
419
420      DEBUG_F("dev_path = %s\nfile_name = %s\npartition = %d\n",
421              spec->dev, spec->file, spec->part);
422
423      result = prom_get_devtype(spec->dev);
424      if (result > 0)
425           file->device_kind = result;
426      else
427           return result;
428
429      switch(file->device_kind) {
430      case FILE_DEVICE_BLOCK:
431           DEBUG_F("device is a block device\n");
432           return file_block_open(file, spec, spec->part);
433      case FILE_DEVICE_NET:
434           DEBUG_F("device is a network device\n");
435           return file_net_open(file, spec);
436      }
437      return 0;
438 }
439
440 /*
441  * Local variables:
442  * c-file-style: "k&r"
443  * c-basic-offset: 5
444  * End:
445  */