4a44ba36c9458c37776e635060ffa440ae993e31
[yaboot.git] / second / file.c
1 /*
2  *  file.c - Filesystem related interfaces
3  *
4  *  Copyright (C) 2001 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 /* This function follows the device path in the devtree and separates
42    the device name, partition number, and other datas (mostly file name)
43    the string passed in parameters is changed since 0 are put in place
44    of some separators to terminate the various strings.  
45
46    when a default device is supplied imagepath will be assumed to be a
47    plain filename unless it contains a : otherwise if defaultdev is
48    NULL imagepath will be assumed to be a device path.
49
50    returns 1 on success 0 on failure.
51
52    Supported examples:
53     - /pci@80000000/pci-bridge@d/ADPT,2930CU@2/@1:4
54     - /pci@80000000/pci-bridge@d/ADPT,2930CU@2/@1:4,/boot/vmlinux
55     - hd:3,/boot/vmlinux
56     - enet:10.0.0.1,/tftpboot/vmlinux
57     - enet:,/tftpboot/vmlinux
58     - enet:bootp
59     - enet:0
60    Supported only if defdevice == NULL
61     - disc
62     - any other device path lacking a :
63    Unsupported examples:
64     - hd:2,\\:tbxi <- no filename will be detected due to the extra :
65     - enet:192.168.2.1,bootme,c-iaddr,g-iaddr,subnet-mask,bootp-retries,tftp-retries */
66
67 int
68 parse_device_path(char *imagepath, char *defdevice, int defpart,
69                   char *deffile, struct boot_fspec_t *result)
70 {
71      char *ptr;
72      char *ipath = NULL;
73      char *defdev = NULL;
74
75      result->dev = NULL;
76      result->part = -1;
77      result->file = NULL;
78
79      if (!imagepath)
80           return 0;
81      else
82           ipath = strdup(imagepath);
83
84      if (defdevice)
85           defdev = strdup(defdevice);
86
87      if (defdev) {
88           if (!strstr(defdev, "ethernet") && !strstr(defdev, "enet")) {
89                if ((ptr = strrchr(defdev, ':')) != NULL)
90                     *ptr = 0; /* remove trailing : from defdevice if necessary */
91           }
92      }
93
94      /* if there is no : then there is no filename or partition.  must
95         use strrchr() since enet:,10.0.0.1,file is legal */
96
97      if (strchr(ipath, ':') != NULL) {
98           if ((ptr = strrchr(ipath, ',')) != NULL) {
99                char *colon = strrchr(ipath, ':');
100                /* If a ':' occurs *after* a ',', then we assume that there is
101                   no filename */
102                if (!colon || colon < ptr) {
103                     result->file = strdup(ptr+1);
104                     /* Trim the filename off */
105                     *ptr = 0;
106                }
107           }
108      }
109
110      if (strstr(ipath, "ethernet") || strstr(ipath, "enet"))
111           if ((ptr = strstr(ipath, "bootp")) != NULL) { /* `n' key booting boots enet:bootp */
112                *ptr = 0;
113                result->dev = strdup(ipath);
114           } else
115                result->dev = strdup(ipath);
116      else if ((ptr = strchr(ipath, ':')) != NULL) {
117           *ptr = 0;
118           result->dev = strdup(ipath);
119           if (*(ptr+1))
120                result->part = simple_strtol(ptr+1, NULL, 10);
121      } else if (!defdev) {
122           result->dev = strdup(ipath); 
123      } else if (strlen(ipath)) {
124           result->file = strdup(ipath);
125      } else {
126           return 0;
127      }
128
129      if (!result->dev && defdev)
130           result->dev = strdup(defdev);
131      
132      if (result->part < 0)
133           result->part = defpart;
134      
135      if (!result->file)
136           result->file = strdup(deffile);
137
138      free(ipath);
139      if (defdev)
140           free(defdev);
141      return 1;
142 }
143
144
145 static int
146 file_block_open(        struct boot_file_t*     file,
147                         const char*             dev_name,
148                         const char*             file_name,
149                         int                     partition)
150 {
151      struct partition_t*        parts;
152      struct partition_t*        p;
153      struct partition_t*        found;
154         
155      parts = partitions_lookup(dev_name);
156      found = NULL;
157                         
158 #if DEBUG
159      if (parts)
160           prom_printf("partitions:\n");
161      else
162           prom_printf("no partitions found.\n");
163 #endif
164      for (p = parts; p && !found; p=p->next) {
165           DEBUG_F("number: %02d, start: 0x%08lx, length: 0x%08lx\n",
166                   p->part_number, p->part_start, p->part_size );
167           if (partition == -1) {
168                file->fs = fs_open( file, dev_name, p, file_name );
169                if (file->fs != FILE_ERR_OK)
170                     goto bail;
171           }
172           if ((partition >= 0) && (partition == p->part_number))
173                found = p;
174 #if DEBUG
175           if (found)
176                prom_printf(" (match)\n");
177 #endif                                          
178      }
179
180      /* Note: we don't skip when found is NULL since we can, in some
181       * cases, let OF figure out a default partition.
182       */
183      DEBUG_F( "Using OF defaults.. (found = %p)\n", found );
184      file->fs = fs_open( file, dev_name, found, file_name );
185
186 bail:
187      if (parts)
188           partitions_free(parts);
189
190      return fserrorno;
191 }
192
193 static int
194 file_net_open(  struct boot_file_t*     file,
195                 const char*             dev_name,
196                 const char*             file_name)
197 {
198      file->fs = fs_of_netboot;
199      return fs_of_netboot->open(file, dev_name, NULL, file_name);
200 }
201
202 static int
203 default_read(   struct boot_file_t*     file,
204                 unsigned int            size,
205                 void*                   buffer)
206 {
207      prom_printf("WARNING ! default_read called !\n");
208      return FILE_ERR_EOF;
209 }
210
211 static int
212 default_seek(   struct boot_file_t*     file,
213                 unsigned int            newpos)
214 {
215      prom_printf("WARNING ! default_seek called !\n");
216      return FILE_ERR_EOF;
217 }
218
219 static int
220 default_close(  struct boot_file_t*     file)
221 {
222      prom_printf("WARNING ! default_close called !\n");
223      return FILE_ERR_OK;
224 }
225
226 static struct fs_t fs_default =
227 {
228      "defaults",
229      NULL,
230      default_read,
231      default_seek,
232      default_close
233 };
234
235
236 int open_file(const struct boot_fspec_t* spec, struct boot_file_t* file)
237 {
238      int result;
239         
240      memset(file, 0, sizeof(struct boot_file_t*));
241      file->fs        = &fs_default;
242
243      DEBUG_F("dev_path = %s\nfile_name = %s\npartition = %d\n",
244              spec->dev, spec->file, spec->part);
245
246      result = prom_get_devtype(spec->dev);
247      if (result > 0)
248           file->device_kind = result;
249      else
250           return result;
251         
252      switch(file->device_kind) {
253      case FILE_DEVICE_BLOCK:
254           DEBUG_F("device is a block device\n");
255           return file_block_open(file, spec->dev, spec->file, spec->part);
256      case FILE_DEVICE_NET:
257           DEBUG_F("device is a network device\n");
258           return file_net_open(file, spec->dev, spec->file);
259      }
260      return 0;
261 }
262
263 /* 
264  * Local variables:
265  * c-file-style: "k&r"
266  * c-basic-offset: 5
267  * End:
268  */