e4c379f4900b94c8c7a15c7807fa49afc68f343a
[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 = strrchr(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           if ((ptr = strstr(ipath, "bootp")) != NULL) { /* `n' key booting boots enet:bootp */
66                *ptr = 0;
67                result->dev = strdup(ipath);
68           } else
69           result->dev = strdup(ipath);
70      else if ((ptr = strchr(ipath, ':')) != NULL) {
71           *ptr = 0;
72           result->dev = strdup(ipath);
73           if (*(ptr+1))
74                result->part = simple_strtol(ptr+1, NULL, 10);
75      } else if (strlen(ipath)) {
76           result->file = strdup(ipath);
77      } else {
78           return 0;
79      }
80      
81  punt:
82      if (!result->dev)
83           result->dev = strdup(defdev);
84      
85      if (result->part < 0)
86           result->part = defpart;
87      
88      if (!result->file)
89           result->file = strdup(deffile);
90      free(ipath);
91      return 1;
92 }
93
94 #if 0
95 char *
96 parse_device_path(char *of_device, char **file_spec, int *partition)
97 {
98         char *p, *last;
99
100         if (file_spec)
101                 *file_spec = NULL;
102         if (partition)
103                 *partition = -1;
104
105         DEBUG_F("of_device before parsing: %s\n", of_device);
106         p = strchr(of_device, ':');
107         DEBUG_F("of_device after parsing: %s\n", p);
108
109         if (!p) {                          /* if null terminated we are finished */
110              DEBUG_F("of_device: %s\n", of_device);
111              return of_device;
112         }
113 #if 0 /* this is broken crap, breaks netboot entirely */
114         else if (strstr(of_device, "ethernet") != NULL)
115              p = strchr(of_device, ',');  /* skip over ip all the way to the ',' */
116         else if (strstr(of_device, "enet") != NULL)
117              p = strchr(of_device, ',');  /* skip over ip all the way to the ',' */
118 #endif
119         *p = 0;
120         last = ++p;                       /* sets to start of second part */
121         while(*p && *p != ',') {
122         if (!isdigit (*p)) {
123              p = last;
124              break;
125         }
126         ++p;
127         }
128         if (p != last) {
129              *(p++) = 0;
130              if (partition)
131                   *partition = simple_strtol(last, NULL, 10);
132         }
133         if (*p && file_spec)
134              *file_spec = p;
135
136         DEBUG_F("of_device: %s\n", of_device);
137         strcat(of_device, ":");
138         DEBUG_F("of_device after strcat: %s\n", of_device);
139         return of_device;
140 }
141
142 int
143 validate_fspec(         struct boot_fspec_t*    spec,
144                         char*                   default_device,
145                         int                     default_part)
146 {
147     if (!spec->file) {
148         spec->file = spec->dev;
149         spec->dev = NULL;
150     }
151     if (spec->part == -1)
152         spec->part = default_part;
153     if (!spec->dev)
154         spec->dev = default_device;
155     if (!spec->file)
156         return FILE_BAD_PATH;
157     else if (spec->file[0] == ',')
158         spec->file++;
159
160     return FILE_ERR_OK;
161 }
162
163 #endif
164
165 static int
166 file_block_open(        struct boot_file_t*     file,
167                         const char*             dev_name,
168                         const char*             file_name,
169                         int                     partition)
170 {
171         struct partition_t*     parts;
172         struct partition_t*     p;
173         struct partition_t*     found;
174         
175         parts = partitions_lookup(dev_name);
176         found = NULL;
177                         
178 #if DEBUG
179         if (parts)
180                 prom_printf("partitions:\n");
181         else
182                 prom_printf("no partitions found.\n");
183 #endif
184         for (p = parts; p && !found; p=p->next) {
185                 DEBUG_F("number: %02d, start: 0x%08lx, length: 0x%08lx\n",
186                         p->part_number, p->part_start, p->part_size );
187                 if (partition == -1) {
188                         file->fs = fs_open( file, dev_name, p, file_name );
189                         if (file->fs != NULL)
190                                 goto bail;
191                 }
192                 if ((partition >= 0) && (partition == p->part_number))
193                         found = p;
194 #if DEBUG
195                 if (found)
196                         prom_printf(" (match)\n");
197 #endif                                          
198         }
199
200         /* Note: we don't skip when found is NULL since we can, in some
201          * cases, let OF figure out a default partition.
202          */
203         DEBUG_F( "Using OF defaults.. (found = %p)\n", found );
204         file->fs = fs_open( file, dev_name, found, file_name );
205
206 bail:
207         if (parts)
208                 partitions_free(parts);
209
210         return fserrorno;
211 }
212
213 static int
214 file_net_open(  struct boot_file_t*     file,
215                 const char*             dev_name,
216                 const char*             file_name)
217 {
218     file->fs = fs_of_netboot;
219     return fs_of_netboot->open(file, dev_name, NULL, file_name);
220 }
221
222 static int
223 default_read(   struct boot_file_t*     file,
224                 unsigned int            size,
225                 void*                   buffer)
226 {
227         prom_printf("WARNING ! default_read called !\n");
228         return FILE_ERR_EOF;
229 }
230
231 static int
232 default_seek(   struct boot_file_t*     file,
233                 unsigned int            newpos)
234 {
235         prom_printf("WARNING ! default_seek called !\n");
236         return FILE_ERR_EOF;
237 }
238
239 static int
240 default_close(  struct boot_file_t*     file)
241 {
242         prom_printf("WARNING ! default_close called !\n");
243         return FILE_ERR_OK;
244 }
245
246 static struct fs_t fs_default =
247 {
248     "defaults",
249     NULL,
250     default_read,
251     default_seek,
252     default_close
253 };
254
255
256 int open_file(  const struct boot_fspec_t*      spec,
257                 struct boot_file_t*             file)
258 {
259 //      static char     temp[1024];
260         static char     temps[64];
261 //      char            *dev_name;
262 //      char            *file_name = NULL;
263         phandle         dev;
264         int             result;
265         int             partition;
266         
267         memset(file, 0, sizeof(struct boot_file_t*));
268         file->fs        = &fs_default;
269
270         /* Lookup the OF device path */
271         /* First, see if a device was specified for the kernel
272          * if not, we hope that the user wants a kernel on the same
273          * drive and partition as yaboot itself */
274 #if 0 /* this is crap */
275         if (!spec->dev)
276                 strcpy(spec->dev, bootdevice);
277         strncpy(temp,spec->dev,1024);
278         dev_name = parse_device_path(temp, &file_name, &partition);
279         if (file_name == NULL)
280                 file_name = (char *)spec->file;
281         if (file_name == NULL) {
282              prom_printf("Configuration error: null filename\n");
283              return FILE_ERR_NOTFOUND;
284         }
285         if (partition == -1)
286 #endif
287                 partition = spec->part;
288
289
290         DEBUG_F("dev_path = %s\nfile_name = %s\npartition = %d\n",
291                 spec->dev, spec->file, partition);
292
293         /* Find OF device phandle */
294         dev = prom_finddevice(spec->dev);
295         if (dev == PROM_INVALID_HANDLE) {
296                 return FILE_ERR_BADDEV;
297         }
298
299         DEBUG_F("dev_phandle = %p\n", dev);
300
301         /* Check the kind of device */
302         result = prom_getprop(dev, "device_type", temps, 63);
303         if (result == -1) {
304                 prom_printf("can't get <device_type> for device\n");
305                 return FILE_ERR_BADDEV;
306         }
307         temps[result] = 0;
308         if (!strcmp(temps, "block"))
309                 file->device_kind = FILE_DEVICE_BLOCK;
310         else if (!strcmp(temps, "network"))
311                 file->device_kind = FILE_DEVICE_NET;
312         else {
313                 prom_printf("Unkown device type <%s>\n", temps);
314                 return FILE_ERR_BADDEV;
315         }
316         
317         switch(file->device_kind) {
318             case FILE_DEVICE_BLOCK:
319                 DEBUG_F("device is a block device\n");
320                 return file_block_open(file, spec->dev, spec->file, partition);
321             case FILE_DEVICE_NET:
322                 DEBUG_F("device is a network device\n");
323                 return file_net_open(file, spec->dev, spec->file);
324         }
325         return 0;
326 }
327
328 /* 
329  * Local variables:
330  * c-file-style: "K&R"
331  * c-basic-offset: 5
332  * End:
333  */