b5763a235f6d5db62ad5fb1b4c92254b89775270
[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
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 == NULL || fserrorno != FILE_ERR_OK)
170                     continue;
171                else {
172                     partition = p->part_number;
173                     goto done;
174                }
175           }
176           if ((partition >= 0) && (partition == p->part_number))
177                found = p;
178 #if DEBUG
179           if (found)
180                prom_printf(" (match)\n");
181 #endif                                          
182      }
183
184      /* Note: we don't skip when found is NULL since we can, in some
185       * cases, let OF figure out a default partition.
186       */
187      DEBUG_F( "Using OF defaults.. (found = %p)\n", found );
188      file->fs = fs_open( file, dev_name, found, file_name );
189
190 done:
191      if (parts)
192           partitions_free(parts);
193
194      return fserrorno;
195 }
196
197 static int
198 file_net_open(  struct boot_file_t*     file,
199                 const char*             dev_name,
200                 const char*             file_name)
201 {
202      file->fs = fs_of_netboot;
203      return fs_of_netboot->open(file, dev_name, NULL, file_name);
204 }
205
206 static int
207 default_read(   struct boot_file_t*     file,
208                 unsigned int            size,
209                 void*                   buffer)
210 {
211      prom_printf("WARNING ! default_read called !\n");
212      return FILE_ERR_EOF;
213 }
214
215 static int
216 default_seek(   struct boot_file_t*     file,
217                 unsigned int            newpos)
218 {
219      prom_printf("WARNING ! default_seek called !\n");
220      return FILE_ERR_EOF;
221 }
222
223 static int
224 default_close(  struct boot_file_t*     file)
225 {
226      prom_printf("WARNING ! default_close called !\n");
227      return FILE_ERR_OK;
228 }
229
230 static struct fs_t fs_default =
231 {
232      "defaults",
233      NULL,
234      default_read,
235      default_seek,
236      default_close
237 };
238
239
240 int open_file(const struct boot_fspec_t* spec, struct boot_file_t* file)
241 {
242      int result;
243         
244      memset(file, 0, sizeof(struct boot_file_t*));
245      file->fs        = &fs_default;
246
247      DEBUG_F("dev_path = %s\nfile_name = %s\npartition = %d\n",
248              spec->dev, spec->file, spec->part);
249
250      result = prom_get_devtype(spec->dev);
251      if (result > 0)
252           file->device_kind = result;
253      else
254           return result;
255         
256      switch(file->device_kind) {
257      case FILE_DEVICE_BLOCK:
258           DEBUG_F("device is a block device\n");
259           return file_block_open(file, spec->dev, spec->file, spec->part);
260      case FILE_DEVICE_NET:
261           DEBUG_F("device is a network device\n");
262           return file_net_open(file, spec->dev, spec->file);
263      }
264      return 0;
265 }
266
267 /* 
268  * Local variables:
269  * c-file-style: "k&r"
270  * c-basic-offset: 5
271  * End:
272  */