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