ed70376be542670865c249a52e1086381418acd3
[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 /* 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 if (!(ipath = strdup(imagepath))) 
82           return 0;
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      } else {
93           return 0;
94      }
95
96      /* if there is no : then there is no filename or partition.  must
97         use strrchr() since enet:,10.0.0.1,file is legal */
98
99      if (strchr(ipath, ':') != NULL) {
100           if ((ptr = strrchr(ipath, ',')) != NULL) {
101                char *colon = strrchr(ipath, ':');
102                /* If a ':' occurs *after* a ',', then we assume that there is
103                   no filename */
104                if (!colon || colon < ptr) {
105                     result->file = strdup(ptr+1);
106                     /* Trim the filename off */
107                     *ptr = 0;
108                }
109           }
110      }
111
112      if (strstr(ipath, "ethernet") || strstr(ipath, "enet"))
113           if ((ptr = strstr(ipath, "bootp")) != NULL) { /* `n' key booting boots enet:bootp */
114                *ptr = 0;
115                result->dev = strdup(ipath);
116           } else
117                result->dev = strdup(ipath);
118      else if ((ptr = strchr(ipath, ':')) != NULL) {
119           *ptr = 0;
120           result->dev = strdup(ipath);
121           if (*(ptr+1))
122                result->part = simple_strtol(ptr+1, NULL, 10);
123      } else if (!defdev) {
124           result->dev = strdup(ipath); 
125      } else if (strlen(ipath)) {
126           result->file = strdup(ipath);
127      } else {
128           free(defdev);
129           return 0;
130      }
131
132      if (!result->dev && defdev)
133           result->dev = strdup(defdev);
134      
135      if (result->part < 0)
136           result->part = defpart;
137      
138      if (!result->file)
139           result->file = strdup(deffile);
140
141      free(ipath);
142      if (defdev)
143           free(defdev);
144      return 1;
145 }
146
147
148 static int
149 file_block_open(        struct boot_file_t*     file,
150                         const char*             dev_name,
151                         const char*             file_name,
152                         int                     partition)
153 {
154      struct partition_t*        parts;
155      struct partition_t*        p;
156      struct partition_t*        found;
157         
158      parts = partitions_lookup(dev_name);
159      found = NULL;
160                         
161 #if DEBUG
162      if (parts)
163           prom_printf("partitions:\n");
164      else
165           prom_printf("no partitions found.\n");
166 #endif
167      for (p = parts; p && !found; p=p->next) {
168           DEBUG_F("number: %02d, start: 0x%08lx, length: 0x%08lx\n",
169                   p->part_number, p->part_start, p->part_size );
170           if (partition == -1) {
171                file->fs = fs_open( file, dev_name, p, file_name );
172                if (file->fs == NULL || fserrorno != FILE_ERR_OK)
173                     continue;
174                else {
175                     partition = p->part_number;
176                     goto done;
177                }
178           }
179           if ((partition >= 0) && (partition == p->part_number))
180                found = p;
181 #if DEBUG
182           if (found)
183                prom_printf(" (match)\n");
184 #endif                                          
185      }
186
187      /* Note: we don't skip when found is NULL since we can, in some
188       * cases, let OF figure out a default partition.
189       */
190      DEBUG_F( "Using OF defaults.. (found = %p)\n", found );
191      file->fs = fs_open( file, dev_name, found, file_name );
192
193 done:
194      if (parts)
195           partitions_free(parts);
196
197      return fserrorno;
198 }
199
200 static int
201 file_net_open(  struct boot_file_t*     file,
202                 const char*             dev_name,
203                 const char*             file_name)
204 {
205      file->fs = fs_of_netboot;
206      return fs_of_netboot->open(file, dev_name, NULL, file_name);
207 }
208
209 static int
210 default_read(   struct boot_file_t*     file,
211                 unsigned int            size,
212                 void*                   buffer)
213 {
214      prom_printf("WARNING ! default_read called !\n");
215      return FILE_ERR_EOF;
216 }
217
218 static int
219 default_seek(   struct boot_file_t*     file,
220                 unsigned int            newpos)
221 {
222      prom_printf("WARNING ! default_seek called !\n");
223      return FILE_ERR_EOF;
224 }
225
226 static int
227 default_close(  struct boot_file_t*     file)
228 {
229      prom_printf("WARNING ! default_close called !\n");
230      return FILE_ERR_OK;
231 }
232
233 static struct fs_t fs_default =
234 {
235      "defaults",
236      NULL,
237      default_read,
238      default_seek,
239      default_close
240 };
241
242
243 int open_file(const struct boot_fspec_t* spec, struct boot_file_t* file)
244 {
245      int result;
246         
247      memset(file, 0, sizeof(struct boot_file_t*));
248      file->fs        = &fs_default;
249
250      DEBUG_F("dev_path = %s\nfile_name = %s\npartition = %d\n",
251              spec->dev, spec->file, spec->part);
252
253      result = prom_get_devtype(spec->dev);
254      if (result > 0)
255           file->device_kind = result;
256      else
257           return result;
258         
259      switch(file->device_kind) {
260      case FILE_DEVICE_BLOCK:
261           DEBUG_F("device is a block device\n");
262           return file_block_open(file, spec->dev, spec->file, spec->part);
263      case FILE_DEVICE_NET:
264           DEBUG_F("device is a network device\n");
265           return file_net_open(file, spec->dev, spec->file);
266      }
267      return 0;
268 }
269
270 /* 
271  * Local variables:
272  * c-file-style: "k&r"
273  * c-basic-offset: 5
274  * End:
275  */