Commit yaboot 1.3.4-pre1
[yaboot.git] / second / file.c
1 /* File related stuff
2    
3    Copyright (C) 1999 Benjamin Herrenschmidt
4    
5    This program is free software; you can redistribute it and/or modify
6    it under the terms of the GNU General Public License as published by
7    the Free Software Foundation; either version 2 of the License, or
8    (at your option) any later version.
9    
10    This program is distributed in the hope that it will be useful,
11    but WITHOUT ANY WARRANTY; without even the implied warranty of
12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13    GNU General Public License for more details.
14
15    You should have received a copy of the GNU General Public License
16    along with this program; if not, write to the Free Software
17    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.  */
18
19 #include "ctype.h"
20 #include "types.h"
21 #include "stddef.h"
22 #include "stdlib.h"
23 #include "file.h"
24 #include "prom.h"
25 #include "string.h"
26 #include "partition.h"
27 #include "fs.h"
28 #include "errors.h"
29
30 extern char bootdevice[1024];
31
32 /* This function follows the device path in the devtree and separates
33    the device name, partition number, and other datas (mostly file name)
34    the string passed in parameters is changed since 0 are put in place
35    of some separators to terminate the various strings
36  */
37
38 int
39 parse_device_path(char *imagepath, char *defdevice, int defpart,
40                   char *deffile, struct boot_fspec_t *result)
41 {
42      char *ptr;
43      char *ipath = strdup(imagepath);
44      char *defdev = strdup(defdevice);
45
46      result->dev = NULL;
47      result->part = -1;
48      result->file = NULL;
49
50      if (!strstr(defdev, "ethernet") && !strstr(defdev, "enet")) {
51           if ((ptr = strchr(defdev, ':')) != NULL)
52                *ptr = 0; /* remove trailing : from defdevice if necessary */
53      }
54
55      if (!imagepath)
56           goto punt;
57
58      if ((ptr = strrchr(ipath, ',')) != NULL) {
59           result->file = strdup(ptr+1);
60           /* Trim the filename off */
61           *ptr = 0;
62      }
63
64      if (strstr(ipath, "ethernet") || strstr(ipath, "enet"))
65           result->dev = strdup(ipath);
66      else if ((ptr = strchr(ipath, ':')) != NULL) {
67           *ptr = 0;
68           result->dev = strdup(ipath);
69           if (*(ptr+1))
70                result->part = simple_strtol(ptr+1, NULL, 10);
71      } else if (strlen(ipath)) {
72           result->file = strdup(ipath);
73      } else {
74           return 0;
75      }
76      
77  punt:
78      if (!result->dev)
79           result->dev = strdup(defdev);
80      
81      if (result->part < 0)
82           result->part = defpart;
83      
84      if (!result->file)
85           result->file = strdup(deffile);
86      free(ipath);
87      return 1;
88 }
89
90 #if 0
91 char *
92 parse_device_path(char *of_device, char **file_spec, int *partition)
93 {
94         char *p, *last;
95
96         if (file_spec)
97                 *file_spec = NULL;
98         if (partition)
99                 *partition = -1;
100
101         DEBUG_F("of_device before parsing: %s\n", of_device);
102         p = strchr(of_device, ':');
103         DEBUG_F("of_device after parsing: %s\n", p);
104
105         if (!p) {                          /* if null terminated we are finished */
106              DEBUG_F("of_device: %s\n", of_device);
107              return of_device;
108         }
109 #if 0 /* this is broken crap, breaks netboot entirely */
110         else if (strstr(of_device, "ethernet") != NULL)
111              p = strchr(of_device, ',');  /* skip over ip all the way to the ',' */
112         else if (strstr(of_device, "enet") != NULL)
113              p = strchr(of_device, ',');  /* skip over ip all the way to the ',' */
114 #endif
115         *p = 0;
116         last = ++p;                       /* sets to start of second part */
117         while(*p && *p != ',') {
118         if (!isdigit (*p)) {
119              p = last;
120              break;
121         }
122         ++p;
123         }
124         if (p != last) {
125              *(p++) = 0;
126              if (partition)
127                   *partition = simple_strtol(last, NULL, 10);
128         }
129         if (*p && file_spec)
130              *file_spec = p;
131
132         DEBUG_F("of_device: %s\n", of_device);
133         strcat(of_device, ":");
134         DEBUG_F("of_device after strcat: %s\n", of_device);
135         return of_device;
136 }
137
138 int
139 validate_fspec(         struct boot_fspec_t*    spec,
140                         char*                   default_device,
141                         int                     default_part)
142 {
143     if (!spec->file) {
144         spec->file = spec->dev;
145         spec->dev = NULL;
146     }
147     if (spec->part == -1)
148         spec->part = default_part;
149     if (!spec->dev)
150         spec->dev = default_device;
151     if (!spec->file)
152         return FILE_BAD_PATH;
153     else if (spec->file[0] == ',')
154         spec->file++;
155
156     return FILE_ERR_OK;
157 }
158
159 #endif
160
161 static int
162 file_block_open(        struct boot_file_t*     file,
163                         const char*             dev_name,
164                         const char*             file_name,
165                         int                     partition)
166 {
167         struct partition_t*     parts;
168         struct partition_t*     p;
169         struct partition_t*     found;
170         
171         parts = partitions_lookup(dev_name);
172         found = NULL;
173                         
174 #if DEBUG
175         if (parts)
176                 prom_printf("partitions:\n");
177         else
178                 prom_printf("no partitions found.\n");
179 #endif
180         for (p = parts; p && !found; p=p->next) {
181                 DEBUG_F("number: %02d, start: 0x%08lx, length: 0x%08lx\n",
182                         p->part_number, p->part_start, p->part_size );
183                 if (partition == -1) {
184                         file->fs = fs_open( file, dev_name, p, file_name );
185                         if (file->fs != NULL)
186                                 goto bail;
187                 }
188                 if ((partition >= 0) && (partition == p->part_number))
189                         found = p;
190 #if DEBUG
191                 if (found)
192                         prom_printf(" (match)\n");
193 #endif                                          
194         }
195
196         /* Note: we don't skip when found is NULL since we can, in some
197          * cases, let OF figure out a default partition.
198          */
199         DEBUG_F( "Using OF defaults.. (found = %p)\n", found );
200         file->fs = fs_open( file, dev_name, found, file_name );
201
202 bail:
203         if (parts)
204                 partitions_free(parts);
205
206         return fserrorno;
207 }
208
209 static int
210 file_net_open(  struct boot_file_t*     file,
211                 const char*             dev_name,
212                 const char*             file_name)
213 {
214     file->fs = fs_of_netboot;
215     return fs_of_netboot->open(file, dev_name, NULL, file_name);
216 }
217
218 static int
219 default_read(   struct boot_file_t*     file,
220                 unsigned int            size,
221                 void*                   buffer)
222 {
223         prom_printf("WARNING ! default_read called !\n");
224         return FILE_ERR_EOF;
225 }
226
227 static int
228 default_seek(   struct boot_file_t*     file,
229                 unsigned int            newpos)
230 {
231         prom_printf("WARNING ! default_seek called !\n");
232         return FILE_ERR_EOF;
233 }
234
235 static int
236 default_close(  struct boot_file_t*     file)
237 {
238         prom_printf("WARNING ! default_close called !\n");
239         return FILE_ERR_OK;
240 }
241
242 static struct fs_t fs_default =
243 {
244     "defaults",
245     NULL,
246     default_read,
247     default_seek,
248     default_close
249 };
250
251
252 int open_file(  const struct boot_fspec_t*      spec,
253                 struct boot_file_t*             file)
254 {
255 //      static char     temp[1024];
256         static char     temps[64];
257 //      char            *dev_name;
258 //      char            *file_name = NULL;
259         phandle         dev;
260         int             result;
261         int             partition;
262         
263         memset(file, 0, sizeof(struct boot_file_t*));
264         file->fs        = &fs_default;
265
266         /* Lookup the OF device path */
267         /* First, see if a device was specified for the kernel
268          * if not, we hope that the user wants a kernel on the same
269          * drive and partition as yaboot itself */
270 #if 0 /* this is crap */
271         if (!spec->dev)
272                 strcpy(spec->dev, bootdevice);
273         strncpy(temp,spec->dev,1024);
274         dev_name = parse_device_path(temp, &file_name, &partition);
275         if (file_name == NULL)
276                 file_name = (char *)spec->file;
277         if (file_name == NULL) {
278              prom_printf("Configuration error: null filename\n");
279              return FILE_ERR_NOTFOUND;
280         }
281         if (partition == -1)
282 #endif
283                 partition = spec->part;
284
285
286         DEBUG_F("dev_path = %s\nfile_name = %s\npartition = %d\n",
287                 spec->dev, spec->file, partition);
288
289         /* Find OF device phandle */
290         dev = prom_finddevice(spec->dev);
291         if (dev == PROM_INVALID_HANDLE) {
292                 return FILE_ERR_BADDEV;
293         }
294
295         DEBUG_F("dev_phandle = %p\n", dev);
296
297         /* Check the kind of device */
298         result = prom_getprop(dev, "device_type", temps, 63);
299         if (result == -1) {
300                 prom_printf("can't get <device_type> for device\n");
301                 return FILE_ERR_BADDEV;
302         }
303         temps[result] = 0;
304         if (!strcmp(temps, "block"))
305                 file->device_kind = FILE_DEVICE_BLOCK;
306         else if (!strcmp(temps, "network"))
307                 file->device_kind = FILE_DEVICE_NET;
308         else {
309                 prom_printf("Unkown device type <%s>\n", temps);
310                 return FILE_ERR_BADDEV;
311         }
312         
313         switch(file->device_kind) {
314             case FILE_DEVICE_BLOCK:
315                 DEBUG_F("device is a block device\n");
316                 return file_block_open(file, spec->dev, spec->file, partition);
317             case FILE_DEVICE_NET:
318                 DEBUG_F("device is a network device\n");
319                 return file_net_open(file, spec->dev, spec->file);
320         }
321         return 0;
322 }
323
324 /* 
325  * Local variables:
326  * c-file-style: "K&R"
327  * c-basic-offset: 5
328  * End:
329  */