8f86a6eaefb0b11e685ae5831bfd799617a59ba3
[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[1024];
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;
181
182      result->dev = NULL;
183      result->part = -1;
184      result->file = NULL;
185
186      if (!imagepath)
187           return 0;
188      else if (!(ipath = strdup(imagepath))) 
189           return 0;
190
191      if (defdevice) {
192           defdev = strdup(defdevice);
193           device_kind = prom_get_devtype(defdev);
194      } else
195           device_kind = prom_get_devtype(ipath);
196
197      if (device_kind != FILE_DEVICE_NET && strchr(defdev, ':') != NULL) {
198            if ((ptr = strrchr(defdev, ':')) != NULL)
199                 *ptr = 0; /* remove trailing : from defdevice if necessary */
200      }
201
202      /* This will not properly handle an obp-tftp argument list
203       * with elements after the filename; that is handled below.
204       */
205      if (device_kind != FILE_DEVICE_NET && strchr(ipath, ':') != NULL) {
206           if ((ptr = strrchr(ipath, ',')) != NULL) {
207                char *colon = strrchr(ipath, ':');
208                /* If a ':' occurs *after* a ',', then we assume that there is
209                   no filename */
210                if (!colon || colon < ptr) {
211                     result->file = strdup(ptr+1);
212                     /* Trim the filename off */
213                     *ptr = 0;
214                }
215           }
216      }
217
218      if (device_kind == FILE_DEVICE_NET) {
219           if (strchr(ipath, ':'))
220                result->file = netdev_path_to_filename(ipath);
221           else
222                result->file = strdup(ipath);
223
224           if (!defdev)
225                result->dev = netdev_path_to_dev(ipath);
226      } else if ((ptr = strchr(ipath, ':')) != NULL) {
227           *ptr = 0;
228           result->dev = strdup(ipath);
229           if (*(ptr+1))
230                result->part = simple_strtol(ptr+1, NULL, 10);
231      } else if (!defdev) {
232           result->dev = strdup(ipath); 
233      } else if (strlen(ipath)) {
234           result->file = strdup(ipath);
235      } else {
236           free(defdev);
237           return 0;
238      }
239
240      if (!result->dev && defdev)
241           result->dev = strdup(defdev);
242      
243      if (result->part < 0)
244           result->part = defpart;
245      
246      if (!result->file)
247           result->file = strdup(deffile);
248
249      free(ipath);
250      if (defdev)
251           free(defdev);
252      return 1;
253 }
254
255
256 static int
257 file_block_open(        struct boot_file_t*     file,
258                         const char*             dev_name,
259                         const char*             file_name,
260                         int                     partition)
261 {
262      struct partition_t*        parts;
263      struct partition_t*        p;
264      struct partition_t*        found;
265         
266      parts = partitions_lookup(dev_name);
267      found = NULL;
268                         
269 #if DEBUG
270      if (parts)
271           prom_printf("partitions:\n");
272      else
273           prom_printf("no partitions found.\n");
274 #endif
275      for (p = parts; p && !found; p=p->next) {
276           DEBUG_F("number: %02d, start: 0x%08lx, length: 0x%08lx\n",
277                   p->part_number, p->part_start, p->part_size );
278           if (partition == -1) {
279                file->fs = fs_open( file, dev_name, p, file_name );
280                if (file->fs == NULL || fserrorno != FILE_ERR_OK)
281                     continue;
282                else {
283                     partition = p->part_number;
284                     goto done;
285                }
286           }
287           if ((partition >= 0) && (partition == p->part_number))
288                found = p;
289 #if DEBUG
290           if (found)
291                prom_printf(" (match)\n");
292 #endif                                          
293      }
294
295      /* Note: we don't skip when found is NULL since we can, in some
296       * cases, let OF figure out a default partition.
297       */
298      DEBUG_F( "Using OF defaults.. (found = %p)\n", found );
299      file->fs = fs_open( file, dev_name, found, file_name );
300
301 done:
302      if (parts)
303           partitions_free(parts);
304
305      return fserrorno;
306 }
307
308 static int
309 file_net_open(  struct boot_file_t*     file,
310                 const char*             dev_name,
311                 const char*             file_name)
312 {
313      file->fs = fs_of_netboot;
314      return fs_of_netboot->open(file, dev_name, NULL, file_name);
315 }
316
317 static int
318 default_read(   struct boot_file_t*     file,
319                 unsigned int            size,
320                 void*                   buffer)
321 {
322      prom_printf("WARNING ! default_read called !\n");
323      return FILE_ERR_EOF;
324 }
325
326 static int
327 default_seek(   struct boot_file_t*     file,
328                 unsigned int            newpos)
329 {
330      prom_printf("WARNING ! default_seek called !\n");
331      return FILE_ERR_EOF;
332 }
333
334 static int
335 default_close(  struct boot_file_t*     file)
336 {
337      prom_printf("WARNING ! default_close called !\n");
338      return FILE_ERR_OK;
339 }
340
341 static struct fs_t fs_default =
342 {
343      "defaults",
344      NULL,
345      default_read,
346      default_seek,
347      default_close
348 };
349
350
351 int open_file(const struct boot_fspec_t* spec, struct boot_file_t* file)
352 {
353      int result;
354         
355      memset(file, 0, sizeof(struct boot_file_t*));
356      file->fs        = &fs_default;
357
358      DEBUG_F("dev_path = %s\nfile_name = %s\npartition = %d\n",
359              spec->dev, spec->file, spec->part);
360
361      result = prom_get_devtype(spec->dev);
362      if (result > 0)
363           file->device_kind = result;
364      else
365           return result;
366         
367      switch(file->device_kind) {
368      case FILE_DEVICE_BLOCK:
369           DEBUG_F("device is a block device\n");
370           return file_block_open(file, spec->dev, spec->file, spec->part);
371      case FILE_DEVICE_NET:
372           DEBUG_F("device is a network device\n");
373           return file_net_open(file, spec->dev, spec->file);
374      }
375      return 0;
376 }
377
378 /* 
379  * Local variables:
380  * c-file-style: "k&r"
381  * c-basic-offset: 5
382  * End:
383  */