1 /* 2 * Copyright (c) 2006-2018, RT-Thread Development Team 3 * 4 * SPDX-License-Identifier: Apache-2.0 5 * 6 * Change Logs: 7 * Date Author Notes 8 * 2012-1-7 prife the first version 9 */ 10 11 #include <rtthread.h> 12 #include <rtdevice.h> 13 14 #include "cyg/infra/cyg_type.h" 15 #include "cyg/fileio/fileio.h" 16 #include "port/codes.h" 17 #include "port/fcntl.h" 18 #undef mode_t 19 20 #include <dfs_fs.h> 21 #include <dfs_file.h> 22 23 #include "dfs_jffs2.h" 24 #include "jffs2_config.h" 25 #include "porting.h" 26 #include <string.h> 27 28 #if DEVICE_PART_MAX > 1 29 #error "support only one jffs2 partition on a flash device!" 30 #endif 31 32 /* make sure the following struct var had been initilased to 0! */ 33 struct device_part 34 { 35 struct cyg_mtab_entry * mte; 36 struct rt_mtd_nor_device *dev; 37 }; 38 static struct device_part device_partition[DEVICE_PART_MAX] = {0}; 39 static struct rt_mutex jffs2_lock; 40 41 #define jffs2_mount jffs2_fste.mount 42 #define jffs2_umount jffs2_fste.umount 43 #define jffs2_open jffs2_fste.open 44 #define jffs2_file_unlink jffs2_fste.unlink 45 #define jffs2_mkdir jffs2_fste.mkdir 46 #define jffs2_rmdir jffs2_fste.rmdir 47 #define jffs2_rename jffs2_fste.rename 48 #define jffs2_link jffs2_fste.link 49 #define jffs2_opendir jffs2_fste.opendir 50 #define jffs2_chdir jffs2_fste.chdir 51 #define jffs2_ops_stat jffs2_fste.stat 52 #define jffs2_getinfo jffs2_fste.getinfo 53 #define jffs2_setinfo jffs2_fste.setinfo 54 55 #define jffs2_file_read jffs2_fileops.fo_read 56 #define jffs2_file_write jffs2_fileops.fo_write 57 #define jffs2_file_lseek jffs2_fileops.fo_lseek 58 #define jffs2_file_ioctl jffs2_fileops.fo_ioctl 59 #define jffs2_file_select jffs2_fileops.fo_select 60 #define jffs2_file_fsync jffs2_fileops.fo_fsync 61 #define jffs2_file_colse jffs2_fileops.fo_close 62 #define jffs2_file_fstat jffs2_fileops.fo_fstat 63 #define jffs2_file_getinfo jffs2_fileops.fo_getinfo 64 #define jffs2_file_setinfo jffs2_fileops.fo_setinfo 65 66 #define jffs2_dir_read jffs2_dirops.fo_read 67 //#define jffs2_dir_write jffs2_dirops.fo_write 68 #define jffs2_dir_lseek jffs2_dirops.fo_lseek 69 //#define jffs2_dir_ioctl jffs2_dirops.fo_ioctl 70 #define jffs2_dir_select jffs2_dirops.fo_select 71 //#define jffs2_dir_fsync jffs2_dirops.fo_fsync 72 #define jffs2_dir_colse jffs2_dirops.fo_close 73 //#define jffs2_dir_fstat jffs2_dirops.fo_fstat 74 //#define jffs2_dir_getinfo jffs2_dirops.fo_getinfo 75 //#define jffs2_dir_setinfo jffs2_dirops.fo_setinfo 76 77 /* 78 * RT-Thread Device Interface for jffs2 79 */ 80 81 /* these code is in src/flashio.c */ 82 static int jffs2_result_to_dfs(int result) 83 { 84 if (result < 0) return result; 85 if (result > 0) return -result; 86 87 return 0; 88 } 89 90 /* 91 * RT-Thread DFS Interface for jffs2 92 */ 93 static int dfs_jffs2_mount(struct dfs_filesystem* fs, 94 unsigned long rwflag, 95 const void* data) 96 { 97 unsigned index; 98 struct cyg_mtab_entry * mte; 99 int result; 100 101 /* find a empty entry in partition table */ 102 for (index = 0; index < DEVICE_PART_MAX; index ++) 103 { 104 if (device_partition[index].dev == RT_NULL) 105 break; 106 } 107 if (index == DEVICE_PART_MAX) 108 return -ENOSPC; 109 110 mte = rt_malloc(sizeof(struct cyg_mtab_entry)); 111 if (mte == RT_NULL) 112 return -ENOMEM; 113 114 mte->name = fs->path; 115 mte->fsname = "jffs2"; 116 mte->devname = NULL; 117 /* note that, i use mte->data to store rtt's device 118 * while, in jffs2_mount, mte->data will be copy into 119 * s_dev in struct super_block, and mte->data will be 120 * filled with jffs2_sb(see the source of jffs2_mount. 121 */ 122 mte->data = (CYG_ADDRWORD)fs->dev_id; 123 124 device_partition[index].dev = RT_MTD_NOR_DEVICE(fs->dev_id); 125 /* after jffs2_mount, mte->data will not be dev_id any more */ 126 result = jffs2_mount(NULL, mte); 127 if (result != 0) 128 { 129 device_partition[index].dev = NULL; 130 return jffs2_result_to_dfs(result); 131 } 132 133 /* save this pointer */ 134 device_partition[index].mte = mte; 135 return 0; 136 } 137 138 static int _find_fs(struct cyg_mtab_entry ** mte, rt_device_t dev_id) 139 { 140 unsigned index; 141 142 /* find device index */ 143 for (index = 0; index < DEVICE_PART_MAX; index++) 144 { 145 if (device_partition[index].dev == RT_MTD_NOR_DEVICE(dev_id)) 146 { 147 *mte = device_partition[index].mte; 148 return 0; 149 } 150 } 151 152 rt_kprintf("error, could not found the fs!"); 153 return -1; 154 } 155 156 static int dfs_jffs2_unmount(struct dfs_filesystem* fs) 157 { 158 int result; 159 unsigned index; 160 161 /* find device index, then umount it */ 162 for (index = 0; index < DEVICE_PART_MAX; index++) 163 { 164 if (device_partition[index].dev == RT_MTD_NOR_DEVICE(fs->dev_id)) 165 { 166 result = jffs2_umount(device_partition[index].mte); 167 if (result) return jffs2_result_to_dfs(result); 168 169 rt_free(device_partition[index].mte); 170 device_partition[index].dev = NULL; 171 device_partition[index].mte = NULL; 172 return RT_EOK; 173 } 174 } 175 176 return -ENOENT; 177 } 178 179 static int dfs_jffs2_mkfs(rt_device_t dev_id) 180 { 181 /* just erase all blocks on this nand partition */ 182 return -ENOSYS; 183 } 184 185 static int dfs_jffs2_statfs(struct dfs_filesystem* fs, 186 struct statfs *buf) 187 { 188 /* since the limit of unsigned long, so the max size of flash device is 4G */ 189 struct cyg_mtab_entry * mte; 190 struct jffs2_fs_info info; 191 int result; 192 193 result = _find_fs(&mte, fs->dev_id); 194 if (result) 195 return -ENOENT; 196 197 RT_ASSERT(mte->data != 0); 198 199 jffs2_get_info_from_sb((void *)mte->data, &info); 200 buf->f_bsize = info.sector_size; 201 buf->f_blocks = info.nr_blocks; 202 buf->f_bfree = info.free_size / info.sector_size; 203 204 return 0; 205 } 206 207 static const char jffs2_root_path[] = "."; 208 209 static int dfs_jffs2_open(struct dfs_fd* file) 210 { 211 int result; 212 int oflag, mode; 213 const char * name; 214 cyg_file * jffs2_file; 215 struct dfs_filesystem *fs; 216 struct cyg_mtab_entry * mte; 217 218 oflag = file->flags; 219 fs = (struct dfs_filesystem *)file->data; 220 RT_ASSERT(fs != RT_NULL); 221 222 jffs2_file = rt_malloc(sizeof(cyg_file)); 223 if (jffs2_file == RT_NULL) 224 return -ENOMEM; 225 226 /* just escape '/' provided by dfs code */ 227 name = file->path; 228 if ((name[0] == '/') && (name[1] == 0)) 229 name = jffs2_root_path; 230 else /* name[0] still will be '/' */ 231 name ++; 232 233 result = _find_fs(&mte, fs->dev_id); 234 if (result) 235 { 236 rt_free(jffs2_file); 237 return -ENOENT; 238 } 239 240 /* set mount table */ 241 jffs2_file->f_mte = mte; 242 243 if (oflag & O_DIRECTORY) /* operations about dir */ 244 { 245 rt_mutex_take(&jffs2_lock, RT_WAITING_FOREVER); 246 if (oflag & O_CREAT) /* create a dir*/ 247 { 248 /* fixme, should test file->path can end with '/' */ 249 result = jffs2_mkdir(mte, mte->root, name); 250 if (result) 251 { 252 rt_mutex_release(&jffs2_lock); 253 rt_free(jffs2_file); 254 return jffs2_result_to_dfs(result); 255 } 256 } 257 258 /* open dir */ 259 result = jffs2_opendir(mte, mte->root, name, jffs2_file); 260 rt_mutex_release(&jffs2_lock); 261 if (result) 262 { 263 rt_free(jffs2_file); 264 return jffs2_result_to_dfs(result); 265 } 266 #ifdef CONFIG_JFFS2_NO_RELATIVEDIR 267 jffs2_file->f_offset = 2; 268 #endif 269 /* save this pointer, it will be used by dfs_jffs2_getdents*/ 270 file->data = jffs2_file; 271 return 0; 272 } 273 /* regular file operations */ 274 mode = JFFS2_O_RDONLY; 275 if (oflag & O_WRONLY) mode |= JFFS2_O_WRONLY; 276 if (oflag & O_RDWR) mode |= JFFS2_O_RDWR; 277 /* Opens the file, if it is existing. If not, a new file is created. */ 278 if (oflag & O_CREAT) mode |= JFFS2_O_CREAT; 279 /* Creates a new file. If the file is existing, it is truncated and overwritten. */ 280 if (oflag & O_TRUNC) mode |= JFFS2_O_TRUNC; 281 /* Creates a new file. The function fails if the file is already existing. */ 282 if (oflag & O_EXCL) mode |= JFFS2_O_EXCL; 283 284 rt_mutex_take(&jffs2_lock, RT_WAITING_FOREVER); 285 result = jffs2_open(mte, 0, name, mode, jffs2_file); 286 if (result != 0) 287 { 288 rt_mutex_release(&jffs2_lock); 289 rt_free(jffs2_file); 290 return jffs2_result_to_dfs(result); 291 } 292 293 /* save this pointer, it will be used when calling read(), write(), 294 flush(), lessk(), and will be rt_free when calling close()*/ 295 file->data = jffs2_file; 296 file->pos = jffs2_file->f_offset; 297 file->size = 0; 298 jffs2_file_lseek(jffs2_file, (off_t *)(&(file->size)), SEEK_END); 299 jffs2_file->f_offset = (off_t)file->pos; 300 rt_mutex_release(&jffs2_lock); 301 302 if (oflag & O_APPEND) 303 { 304 file->pos = file->size; 305 jffs2_file->f_offset = file->size; 306 } 307 308 return 0; 309 } 310 311 static int dfs_jffs2_close(struct dfs_fd* file) 312 { 313 int result; 314 cyg_file * jffs2_file; 315 316 RT_ASSERT(file->data != NULL); 317 jffs2_file = (cyg_file *)(file->data); 318 319 if (file->flags & O_DIRECTORY) /* operations about dir */ 320 { 321 rt_mutex_take(&jffs2_lock, RT_WAITING_FOREVER); 322 result = jffs2_dir_colse(jffs2_file); 323 rt_mutex_release(&jffs2_lock); 324 if (result) 325 return jffs2_result_to_dfs(result); 326 327 rt_free(jffs2_file); 328 return 0; 329 } 330 /* regular file operations */ 331 rt_mutex_take(&jffs2_lock, RT_WAITING_FOREVER); 332 result = jffs2_file_colse(jffs2_file); 333 rt_mutex_release(&jffs2_lock); 334 if (result) 335 return jffs2_result_to_dfs(result); 336 337 /* release memory */ 338 rt_free(jffs2_file); 339 return 0; 340 } 341 342 static int dfs_jffs2_ioctl(struct dfs_fd* file, int cmd, void* args) 343 { 344 return -ENOSYS; 345 } 346 347 static int dfs_jffs2_read(struct dfs_fd* file, void* buf, size_t len) 348 { 349 cyg_file * jffs2_file; 350 struct CYG_UIO_TAG uio_s; 351 struct CYG_IOVEC_TAG iovec; 352 int char_read; 353 int result; 354 355 RT_ASSERT(file->data != NULL); 356 jffs2_file = (cyg_file *)(file->data); 357 uio_s.uio_iov = &iovec; 358 uio_s.uio_iov->iov_base = buf; 359 uio_s.uio_iov->iov_len = len; 360 uio_s.uio_iovcnt = 1; //must be 1 361 //uio_s.uio_offset //not used... 362 uio_s.uio_resid = uio_s.uio_iov->iov_len; //seem no use in jffs2; 363 364 char_read = jffs2_file->f_offset; /* the current offset */ 365 rt_mutex_take(&jffs2_lock, RT_WAITING_FOREVER); 366 result = jffs2_file_read(jffs2_file, &uio_s); 367 rt_mutex_release(&jffs2_lock); 368 if (result) 369 return jffs2_result_to_dfs(result); 370 371 /* update position */ 372 file->pos = jffs2_file->f_offset; 373 char_read = jffs2_file->f_offset - char_read; 374 return char_read; 375 } 376 377 static int dfs_jffs2_write(struct dfs_fd* file, 378 const void* buf, 379 size_t len) 380 { 381 cyg_file * jffs2_file; 382 struct CYG_UIO_TAG uio_s; 383 struct CYG_IOVEC_TAG iovec; 384 int char_write; 385 int result; 386 387 RT_ASSERT(file->data != NULL); 388 jffs2_file = (cyg_file *)(file->data); 389 uio_s.uio_iov = &iovec; 390 uio_s.uio_iov->iov_base = (void *)buf; 391 uio_s.uio_iov->iov_len = len; 392 uio_s.uio_iovcnt = 1; //must be 1 393 //uio_s.uio_offset //not used... 394 uio_s.uio_resid = uio_s.uio_iov->iov_len; //seem no use in jffs2; 395 396 char_write = jffs2_file->f_offset; 397 rt_mutex_take(&jffs2_lock, RT_WAITING_FOREVER); 398 result = jffs2_file_write(jffs2_file, &uio_s); 399 rt_mutex_release(&jffs2_lock); 400 if (result) 401 return jffs2_result_to_dfs(result); 402 403 /* update position */ 404 file->pos = jffs2_file->f_offset; 405 char_write = jffs2_file->f_offset - char_write; 406 return char_write; 407 } 408 409 static int dfs_jffs2_flush(struct dfs_fd* file) 410 { 411 /* infact, jffs2 not support, jffs2_fo_sync just return ok */ 412 return -ENOSYS; 413 } 414 415 /* fixme warning: the offset is rt_off_t, so maybe the size of a file is must <= 2G*/ 416 static int dfs_jffs2_lseek(struct dfs_fd* file, 417 rt_off_t offset) 418 { 419 cyg_file * jffs2_file; 420 int result; 421 422 RT_ASSERT(file->data != NULL); 423 jffs2_file = (cyg_file *)(file->data); 424 425 /* set offset as current offset */ 426 rt_mutex_take(&jffs2_lock, RT_WAITING_FOREVER); 427 result = jffs2_file_lseek(jffs2_file, &offset, SEEK_SET); 428 rt_mutex_release(&jffs2_lock); 429 if (result) 430 return jffs2_result_to_dfs(result); 431 /* update file position */ 432 file->pos = offset; 433 return offset; 434 } 435 436 /* return the size of struct dirent*/ 437 static int dfs_jffs2_getdents(struct dfs_fd* file, 438 struct dirent* dirp, 439 rt_uint32_t count) 440 { 441 cyg_file * jffs2_file; 442 struct CYG_UIO_TAG uio_s; 443 struct CYG_IOVEC_TAG iovec; 444 struct jffs2_dirent jffs2_d; 445 struct dirent * d; 446 rt_uint32_t index; 447 #if !defined (CYGPKG_FS_JFFS2_RET_DIRENT_DTYPE) 448 struct jffs2_stat s; 449 cyg_mtab_entry * mte; 450 char * fullname; 451 #endif 452 int result; 453 454 RT_ASSERT(file->data != RT_NULL); 455 jffs2_file = (cyg_file*)(file->data); 456 mte = jffs2_file->f_mte; 457 458 //set jffs2_d 459 memset(&jffs2_d, 0, sizeof(struct jffs2_dirent)); 460 //set CYG_UIO_TAG uio_s 461 uio_s.uio_iov = &iovec; 462 uio_s.uio_iov->iov_base = &jffs2_d; 463 uio_s.uio_iov->iov_len = sizeof(struct jffs2_dirent);; 464 uio_s.uio_iovcnt = 1; //must be 1 465 uio_s.uio_offset = 0;//not used... 466 uio_s.uio_resid = uio_s.uio_iov->iov_len; //seem no use in jffs2; 467 468 /* make integer count, usually count is 1 */ 469 count = (count / sizeof(struct dirent)) * sizeof(struct dirent); 470 if (count == 0) return -EINVAL; 471 472 index = 0; 473 /* usually, the while loop should only be looped only once! */ 474 while (1) 475 { 476 d = dirp + index; 477 rt_mutex_take(&jffs2_lock, RT_WAITING_FOREVER); 478 result = jffs2_dir_read(jffs2_file, &uio_s); 479 rt_mutex_release(&jffs2_lock); 480 /* if met a error or all entry are read over, break while*/ 481 if (result || jffs2_d.d_name[0] == 0) 482 break; 483 484 #if defined (CYGPKG_FS_JFFS2_RET_DIRENT_DTYPE) 485 switch(jffs2_d.d_type & JFFS2_S_IFMT) 486 { 487 case JFFS2_S_IFREG: d->d_type = DT_REG; break; 488 case JFFS2_S_IFDIR: d->d_type = DT_DIR; break; 489 default: d->d_type = DT_UNKNOWN; break; 490 } 491 #else 492 fullname = rt_malloc(FILE_PATH_MAX); 493 if(fullname == RT_NULL) 494 return -ENOMEM; 495 496 /* make a right entry */ 497 if ((file->path[0] == '/') ) 498 { 499 if (file->path[1] == 0) 500 strcpy(fullname, jffs2_d.d_name); 501 else 502 rt_sprintf(fullname, "%s/%s", file->path+1, jffs2_d.d_name); 503 } 504 else 505 rt_sprintf(fullname, "%s/%s", file->path, jffs2_d.d_name); 506 rt_mutex_take(&jffs2_lock, RT_WAITING_FOREVER); 507 result = jffs2_porting_stat(mte, mte->root, fullname, (void *)&s); 508 rt_mutex_release(&jffs2_lock); 509 if (result) 510 return jffs2_result_to_dfs(result); 511 512 rt_free(fullname); 513 /* convert to dfs stat structure */ 514 switch(s.st_mode & JFFS2_S_IFMT) 515 { 516 case JFFS2_S_IFREG: d->d_type = DT_REG; break; 517 case JFFS2_S_IFDIR: d->d_type = DT_DIR; break; 518 default: d->d_type = DT_UNKNOWN; break; 519 } 520 #endif 521 /* write the rest fields of struct dirent* dirp */ 522 d->d_namlen = rt_strlen(jffs2_d.d_name); 523 d->d_reclen = (rt_uint16_t)sizeof(struct dirent); 524 rt_strncpy(d->d_name, jffs2_d.d_name, d->d_namlen + 1); 525 526 index ++; 527 if (index * sizeof(struct dirent) >= count) 528 break; 529 } 530 if (result) 531 return jffs2_result_to_dfs(result); 532 return index * sizeof(struct dirent); 533 } 534 535 static int dfs_jffs2_unlink(struct dfs_filesystem* fs, const char* path) 536 { 537 int result; 538 struct jffs2_stat s; 539 cyg_mtab_entry * mte; 540 541 result = _find_fs(&mte, fs->dev_id); 542 if (result) 543 return -ENOENT; 544 545 /* deal path */ 546 if (path[0] == '/') 547 path++; 548 549 /* judge file type, dir is to be delete by rmdir, others by unlink */ 550 rt_mutex_take(&jffs2_lock, RT_WAITING_FOREVER); 551 result = jffs2_porting_stat(mte, mte->root, path, (void *)&s); 552 if (result) 553 { 554 rt_mutex_release(&jffs2_lock); 555 return jffs2_result_to_dfs(result); 556 } 557 558 switch(s.st_mode & JFFS2_S_IFMT) 559 { 560 case JFFS2_S_IFREG: 561 result = jffs2_file_unlink(mte, mte->root, path); 562 break; 563 case JFFS2_S_IFDIR: 564 result = jffs2_rmdir(mte, mte->root, path); 565 break; 566 default: 567 /* unknown file type */ 568 rt_mutex_release(&jffs2_lock); 569 return -1; 570 } 571 rt_mutex_release(&jffs2_lock); 572 if (result) 573 return jffs2_result_to_dfs(result); 574 return 0; 575 } 576 577 static int dfs_jffs2_rename(struct dfs_filesystem* fs, 578 const char* oldpath, 579 const char* newpath) 580 { 581 int result; 582 cyg_mtab_entry * mte; 583 584 result = _find_fs(&mte, fs->dev_id); 585 if (result) 586 return -ENOENT; 587 588 if (*oldpath == '/') 589 oldpath += 1; 590 if (*newpath == '/') 591 newpath += 1; 592 rt_mutex_take(&jffs2_lock, RT_WAITING_FOREVER); 593 result = jffs2_rename(mte, mte->root, oldpath, mte->root, newpath); 594 rt_mutex_release(&jffs2_lock); 595 if (result) 596 return jffs2_result_to_dfs(result); 597 return 0; 598 } 599 600 static int dfs_jffs2_stat(struct dfs_filesystem* fs, const char *path, struct stat *st) 601 { 602 int result; 603 struct jffs2_stat s; 604 cyg_mtab_entry * mte; 605 606 /* deal the path for jffs2 */ 607 RT_ASSERT(!((path[0] == '/') && (path[1] == 0))); 608 609 if (path[0] == '/') 610 path++; 611 612 result = _find_fs(&mte, fs->dev_id); 613 if (result) 614 return -ENOENT; 615 616 rt_mutex_take(&jffs2_lock, RT_WAITING_FOREVER); 617 result = jffs2_porting_stat(mte, mte->root, path, (void *)&s); 618 rt_mutex_release(&jffs2_lock); 619 620 if (result) 621 return jffs2_result_to_dfs(result); 622 /* convert to dfs stat structure */ 623 switch(s.st_mode & JFFS2_S_IFMT) 624 { 625 case JFFS2_S_IFREG: 626 st->st_mode = S_IFREG | S_IRUSR | S_IRGRP | S_IROTH | 627 S_IWUSR | S_IWGRP | S_IWOTH; 628 break; 629 630 case JFFS2_S_IFDIR: 631 st->st_mode = S_IFDIR | S_IXUSR | S_IXGRP | S_IXOTH; 632 break; 633 634 default: 635 st->st_mode = DT_UNKNOWN; //fixme 636 break; 637 } 638 639 st->st_dev = 0; 640 st->st_size = s.st_size; 641 st->st_mtime = s.st_mtime; 642 643 return 0; 644 } 645 646 static const struct dfs_file_ops _jffs2_fops = 647 { 648 dfs_jffs2_open, 649 dfs_jffs2_close, 650 dfs_jffs2_ioctl, 651 dfs_jffs2_read, 652 dfs_jffs2_write, 653 dfs_jffs2_flush, 654 dfs_jffs2_lseek, 655 dfs_jffs2_getdents, 656 }; 657 658 static const struct dfs_filesystem_ops _jffs2_ops = 659 { 660 "jffs2", 661 DFS_FS_FLAG_DEFAULT, 662 &_jffs2_fops, 663 664 dfs_jffs2_mount, 665 dfs_jffs2_unmount, 666 dfs_jffs2_mkfs, 667 dfs_jffs2_statfs, 668 669 dfs_jffs2_unlink, 670 dfs_jffs2_stat, 671 dfs_jffs2_rename, 672 }; 673 674 int dfs_jffs2_init(void) 675 { 676 /* register fatfs file system */ 677 dfs_register(&_jffs2_ops); 678 679 /* initialize mutex */ 680 if (rt_mutex_init(&jffs2_lock, "jffs2lock", RT_IPC_FLAG_FIFO) != RT_EOK) 681 { 682 rt_kprintf("init jffs2 lock mutex failed\n"); 683 } 684 rt_kprintf("init jffs2 lock mutex okay\n"); 685 return 0; 686 } 687 INIT_COMPONENT_EXPORT(dfs_jffs2_init); 688