1 /*
2  *	/packages/grubfs-files
3  *
4  *	grub vfs
5  *
6  *   Copyright (C) 2004 Stefan Reinauer
7  *   Copyright (C) 2004 Samuel Rydh
8  *   Copyright (C) 2010 Mark Cave-Ayland
9  *
10  *   inspired by HFS code from Samuel Rydh
11  *
12  *   This program is free software; you can redistribute it and/or
13  *   modify it under the terms of the GNU General Public License
14  *   as published by the Free Software Foundation
15  *
16  */
17 
18 #include "config.h"
19 #include "libopenbios/bindings.h"
20 #include "fs/fs.h"
21 #include "filesys.h"
22 #include "glue.h"
23 #include "libc/diskio.h"
24 #include "libc/vsprintf.h"
25 
26 extern void     grubfs_init( void );
27 
28 /************************************************************************/
29 /* 	grub GLOBALS (horrible... but difficult to fix)			*/
30 /************************************************************************/
31 
32 /* the grub drivers want these: */
33 int		filepos;
34 int		filemax;
35 grub_error_t	errnum;
36 char		FSYS_BUF[FSYS_BUFLEN];
37 
38 /* these are not even used by us, instead
39  * the grub fs drivers want them:
40  */
41 int		fsmax;
42 void		(*disk_read_hook) (int, int, int);
43 void		(*disk_read_func) (int, int, int);
44 
45 
46 /************************************************************************/
47 /*	filsystem table							*/
48 /************************************************************************/
49 
50 typedef struct fsys_entry {
51         const char *name;
52 	int	(*mount_func) (void);
53 	int	(*read_func) (char *buf, int len);
54 	int	(*dir_func) (char *dirname);
55 	void	(*close_func) (void);
56 	int	(*embed_func) (int *start_sector, int needed_sectors);
57 } fsys_entry_t;
58 
59 static const struct fsys_entry fsys_table[] = {
60 # ifdef CONFIG_FSYS_FAT
61     {"fat", fat_mount, fat_read, fat_dir, NULL, NULL},
62 # endif
63 # ifdef CONFIG_FSYS_EXT2FS
64     {"ext2fs", ext2fs_mount, ext2fs_read, ext2fs_dir, NULL, NULL},
65 # endif
66 # ifdef CONFIG_FSYS_MINIX
67     {"minix", minix_mount, minix_read, minix_dir, NULL, NULL},
68 # endif
69 # ifdef CONFIG_FSYS_REISERFS
70     {"reiserfs", reiserfs_mount, reiserfs_read, reiserfs_dir, NULL, reiserfs_embed},
71 # endif
72 # ifdef CONFIG_FSYS_JFS
73     {"jfs", jfs_mount, jfs_read, jfs_dir, NULL, jfs_embed},
74 # endif
75 # ifdef CONFIG_FSYS_XFS
76     {"xfs", xfs_mount, xfs_read, xfs_dir, NULL, NULL},
77 # endif
78 # ifdef CONFIG_FSYS_UFS
79     {"ufs", ufs_mount, ufs_read, ufs_dir, NULL, ufs_embed},
80 # endif
81 # ifdef CONFIG_FSYS_ISO9660
82     {"iso9660", iso9660_mount, iso9660_read, iso9660_dir, NULL, NULL},
83 # endif
84 # ifdef CONFIG_FSYS_NTFS
85     {"ntfs", ntfs_mount, ntfs_read, ntfs_dir, NULL, NULL},
86 # endif
87 # ifdef CONFIG_FSYS_AFFS
88     {"affs", affs_mount, affs_read, affs_dir, NULL, NULL},
89 # endif
90 };
91 
92 /* We don't provide a file search mechanism (yet) */
93 typedef struct {
94 	unsigned long	pos;
95 	unsigned long	len;
96 	const char	*path;
97 } grubfile_t;
98 
99 typedef struct {
100 	const struct fsys_entry *fsys;
101 	grubfile_t *fd;
102 	int dev_fd;
103 	long long offset;	/* Offset added onto each device read; should only ever be non-zero
104 				when probing a partition for a filesystem */
105 } grubfs_t;
106 
107 typedef struct {
108 	grubfs_t *gfs;
109 } grubfs_info_t;
110 
111 /* Static block and global pointer required for I/O glue */
112 static grubfs_t dummy_fs;
113 static grubfs_t *curfs = &dummy_fs;
114 
115 DECLARE_NODE( grubfs, 0, sizeof(grubfs_info_t), "+/packages/grubfs-files" );
116 
117 
118 /************************************************************************/
119 /*	I/O glue (called by grub source)				*/
120 /************************************************************************/
121 
122 int
devread(unsigned long sector,unsigned long byte_offset,unsigned long byte_len,void * buf)123 devread( unsigned long sector, unsigned long byte_offset,
124 	 unsigned long byte_len, void *buf )
125 {
126 	long long offs = (long long)sector * 512 + byte_offset;
127 
128 #ifdef CONFIG_DEBUG_FS
129 	//printk("devread s=%x buf=%x, fd=%x\n",sector, buf, curfs->dev_fd);
130 #endif
131 
132 	if( !curfs ) {
133 #ifdef CONFIG_DEBUG_FS
134 		printk("devread: fsys == NULL!\n");
135 #endif
136 		return -1;
137 	}
138 
139 	if( seek_io(curfs->dev_fd, offs + curfs->offset) ) {
140 #ifdef CONFIG_DEBUG_FS
141 		printk("seek failure\n");
142 #endif
143 		return -1;
144 	}
145 	return (read_io(curfs->dev_fd, buf, byte_len) == byte_len) ? 1:0;
146 }
147 
148 int
file_read(void * buf,unsigned long len)149 file_read( void *buf, unsigned long len )
150 {
151 	if (filepos < 0 || filepos > filemax)
152 		filepos = filemax;
153 	if (len > filemax-filepos)
154 		len = filemax - filepos;
155 	errnum = 0;
156 	return curfs->fsys->read_func( buf, len );
157 }
158 
159 
160 /************************************************************************/
161 /*	Standard package methods					*/
162 /************************************************************************/
163 
164 /* ( -- success? ) */
165 static void
grubfs_files_open(grubfs_info_t * mi)166 grubfs_files_open( grubfs_info_t *mi )
167 {
168 	int fd, i;
169 	char *path = my_args_copy();
170 	char *s;
171 
172 	fd = open_ih( my_parent() );
173 	if ( fd == -1 ) {
174 		free( path );
175 		RET( 0 );
176 	}
177 
178 	mi->gfs = &dummy_fs;
179 
180 	for (i = 0; i < sizeof(fsys_table)/sizeof(fsys_table[0]); i++) {
181 #ifdef CONFIG_DEBUG_FS
182 		printk("Trying %s\n", fsys_table[i].name);
183 #endif
184 		if (fsys_table[i].mount_func()) {
185 			const fsys_entry_t *fsys = &fsys_table[i];
186 #ifdef CONFIG_DEBUG_FS
187 			printk("Mounted %s\n", fsys->name);
188 #endif
189 			mi->gfs = malloc(sizeof(grubfs_t));
190 			mi->gfs->fsys = fsys;
191 			mi->gfs->dev_fd = fd;
192 			mi->gfs->offset = 0;
193 
194 			s = path;
195 			while (*s) {
196 				if(*s=='\\') *s='/';
197 				s++;
198 			}
199 #ifdef CONFIG_DEBUG_FS
200 			printk("Path=%s\n",path);
201 #endif
202 			if (!mi->gfs->fsys->dir_func((char *) path)) {
203 				forth_printf("File not found\n");
204 				RET( 0 );
205 			}
206 
207 			mi->gfs->fd = malloc(sizeof(grubfile_t));
208 			mi->gfs->fd->pos = filepos;
209 			mi->gfs->fd->len = filemax;
210 			mi->gfs->fd->path = strdup(path);
211 
212 			RET( -1 );
213 		}
214 	}
215 #ifdef CONFIG_DEBUG_FS
216 	printk("Unknown filesystem type\n");
217 #endif
218 
219 	RET( 0 );
220 }
221 
222 /* ( -- ) */
223 static void
grubfs_files_close(grubfs_info_t * mi)224 grubfs_files_close( grubfs_info_t *mi )
225 {
226 	grubfile_t *gf = mi->gfs->fd;
227 
228 	if (gf->path)
229 		free((void *)(gf->path));
230 	free(gf);
231 
232 	filepos = 0;
233 	filemax = 0;
234 }
235 
236 /* ( buf len -- actlen ) */
237 static void
grubfs_files_read(grubfs_info_t * mi)238 grubfs_files_read( grubfs_info_t *mi )
239 {
240 	int count = POP();
241 	char *buf = (char *)cell2pointer(POP());
242 
243 	grubfile_t *file = mi->gfs->fd;
244         int ret;
245 
246 	filepos = file->pos;
247 	filemax = file->len;
248 
249 	if (count > filemax - filepos)
250 		count = filemax - filepos;
251 
252 	ret = mi->gfs->fsys->read_func(buf, count);
253 
254 	file->pos = filepos;
255 
256 	RET( ret );
257 }
258 
259 /* ( pos.d -- status ) */
260 static void
grubfs_files_seek(grubfs_info_t * mi)261 grubfs_files_seek( grubfs_info_t *mi )
262 {
263 	long long pos = DPOP();
264 	int offs = (int)pos;
265 	int whence = SEEK_SET;
266 
267 	grubfile_t *file = mi->gfs->fd;
268 	unsigned long newpos;
269 
270 	switch( whence ) {
271 	case SEEK_END:
272 		if (offs < 0 && (unsigned long) -offs > file->len)
273 			newpos = 0;
274 		else
275 			newpos = file->len + offs;
276 		break;
277 	default:
278 	case SEEK_SET:
279 		newpos = (offs < 0) ? 0 : offs;
280 		break;
281 	}
282 
283 	if (newpos > file->len)
284 		newpos = file->len;
285 
286 	file->pos = newpos;
287 
288 	if (newpos)
289 		RET( -1 );
290 	else
291 		RET( 0 );
292 }
293 
294 /* ( addr -- size ) */
295 static void
grubfs_files_load(grubfs_info_t * mi)296 grubfs_files_load( grubfs_info_t *mi )
297 {
298 	char *buf = (char *)cell2pointer(POP());
299 	int count, ret;
300 
301 	grubfile_t *file = mi->gfs->fd;
302 	count = file->len;
303 
304 	ret = mi->gfs->fsys->read_func(buf, count);
305 	file->pos = filepos;
306 
307 	RET( ret );
308 }
309 
310 /* ( -- cstr ) */
311 static void
grubfs_files_get_path(grubfs_info_t * mi)312 grubfs_files_get_path( grubfs_info_t *mi )
313 {
314 	grubfile_t *file = mi->gfs->fd;
315 	const char *path = file->path;
316 
317 	RET( pointer2cell(strdup(path)) );
318 }
319 
320 /* ( -- cstr ) */
321 static void
grubfs_files_get_fstype(grubfs_info_t * mi)322 grubfs_files_get_fstype( grubfs_info_t *mi )
323 {
324 	grubfs_t *gfs = mi->gfs;
325 
326 	PUSH( pointer2cell(strdup(gfs->fsys->name)) );
327 }
328 
329 
330 /* static method, ( pos.d ih -- flag? ) */
331 static void
grubfs_files_probe(grubfs_info_t * dummy)332 grubfs_files_probe( grubfs_info_t *dummy )
333 {
334 	ihandle_t ih = POP_ih();
335 	long long offs = DPOP();
336 	int i;
337 
338 	curfs->dev_fd = open_ih(ih);
339         if (curfs->dev_fd == -1) {
340                 RET( -1 );
341         }
342 	curfs->offset = offs;
343 
344 	for (i = 0; i < sizeof(fsys_table)/sizeof(fsys_table[0]); i++) {
345 #ifdef CONFIG_DEBUG_FS
346 		printk("Probing for %s\n", fsys_table[i].name);
347 #endif
348 		if (fsys_table[i].mount_func()) {
349 			RET( -1 );
350 		}
351 	}
352 
353 #ifdef CONFIG_DEBUG_FS
354 	printk("Unknown filesystem type\n");
355 #endif
356 
357 	close_io(curfs->dev_fd);
358 
359 	RET ( 0 );
360 }
361 
362 /* static method, ( pathstr len ihandle -- ) */
363 static void
grubfs_files_dir(grubfs_info_t * dummy)364 grubfs_files_dir( grubfs_info_t *dummy )
365 {
366 	forth_printf("dir method not implemented for grubfs filesystem\n");
367 	POP();
368 	POP();
369 	POP();
370 }
371 
372 static void
grubfs_initializer(grubfs_info_t * dummy)373 grubfs_initializer( grubfs_info_t *dummy )
374 {
375 	fword("register-fs-package");
376 }
377 
378 NODE_METHODS( grubfs ) = {
379 	{ "probe",	grubfs_files_probe	},
380 	{ "open",	grubfs_files_open	},
381 	{ "close",	grubfs_files_close 	},
382 	{ "read",	grubfs_files_read	},
383 	{ "seek",	grubfs_files_seek	},
384 	{ "load",	grubfs_files_load	},
385 	{ "dir",	grubfs_files_dir	},
386 
387 	/* special */
388 	{ "get-path",	grubfs_files_get_path	},
389 	{ "get-fstype",	grubfs_files_get_fstype	},
390 
391 	{ NULL,		grubfs_initializer	},
392 };
393 
394 void
grubfs_init(void)395 grubfs_init( void )
396 {
397 	REGISTER_NODE( grubfs );
398 }
399