diff options
Diffstat (limited to 'ext2fs')
-rw-r--r-- | ext2fs/ChangeLog | 588 | ||||
-rw-r--r-- | ext2fs/Makefile | 18 | ||||
-rw-r--r-- | ext2fs/balloc.c | 149 | ||||
-rw-r--r-- | ext2fs/bitmap.c | 55 | ||||
-rw-r--r-- | ext2fs/devio.c | 51 | ||||
-rw-r--r-- | ext2fs/dir.c | 469 | ||||
-rw-r--r-- | ext2fs/ext2_fs.h | 506 | ||||
-rw-r--r-- | ext2fs/ext2_fs_i.h | 32 | ||||
-rw-r--r-- | ext2fs/ext2fs.c | 196 | ||||
-rw-r--r-- | ext2fs/ext2fs.h | 190 | ||||
-rw-r--r-- | ext2fs/getblk.c | 38 | ||||
-rw-r--r-- | ext2fs/hyper.c | 107 | ||||
-rw-r--r-- | ext2fs/ialloc.c | 38 | ||||
-rw-r--r-- | ext2fs/inode.c | 378 | ||||
-rw-r--r-- | ext2fs/msg.c | 7 | ||||
-rw-r--r-- | ext2fs/pager.c | 301 | ||||
-rw-r--r-- | ext2fs/storeinfo.c | 207 | ||||
-rw-r--r-- | ext2fs/truncate.c | 51 | ||||
-rw-r--r-- | ext2fs/xinl.c | 2 |
19 files changed, 1698 insertions, 1685 deletions
diff --git a/ext2fs/ChangeLog b/ext2fs/ChangeLog deleted file mode 100644 index d8741b9c..00000000 --- a/ext2fs/ChangeLog +++ /dev/null @@ -1,588 +0,0 @@ -Fri Aug 2 12:10:40 1996 Miles Bader <miles@gnu.ai.mit.edu> - - * inode.c (diskfs_write_disknode): If WAIT is false, still record - the write for later, using record_global_poke. - -Thu Aug 1 16:18:59 1996 Miles Bader <miles@gnu.ai.mit.edu> - - * ext2_fs.h (ext2_debug_flag): New decl. - (ext2_debug): Pay attention to EXT2_DEBUG_FLAG. - * ext2fs.c [EXT2FS_DEBUG] (options, ext2_debug_flag): New variables. - [EXT2FS_DEBUG] (parse_opt, diskfs_get_options): New functions. - [EXT2FS_DEBUG] (startup_parents, startup_argp, runtime_parents, - diskfs_runtime_argp): New variables. - [!EXT2FS_DEBUG] (startup_argp): New macro. - (main): Use STARTUP_ARGP instead of DISKFS_STD_DEVICE_STARTUP_ARGP. - * pager.c (diskfs_grow): Fix ext2_debug format strings. - * truncate.c (trunc_direct): Fix ext2_debug call. - -Sat Jul 20 00:58:44 1996 Miles Bader <miles@gnu.ai.mit.edu> - - * ext2fs.h (struct disknode): Remove debugging info. - (RECORD_LAST): Function removed. - (LAST_BUFSZ): Macro removed. - (enum last_act): Type removed. - * inode.c (diskfs_cached_lookup): Don't initialize debugging info. - * pager.c (file_pager_read_page, file_pager_write_page, - pager_unlock_page, diskfs_grow): Don't record debugging info. - * truncate.c (diskfs_truncate): Likewise. - - * pager.c (file_pager_read_page): Set - NODE->dn->last_page_partially_writable if we return such a page. - -Fri Jul 19 15:02:24 1996 Miles Bader <miles@gnu.ai.mit.edu> - - * pager.c (diskfs_grow): Rename OLD_END_BLOCK to END_BLOCK. - Correctly determine whether to set DN->last_page_partially_writable - after allocating new blocks. - - * pager.c (file_pager_read_page, file_pager_write_page): Pass - NODE->dn, not &NODE->dn to RECORD_LAST. - -Mon Jul 15 18:00:26 1996 Miles Bader <miles@gnu.ai.mit.edu> - - * ext2fs.h (struct disknode): Add debugging info. - (RECORD_LAST): New function. - (LAST_BUFSZ): New macro. - (enum last_act): New type. - * pager.c (pager_unlock_page, diskfs_grow, file_pager_read_page, - file_pager_write_page): Record debugging info. - * truncate.c (diskfs_truncate): Likewise. - * inode.c (diskfs_cached_lookup): Initialize debugging info. - - * pager.c (file_pager_read_page): Initialize *WRITELOCK to 0. - -Tue Jun 25 12:22:21 1996 Miles Bader <miles@gnu.ai.mit.edu> - - * ext2fs.h (sync_global): Renamed from sync_global_data. Add WAIT - flag. Don't call diskfs_set_hypermetadata. - (alloc_sync): Call diskfs_set_hypermetadata instead of sync_global_data. - (sync_super_block): Function removed. - * hyper.c (diskfs_readonly_changed): No longer clear the clean bit. - (diskfs_set_hypermetadata): Work correctly. - * truncate.c (diskfs_truncate): Add call diskfs_check_readonly to - clear clean bit. - * inode.c (diskfs_cached_lookup): Use diskfs_check_readonly - instead of diskfs_readonly. - * dir.c (diskfs_lookup_hard, diskfs_dirempty): Likewise. - * pager.c (diskfs_shutdown_pager): Don't shutdown the disk pager, - just sync it. - (diskfs_sync_everything): Call sync_global instead of pokel_sync. - (final_sblock): Variable removed. - (diskfs_grow): Add call diskfs_check_readonly to clear clean bit. - -Mon Jun 24 17:14:25 1996 Miles Bader <miles@gnu.ai.mit.edu> - - * inode.c (check_high_bits, diskfs_validate_owner_change, - diskfs_validate_group_change, diskfs_validate_mode_change, - diskfs_validate_author_change): New functions. - (write_node): For non-hurd filesystems, assert that no hurd - extensions should be used. - -Thu Jun 20 22:36:23 1996 Miles Bader <miles@gnu.ai.mit.edu> - - * ext2fs.c (main): Rename diskfs_device_startup_argp to - diskfs_std_device_startup_argp. - -Sat Jun 15 15:56:01 1996 Miles Bader <miles@gnu.ai.mit.edu> - - * inode.c (read_disknode, write_inode): Use hurd-specific fields - only on a hurd filesystem. - -Fri May 10 09:32:43 1996 Michael I. Bushnell, p/BSG <mib@gnu.ai.mit.edu> - - * inode.c (diskfs_set_statfs): Fix one reference to old name of ST - member. - -Thu May 9 11:52:20 1996 Michael I. Bushnell, p/BSG <mib@gnu.ai.mit.edu> - - * inode.c (diskfs_set_statfs): Expect and fill in new format - statfs buffer. - - * Makefile (ext2fs ext2fs.static): s/ioserver/iohelp/g. - * ext2fs.h: ioserver.h -> iohelp.h. - -Tue May 7 16:22:56 1996 Miles Bader <miles@gnu.ai.mit.edu> - - * storeinfo.c (diskfs_S_file_get_storage_info): Rewrite for new - interface. - -Tue Apr 30 12:51:09 1996 Michael I. Bushnell, p/BSG <mib@gnu.ai.mit.edu> - - * Makefile (targets): Renamed from `target'. - -Fri Apr 26 16:10:19 1996 Michael I. Bushnell, p/BSG <mib@gnu.ai.mit.edu> - - * Makefile (makemode): Now `servers'. - (targets): Renamed from `target'; now include ext2fs.static. - (ext2fs.static-LDFLAGS): Renamed from `LDFLAGS'. - (ext2fs.static): Depend on same things as `ext2fs'. - (include ../Makeconf): Must come before dependency information. - -Wed Apr 17 13:30:49 1996 Miles Bader <miles@gnu.ai.mit.edu> - - * inode.c (diskfs_write_disknode): Only sync DI if WAIT. - - * dir.c (diskfs_lookup_hard): Set atime appropriately, and sync - the new atime if we are running synchronously (!). - (diskfs_dirempty): Likewise. - (diskfs_direnter_hard): Set mtime appropriately. - (diskfs_dirremove_hard): Likewise. - (diskfs_dirrewrite_hard): Likewise. - -Thu Apr 4 18:51:19 1996 Miles Bader <miles@gnu.ai.mit.edu> - - * inode.c (diskfs_cached_lookup): Renamed from iget; all uses updated. - Initialize the CACHE_ID field in the new node. - * ext2fs.h (struct disknode): Get rid of NUMBER field; all references - replaced by references to the CACHE_ID field in the corresponding node. - -Fri Mar 29 11:03:58 1996 Miles Bader <miles@gnu.ai.mit.edu> - - * dir.c (diskfs_null_dirstat): New function. - (diskfs_lookup_hard, diskfs_direnter, diskfs_dirremove_hard, - diskfs_dirrewrite_hard): Renamed from versions without `_hard' suffix. - Get rid of stuff now done by diskfs. - - * ext2fs.c (main): Pass new argument to argp_parse. - -Tue Mar 19 17:52:04 1996 Miles Bader <miles@gnu.ai.mit.edu> - - * pager.c (pager_unlock_page, diskfs_grow): Try to make the logic - a bit simpler and more robust. - -Fri Feb 16 17:05:01 1996 Miles Bader <miles@gnu.ai.mit.edu> - - * ext2fs.c (main): Check error return from diskfs_init_diskfs. - -Tue Feb 6 14:49:40 1996 Miles Bader <miles@gnu.ai.mit.edu> - - * ext2fs.h (ext2_warning): Make a declaration, not a macro. - * msg.c (ext2_warning): Rename from _ext2_warning; don't take (or - print) a function argument any more. - - * dir.c (diskfs_get_directs): When BUFSIZ is 0, allocate enough - extra space over the directory size to account for the worst case - difference between the ext2 and canonical formats. - -Sat Feb 3 11:27:07 1996 Miles Bader <miles@gnu.ai.mit.edu> - - * hyper.c (get_hypermetadata, diskfs_readonly_changed): Use - ext2_warning to print warnings instead of error(). - * msg.c (_ext2_warning): Include `warning:' in message. - -Sat Feb 3 06:10:43 1996 Roland McGrath <roland@churchy.gnu.ai.mit.edu> - - * hyper.c: Fixed handling of the filesystem `clean bit'. - (ext2fs_clean): New boolean variable. - (get_hypermetadata): Set it iff the clean bit is set on entry. - If not clean, complain and force read-only. - (diskfs_set_hypermetadata): Set clean bit only if ext2fs_clean is set. - (diskfs_readonly_changed): Complain if going writable and clean - bit clear. - - * ext2fs.c: Include string.h for strerror decl. - -Tue Jan 30 22:25:19 1996 Miles Bader <miles@gnu.ai.mit.edu> - - * hyper.c (get_hypermetadata): Don't return any error value, just - panic if we can't read the superblock. - * ext2fs.c (main): Move warp_inode() inline. Make sure root inode - is really there. Don't check return value from get_hypermetadata. - (warp_inode): Function removed. - * ext2fs.h (get_hypermetadata): Returns void now. - -Tue Jan 30 17:04:41 1996 Roland McGrath <roland@churchy.gnu.ai.mit.edu> - - * pager.c (file_pager_read_page, file_pager_write_page): Check for - a page offset beyond the allocsize and return EIO. - -Wed Jan 17 15:11:55 1996 Miles Bader <miles@gnu.ai.mit.edu> - - * storeinfo.c (diskfs_S_file_get_storage_info): Calculate the - right value for *RUNS_LEN. - -Tue Jan 16 17:37:00 1996 Miles Bader <miles@gnu.ai.mit.edu> - - * pager.c (diskfs_file_update): Ext2fs doesn't require that the - last block in the file always be allocated, so don't. - (diskfs_grow, pager_unlock_page): Don't set last_block_allocated. - * ext2fs.h (struct disknode): Get rid of last_block_allocated field. - * inode.c (read_disknode): Don't set last_block_allocated. - * truncate.c (diskfs_truncate): Likewise. - - * Makefile (LDFLAGS): Add -static. - -Mon Jan 15 10:25:19 1996 Miles Bader <miles@gnu.ai.mit.edu> - - * pager.c (pager_unlock_page, diskfs_grow): Leave things in a - slightly more consistent state when block allocation errors happen. - - * dir.c (diskfs_direnter): Don't include the terminating '\0' in - on-disk directory entry names. - - * inode.c (diskfs_node_norefs): When losing our in-core copy of an - inode, remember which indirect blocks still have to be written. - * pokel.c (pokel_inherit, pokel_finalize): New functions. - * ext2fs.h (pokel_inherit, pokel_finalize): New declarations. - - * dir.c (diskfs_lookup): Patch from ufs/dir.c: If we are returning - an error, then set the dirstat to be ignored by drop_dirstat. - -Sun Jan 14 13:17:33 1996 Miles Bader <miles@gnu.ai.mit.edu> - - * ialloc.c (diskfs_alloc_node): Set NP->istranslated to 0. - * inode.c (write_node): If NP isn't translated, force - DI->i_translator to 0. - - * getblk.c (inode_getblk, block_getblk): Set dn_set_mtime too. - -Sat Jan 6 11:57:26 1996 Roland McGrath <roland@churchy.gnu.ai.mit.edu> - - * pager.c (pager_bucket): Made global. - (create_disk_pager): Pass MAY_CACHE to disk_pager_setup. - * ext2fs.c (main): Don't map in disk image here; create_disk_pager - now does it. - -Fri Jan 5 16:57:54 1996 Roland McGrath <roland@churchy.gnu.ai.mit.edu> - - * ext2fs.h: Declare create_disk_pager. - -Thu Jan 4 18:46:40 1996 Roland McGrath <roland@churchy.gnu.ai.mit.edu> - - * ext2fs.h (disk_pager, disk_pager_port, disk_image, - create_disk_pager): Decls removed. - Include hurd/diskfs-pager.h instead. - - * pager.c (create_disk_pager): Use disk_pager_setup. - -Tue Nov 14 14:59:32 1995 Miles Bader <miles@gnu.ai.mit.edu> - - * dir.c (dirscanblock): Apply mib's changes to ufs/dir.c. - -Sat Nov 4 20:01:01 1995 Miles Bader <miles@gnu.ai.mit.edu> - - * storeinfo.c (diskfs_S_file_get_storage_info): Add FLAGS argument. - -Wed Nov 1 20:09:59 1995 Miles Bader <miles@gnu.ai.mit.edu> - - * ext2fs.c (main): Add FLAGS arg to diskfs_startup_diskfs call. - -Mon Oct 23 17:49:16 1995 Miles Bader <miles@gnu.ai.mit.edu> - - * inode.c (diskfs_get_translator, diskfs_set_translator): Only - support these if the filesystem's creator-os is `hurd'. - (read_disknode): Only check the i_translator field if the - filesystem's creator-os is `hurd'. - -Fri Oct 20 19:18:16 1995 Miles Bader <miles@gnu.ai.mit.edu> - - * ext2fs.h (MS_RDONLY, MS_NOSUID, MS_NODEV, MS_NOEXEC, - MS_SYNCHRONOUS, MS_REMOUNT, S_APPEND, S_IMMUTABLE, IS_APPEND, - IS_IMMUTABLE): Macros deleted. - -Thu Oct 19 19:15:15 1995 Miles Bader <miles@gnu.ai.mit.edu> - - * pager.c (create_disk_pager, diskfs_get_filemap, - pager_clear_user_data): Don't use the p field in a upi. - (diskfs_get_filemap): Update/initialize the max_prot field. - Add the prot arg. - - * ext2fs.h (struct user_pager_info): Add max_prot field, remove p. - * pager.c (drop_pager_softrefs): Declare PAGER, not UPI. - (enable_caching): The disk node is upi->node, not upi->np. - (diskfs_enable_pagers): Function removed. - * inode.c (read_disknode): Add DN and OFFSET variables. Use - log2_block_size to mask instead of doing a modulo with block_size. - * hyper.c (diskfs_readonly_changed): Typo. - (allocate_mod_map): Declare ERR; OLD_MOD_MAP_SIZE --> MOD_MAP_SIZE. - * dir.c (diskfs_lookup, diskfs_dirempty): Give diskfs_get_filemap - a protection arg. - * truncate.c (force_delayed_copies): Ditto. - -Wed Oct 18 21:00:28 1995 Miles Bader <miles@gnu.ai.mit.edu> - - * ext2fs.h (struct disknode): Replace fileinfo field with pager. - * inode.c (diskfs_node_norefs, iget): Use pager field, not fileinfo. - * pager.c (diskfs_get_filemap_pager_struct, flush_node_pager, - diskfs_file_update, pager_clear_user_data, drop_pager_softrefs): Ditto. - * truncate.c (enable_delayed_copies, force_delayed_copies): Ditto. - - * ext2fs.c (main): Always include VM_PROT_WRITE in max prot. - * hyper.c (diskfs_readonly_changed): Change the protection of - DISK_IMAGE to reflect the new state. - * pager.c (diskfs_enable_pagers): New function. - -Tue Oct 17 21:16:04 1995 Miles Bader <miles@gnu.ai.mit.edu> - - * pokel.c (_pokel_exec, pokel_flush): New functions. - (pokel_sync): Use _pokel_exec to do the work. - * pager.c (flush_node_pager): New function. - * ext2fs.h (pokel_flush, flush_node_pager): New declarations. - - * hyper.c (allocate_mod_map): New function (from get_hypermetadata). - (zeroblock, modified_global_blocks): Define (were common). - (get_hypermetadata): Deallocate ZEROBLOCK if necessary. Use - allocate_mod_map to allocate MODIFIED_GLOBAL_BLOCKS. - (diskfs_readonly_changed): New function. - * main.c (main): Move stuff into get_hypermetadata. - Writable init code moved to diskfs_readonly_changed. - - * inode.c (diskfs_node_reload): New function. - (iget, read_disknode): Code to set allocsize and the last_* fields - moved from iget to read_disknode. - - * ext2fs.h (disk_pager): Type changed to struct pager. - (sync_global_ptr): Use DISK_PAGER, not DISK_PAGER->p. - * pager.c (create_disk_pager): Store the actual pager into DISK_PAGER. - * ext2fs.c (main): Use DISK_PAGER directly, not ->p. - * inode.c (iget): Ditto. - * pager.c (diskfs_shutdown_pager, diskfs_sync_everything): Ditto. - -Mon Oct 16 15:23:25 1995 Miles Bader <miles@gnu.ai.mit.edu> - - * inode.c (diskfs_lost_hardrefs): #ifdef'd out contents removed. - -Fri Oct 13 17:50:23 1995 Miles Bader <miles@gnu.ai.mit.edu> - - * ext2fs.c (main): Use argp for parsing. - (usage, USAGE, SHORT_OPTS, long_opts, console_stdio): Removed - -Thu Oct 12 18:16:00 1995 Miles Bader <miles@gnu.ai.mit.edu> - - * hyper.c (get_hypermetadata): Use diskfs device functions & - variables instead of our own. - * ext2fs.h (device_arg, device_name, device_port, device_start, - device_size, device_block_size): Declarations removed. - - * ext2fs.c (printf, _ext2_error, _ext2_panic, _ext2_warning): - Functions moved to msg.c - * msg.c: New file. - (printf, _ext2_error, _ext2_panic, _ext2_warning): Funcs from ext2fs.c. - * Makefile (SRCS): Remove devio.c, add msg.c. - -Sat Oct 7 20:47:19 1995 Miles Bader <miles@gnu.ai.mit.edu> - - * storeinfo.c (diskfs_S_file_get_storage_info): run_elem_t --> off_t. - - * ext2fs.c (diskfs_init_completed): Func deleted (now in libdiskfs). - -Fri Oct 6 17:24:57 1995 Miles Bader <miles@gnu.ai.mit.edu> - - * storeinfo.c (diskfs_S_file_get_storage_info): Change type of - ADDRESSES to off_t **, and add BLOCK_SIZE parameter. - -Wed Oct 4 20:02:34 1995 Miles Bader <miles@gnu.ai.mit.edu> - - * inode.c (diskfs_set_statfs): fsys_stb_bsize -> fsys_stb_iosize. - fsys_stb_fsize -> fsys_stb_bsize. - -Wed Sep 27 20:07:53 1995 Miles Bader <miles@gnu.ai.mit.edu> - - * ext2fs.c (main): Use diskfs routines to open the device. - Support both file and mach devices. Move the parse function here. - (parse_opt): Move into main (as a nested function). - * ext2fs.h (device_arg, device_start): New declarations. - * devio.c (dev_read_sync, dev_write_sync): Offset the address to - which we're doing i/o with DEVICE_START. - -Tue Sep 26 18:39:58 1995 Miles Bader <miles@gnu.ai.mit.edu> - - * storeinfo.c (diskfs_S_file_get_storage_info): New function. - * Makefile (SRCS): Add storeinfo.c. - -Fri Sep 15 14:21:18 1995 Miles Bader <miles@churchy.gnu.ai.mit.edu> - - * truncate.c (trunc_indirect): Only record an indirect block as - being modified when it actually is. - - * truncate.c (diskfs_truncate): Use the new truncate functions. - (poke_pages): Gratuitous cosmetic changes. - (trunc_direct): Rewritten, new args. - (trunc_indirect): Rewritten, new args, now more general to support - all the various indirection levels. - (trunc_triple_indirect, trunc_double_indirect, - trunc_single_indirect): New functions. - (struct free_block_run): New structure. - (free_block_run_finish, free_block_run_free_ptr, - free_block_run_add, free_block_run_init, _free_block_run_flush): - New functions. - (trunc_dindirect, trunc_tindirect): Functions deleted. - (DIRECT_BLOCK, INDIRECT_BLOCK, DINDIRECT_BLOCK, TINDIRECT_BLOCK): - Macros deleted. - - * getblk.c (block_getblk, ext2_getblk): u32 --> block_t. - * balloc.c (ext2_new_block): Ditto. - * hyper.c (get_hypermetadata): Ditto. - * pager.c (file_pager_write_page): Ditto. - -Wed Sep 13 12:30:23 1995 Michael I. Bushnell, p/BSG <mib@gnu.ai.mit.edu> - - * dir.c (diskfs_lookup): Don't attempt to lock NP if NPP is not - set. Don't even set NP if NPP is not set; use INUM as "lookup - succeeded flag" instead. Lookups for REMOVE and RENAME now *must* - set NPP. - -Tue Sep 12 11:03:19 1995 Miles Bader <miles@churchy.gnu.ai.mit.edu> - - * pokel.c (pokel_init): Initialize the free_pokes field. - (pokel_add): Assert that this malloc should succeed. - (pokel_sync): Don't hold POKEL's spin lock while syncing. - - * ialloc.c (diskfs_alloc_node): Check for a non-zero ALLOCSIZE. - -Tue Sep 5 16:59:40 1995 Miles Bader <miles@churchy.gnu.ai.mit.edu> - - * pager.c (diskfs_pager_users): Ignore the disk pager when seeing - if there are any active pagers. - (diskfs_shutdown_pager): shutdown_one gets passed a pager, not a upi. - (diskfs_sync_everything): sync_one gets passed a pager, not a upi. - -Sun Sep 3 17:28:13 1995 Miles Bader <miles@churchy.gnu.ai.mit.edu> - - * ext2fs.c (thread_cancel): Removed. - -Fri Aug 25 14:37:32 1995 Miles Bader <miles@churchy.gnu.ai.mit.edu> - - * Makefile (ext2fs): Put libports in the right place in the - linking order. - -Thu Aug 24 10:34:15 1995 Miles Bader <miles@churchy.gnu.ai.mit.edu> - - * Makefile (ext2fs): Put all dependencies here. - (HURDLIBS): Removed. - -Tue Aug 22 19:39:06 1995 Miles Bader <miles@churchy.gnu.ai.mit.edu> - - * Makefile (HURDLIBS): Add libshouldbeinlibc. - Remove rules for error.o. - -Fri Jul 21 17:51:33 1995 Michael I Bushnell <mib@geech.gnu.ai.mit.edu> - - * pager.c (diskfs_get_filemap): Free initial reference created by - pager_create. - - * pager.c (diskfs_get_filemap): Avoid race with simultaneous - termination by looping until we win. - (pager_clear_user_data): Only clear UPI->np->dn->fileinfo if it - still points to us. - -Thu Jul 6 15:33:24 1995 Michael I Bushnell <mib@duality.gnu.ai.mit.edu> - - * Makefile: Removed dependencies that are now automatically - generated. - -Thu Jul 6 13:36:25 1995 Miles Bader <miles@geech.gnu.ai.mit.edu> - - * pager.c (diskfs_pager_users): New function. - -Tue Jun 27 13:08:33 1995 Michael I Bushnell <mib@duality.gnu.ai.mit.edu> - - * pager.c (pager_unlock_page): Declare BLOCK volatile. - -Sat Jun 24 17:59:36 1995 Miles Bader <miles@churchy.gnu.ai.mit.edu> - - * Makefile (HURDLIBS): Add libihash. - - * ext2fs.c (thread_cancel): Dummy function. - - * dir.c (diskfs_direnter): Move assignment out of test. - (diskfs_get_directs): Fix print-format types. - * ialloc.c (diskfs_free_node): Fix print-format types. - - * pager.c (thread_function): New function. - (create_disk_pager): Make a new thread to service paging requests. - * ext2fs.c (main): Have the initial thread die when it's done, leaving - other thread to do the work. - - * pager.c (pager_bucket): New variable. - (pager_list_lock, file_pager_list): Variables deleted. - (create_disk_pager): Create pager_bucket. - (create_disk_pager, diskfs_get_filemap): Pass pager_bucket to - pager_create. - (pager_traverse): Function deleted. - (diskfs_get_filemap, pager_clear_user_data): Don't add/remove UPI - to/from the pager list, as there isn't any. - (diskfs_shutdown_pager, diskfs_sync_everything): Use - ports_bucket_iterate on pager_bucket to go through all the pagers, - instead of pager_traverse. - (diskfs_file_update, pager_traverse, allow_pager_softrefs, - drop_pager_softrefs): Change pager [un]ref calls to use the new ports - ref calls directly instead. - (pager_dropweak): New function (does nothing). - * ext2fs.h (struct user_pager_info): Remove the next & prevp fields. - - * truncate.c (force_delayed_copies, enable_delayed_copies): Change - pager [un]ref calls to use the new ports ref calls directly instead. - * inode.c (diskfs_lost_hardrefs): Ditto. - - * inode.c (diskfs_node_iterate): New function. - (write_all_disknodes): Re-implemented using diskfs_node_iterate. - -Wed Jun 14 16:19:49 1995 Michael I Bushnell <mib@duality.gnu.ai.mit.edu> - - * inode.c (diskfs_get_translator): Conform to new memory usage - semantic. - -Fri May 19 20:56:51 1995 Miles Bader <miles@churchy.gnu.ai.mit.edu> - - * ext2fs.c (main): Use options_parse & diskfs_standard_startup_options - to do command line options parsing. - (long_opts): Was `options'. Most things removed, as - they're now handled by libdiskfs. - (parse_opt): New routine to deal with our few meagre remaining - options in the approved options_parse manner. - -Mon May 15 15:55:49 1995 Miles Bader <miles@churchy.gnu.ai.mit.edu> - - * ext2fs.c (main, usage, options): Add --writable & --nosync options. - -Sat May 13 20:04:55 1995 Miles Bader <miles@duality.gnu.ai.mit.edu> - - * inode.c (diskfs_set_statfs): Set st->fsys_stb_bsize, not _fsize, - to the block size. - -Sat May 13 05:02:59 1995 Roland McGrath <roland@churchy.gnu.ai.mit.edu> - - * Makefile (OBJS): Remove exec_server_image.o. - (exec_server_image.o): Rule removed. - -Fri May 12 15:23:02 1995 Miles Bader <miles@churchy.gnu.ai.mit.edu> - - * ext2fs.c (main): Add an optional argument to the --sync option - that lets the user specify an initial sync interval. - -Thu May 11 13:30:06 1995 Miles Bader <miles@churchy.gnu.ai.mit.edu> - - * pager.c (pager_unlock_page): Give an explanation of why the file - system will shortly crash. - - * balloc.c (ext2_free_blocks, ext2_new_block): Get rid of the - CHECK_STRICT variable, and just always do the tests it controlled. - * ext2fs.h: Get rid of the CHECK_STRICT variable. - - * ext2fs.h (ext2_error, ext2_warning, ext2_panic, all callers changed): - Make these into macros that automagically supply the caller's - function name, and rename the original functions (which these - macros call) to have an underline prefix. - * ext2fs.c (ext2_error, ext2_warning, ext2_panic): Rename to add - the underline prefix. Also rearrange a bit to hold the lock - around the use of the global message buffer. - - * ext2fs.c (main): Enable the bootstrap code. - - * inode.c (read_disknode): Make st_blksize larger: 2 * pagesize. - -Wed May 10 14:03:34 1995 Miles Bader <miles@churchy.gnu.ai.mit.edu> - - * getblk.c (block_getblk, inode_getblk): Return ENOSPC instead of - EIO when we can't allocate a new block. - - * bitmap.c (find_next_zero_bit): Fix stupid typos (present in the - original linux source I copied this function from!) which were - causing occasional garbage results. - -Tue May 9 18:08:41 1995 Miles Bader <miles@churchy.gnu.ai.mit.edu> - - * ext2fs.h (DONT_CACHE_MEMORY_OBJECTS): Don't define this any - more, as the bugs we were using it to catch are supposedly gone :-| diff --git a/ext2fs/Makefile b/ext2fs/Makefile index 0c4d3467..e83aab26 100644 --- a/ext2fs/Makefile +++ b/ext2fs/Makefile @@ -1,6 +1,6 @@ # Makefile for ext2fs # -# Copyright (C) 1994, 1995, 1996 Free Software Foundation, Inc. +# Copyright (C) 1994,95,96,99,2000,02 Free Software Foundation, Inc. # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License as @@ -17,17 +17,15 @@ # Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. dir := ext2fs -makemode := servers +makemode := server -targets = ext2fs ext2fs.static -SRCS = balloc.c bitmap.c dir.c ext2fs.c getblk.c hyper.c ialloc.c \ - inode.c pager.c pokel.c truncate.c storeinfo.c msg.c +target = ext2fs +SRCS = balloc.c dir.c ext2fs.c getblk.c hyper.c ialloc.c \ + inode.c pager.c pokel.c truncate.c storeinfo.c msg.c xinl.c OBJS = $(SRCS:.c=.o) -LCLHDRS = ext2fs.h ext2_fs.h ext2_fs_i.h - -ext2fs.static-LDFLAGS += -static +LCLHDRS = ext2fs.h ext2_fs.h ext2_fs_i.h bitmap.c +HURDLIBS = diskfs pager iohelp fshelp store threads ports ihash shouldbeinlibc include ../Makeconf -ext2fs ext2fs.static: $(OBJS) ../libdiskfs/libdiskfs.a ../libpager/libpager.a ../libiohelp/libiohelp.a ../libfshelp/libfshelp.a ../libports/libports.a ../libthreads/libthreads.a ../libihash/libihash.a ../libshouldbeinlibc/libshouldbeinlibc.a - +ext2fs.static: $(boot-store-types:%=../libstore/libstore_%.a) diff --git a/ext2fs/balloc.c b/ext2fs/balloc.c index 9c47b742..7333123c 100644 --- a/ext2fs/balloc.c +++ b/ext2fs/balloc.c @@ -1,8 +1,8 @@ /* Block allocation routines - Copyright (C) 1995 Free Software Foundation, Inc. + Copyright (C) 1995,99,2000 Free Software Foundation, Inc. - Converted to work under the hurd by Miles Bader <miles@gnu.ai.mit.edu> + Converted to work under the hurd by Miles Bader <miles@gnu.org> This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as @@ -42,10 +42,19 @@ #include <string.h> #include "ext2fs.h" +#include "bitmap.c" + +/* Returns a pointer to the first occurrence of CH in the buffer BUF of len + LEN, or BUF + LEN if CH doesn't occur. */ +static inline void * +memscan (void *buf, unsigned char ch, size_t len) +{ + return memchr (buf, ch, len) ?: buf + len; +} #define in_range(b, first, len) ((b) >= (first) && (b) <= (first) + (len) - 1) -void +void ext2_free_blocks (block_t block, unsigned long count) { char *bh; @@ -60,44 +69,57 @@ ext2_free_blocks (block_t block, unsigned long count) (block + count) > sblock->s_blocks_count) { ext2_error ("freeing blocks not in datazone - " - "block = %lu, count = %lu", block, count); + "block = %u, count = %lu", block, count); spin_unlock (&global_lock); return; } - ext2_debug ("freeing block %lu[%lu]", block, count); + ext2_debug ("freeing block %u[%lu]", block, count); - block_group = (block - sblock->s_first_data_block) / - sblock->s_blocks_per_group; - bit = (block - sblock->s_first_data_block) % sblock->s_blocks_per_group; - if (bit + count > sblock->s_blocks_per_group) - ext2_panic ("freeing blocks across group boundary - " - "block = %lu, count = %lu", - block, count); - gdp = group_desc (block_group); - bh = bptr (gdp->bg_block_bitmap); + do + { + unsigned long int gcount = count; + + block_group = ((block - sblock->s_first_data_block) + / sblock->s_blocks_per_group); + bit = (block - sblock->s_first_data_block) % sblock->s_blocks_per_group; + if (bit + count > sblock->s_blocks_per_group) + { + unsigned long overflow = bit + count - sblock->s_blocks_per_group; + gcount -= overflow; + ext2_debug ("freeing blocks across group boundary - " + "block = %u, count = %lu", + block, count); + } + gdp = group_desc (block_group); + bh = bptr (gdp->bg_block_bitmap); - if (in_range (gdp->bg_block_bitmap, block, count) || - in_range (gdp->bg_inode_bitmap, block, count) || - in_range (block, gdp->bg_inode_table, itb_per_group) || - in_range (block + count - 1, gdp->bg_inode_table, itb_per_group)) - ext2_panic ("freeing blocks in system zones - " - "block = %lu, count = %lu", - block, count); + if (in_range (gdp->bg_block_bitmap, block, gcount) || + in_range (gdp->bg_inode_bitmap, block, gcount) || + in_range (block, gdp->bg_inode_table, itb_per_group) || + in_range (block + gcount - 1, gdp->bg_inode_table, itb_per_group)) + ext2_panic ("freeing blocks in system zones - " + "block = %u, count = %lu", + block, count); - for (i = 0; i < count; i++) - { - if (!clear_bit (bit + i, bh)) - ext2_warning ("bit already cleared for block %lu", block + i); - else + for (i = 0; i < gcount; i++) { - gdp->bg_free_blocks_count++; - sblock->s_free_blocks_count++; + if (!clear_bit (bit + i, bh)) + ext2_warning ("bit already cleared for block %lu", block + i); + else + { + gdp->bg_free_blocks_count++; + sblock->s_free_blocks_count++; + } } - } - record_global_poke (bh); - record_global_poke (gdp); + record_global_poke (bh); + record_global_poke (gdp); + + block += gcount; + count -= gcount; + } while (count > 0); + sblock_dirty = 1; spin_unlock (&global_lock); @@ -108,12 +130,14 @@ ext2_free_blocks (block_t block, unsigned long count) /* * ext2_new_block uses a goal block to assist allocation. If the goal is * free, or there is a free block within 32 blocks of the goal, that block - * is allocated. Otherwise a forward search is made for a free block; within + * is allocated. Otherwise a forward search is made for a free block; within * each block group the search first looks for an entire free byte in the block * bitmap, and then for any free bit if that fails. */ block_t -ext2_new_block (block_t goal, block_t *prealloc_count, block_t *prealloc_block) +ext2_new_block (block_t goal, + block_t prealloc_goal, + block_t *prealloc_count, block_t *prealloc_block) { char *bh; char *p, *r; @@ -138,7 +162,7 @@ ext2_new_block (block_t goal, block_t *prealloc_count, block_t *prealloc_block) } #endif - ext2_debug ("goal=%lu", goal); + ext2_debug ("goal=%u", goal); repeat: /* @@ -170,7 +194,7 @@ repeat: if (j) { /* - * The goal was occupied; search forward for a free + * The goal was occupied; search forward for a free * block within the next 32 blocks */ lmap = ((((unsigned long *) bh)[j >> 5]) >> @@ -198,7 +222,7 @@ repeat: * of the goal: do a search forward through the block groups, * searching in each group first for an entire free byte in * the bitmap and then for any free bit. - * + * * Search first in the remainder of the current group; then, * cyclicly search through the rest of the groups. */ @@ -223,7 +247,7 @@ repeat: ext2_debug ("bit not found in block group %d", i); /* - * Now search the rest of the groups. We assume that + * Now search the rest of the groups. We assume that * i and gdp correctly point to the last group visited. */ for (k = 0; k < groups_count; k++) @@ -256,7 +280,7 @@ repeat: } search_back: - /* + /* * We have succeeded in finding a free byte in the block * bitmap. Now search backwards up to 7 bits to find the * start of this group of free blocks. @@ -296,12 +320,12 @@ got_block: * Do block preallocation now if required. */ #ifdef EXT2_PREALLOCATE - if (prealloc_block) + if (prealloc_goal) { *prealloc_count = 0; *prealloc_block = tmp + 1; for (k = 1; - k < 8 && (j + k) < sblock->s_blocks_per_group; k++) + k < prealloc_goal && (j + k) < sblock->s_blocks_per_group; k++) { if (set_bit (j + k, bh)) break; @@ -317,7 +341,7 @@ got_block: } gdp->bg_free_blocks_count -= *prealloc_count; sblock->s_free_blocks_count -= *prealloc_count; - ext2_debug ("preallocated a further %lu bits", *prealloc_count); + ext2_debug ("preallocated a further %u bits", *prealloc_count); } #endif @@ -348,7 +372,7 @@ got_block: return j; } -unsigned long +unsigned long ext2_count_free_blocks () { #ifdef EXT2FS_DEBUG @@ -370,7 +394,7 @@ ext2_count_free_blocks () i, gdp->bg_free_blocks_count, x); bitmap_count += x; } - printf ("ext2_count_free_blocks: stored = %lu, computed = %lu, %lu", + printf ("ext2_count_free_blocks: stored = %u, computed = %lu, %lu", sblock->s_free_blocks_count, desc_count, bitmap_count); spin_unlock (&global_lock); return bitmap_count; @@ -379,14 +403,14 @@ ext2_count_free_blocks () #endif } -static inline int +static inline int block_in_use (block_t block, unsigned char *map) { return test_bit ((block - sblock->s_first_data_block) % sblock->s_blocks_per_group, map); } -void +void ext2_check_blocks_bitmap () { char *bh; @@ -405,16 +429,41 @@ ext2_check_blocks_bitmap () for (i = 0; i < groups_count; i++) { + inline int test_root (int a, int b) + { + if (a == 0) + return 1; + while (1) + { + if (a == 1) + return 1; + if (a % b) + return 0; + a = a / b; + } + } + inline int ext2_group_sparse (int group) + { + return (test_root (group, 3) || test_root (group, 5) + || test_root (group, 7)); + } + gdp = group_desc (i); desc_count += gdp->bg_free_blocks_count; bh = bptr (gdp->bg_block_bitmap); - if (!test_bit (0, bh)) - ext2_error ("superblock in group %d is marked free", i); + if (!EXT2_HAS_RO_COMPAT_FEATURE (sblock, + EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER) + || ext2_group_sparse (i)) + { + if (!test_bit (0, bh)) + ext2_error ("superblock in group %d is marked free", i); - for (j = 0; j < desc_blocks; j++) - if (!test_bit (j + 1, bh)) - ext2_error ("descriptor block #%d in group %d is marked free", j, i); + for (j = 0; j < desc_blocks; j++) + if (!test_bit (j + 1, bh)) + ext2_error ("descriptor block #%d in group %d is marked free", + j, i); + } if (!block_in_use (gdp->bg_block_bitmap, bh)) ext2_error ("block bitmap for group %d is marked free", i); diff --git a/ext2fs/bitmap.c b/ext2fs/bitmap.c index a983cab9..e512d011 100644 --- a/ext2fs/bitmap.c +++ b/ext2fs/bitmap.c @@ -18,6 +18,8 @@ along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ +#define ffz(word) (ffs (~(unsigned int) (word)) - 1) + /* * linux/fs/ext2/bitmap.c (&c) * @@ -29,12 +31,13 @@ static int nibblemap[] = {4, 3, 3, 2, 3, 2, 2, 1, 3, 2, 2, 1, 2, 1, 1, 0}; +static inline unsigned long count_free (char * map, unsigned int numchars) { unsigned int i; unsigned long sum = 0; - - if (!map) + + if (!map) return (0); for (i = 0; i < numchars; i++) sum += nibblemap[map[i] & 0xf] + @@ -44,31 +47,6 @@ unsigned long count_free (char * map, unsigned int numchars) /* ---------------------------------------------------------------- */ -static int ffz_nibble_map[] = {0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 4}; - -inline unsigned long ffz(unsigned long word) -{ - int offset = 0; - if ((word & 0xFFFF) == 0xFFFF) - { - word >>= 16; - offset += 16; - } - if ((word & 0xFF) == 0xFF) - { - word >>= 8; - offset += 8; - } - if ((word & 0xF) == 0xF) - { - word >>= 4; - offset += 4; - } - return ffz_nibble_map[word & 0xF] + offset; -} - -/* ---------------------------------------------------------------- */ - /* * Copyright 1994, David S. Miller (davem@caip.rutgers.edu). */ @@ -78,7 +56,7 @@ inline unsigned long ffz(unsigned long word) * on Linus's ALPHA routines, which are pretty portable BTW. */ -inline unsigned long +static inline unsigned long find_next_zero_bit(void *addr, unsigned long size, unsigned long offset) { unsigned long *p = ((unsigned long *) addr) + (offset >> 5); @@ -89,7 +67,7 @@ find_next_zero_bit(void *addr, unsigned long size, unsigned long offset) return size; size -= result; offset &= 31UL; - if (offset) + if (offset) { tmp = *(p++); tmp |= ~0UL >> (32-offset); @@ -100,7 +78,7 @@ find_next_zero_bit(void *addr, unsigned long size, unsigned long offset) size -= 32; result += 32; } - while (size & ~31UL) + while (size & ~31UL) { if (~(tmp = *(p++))) goto found_middle; @@ -121,23 +99,8 @@ found_middle: * holds on the Sparc as it does for the ALPHA. */ -inline int +static inline int find_first_zero_bit(void *buf, unsigned len) { return find_next_zero_bit(buf, len, 0); } - -/* ---------------------------------------------------------------- */ - -/* Returns a pointer to the first occurence of CH in the buffer BUF of len - LEN, or BUF + LEN if CH doesn't occur. */ -void *memscan(void *buf, unsigned char ch, unsigned len) -{ - unsigned char *p = (unsigned char *)buf; - while (len-- > 0) - if (*p == ch) - break; - else - p++; - return (void *)p; -} diff --git a/ext2fs/devio.c b/ext2fs/devio.c deleted file mode 100644 index 3e97fcb6..00000000 --- a/ext2fs/devio.c +++ /dev/null @@ -1,51 +0,0 @@ -/* Device input and output - Copyright (C) 1992, 1993, 1994, 1995 Free Software Foundation, Inc. - -This file is part of the GNU Hurd. - -The GNU Hurd is free software; you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 2, or (at your option) -any later version. - -The GNU Hurd is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with the GNU Hurd; see the file COPYING. If not, write to -the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ - -/* Written by Michael I. Bushnell. */ - -#include "ext2fs.h" -#include <device/device.h> -#include <device/device_request.h> - -/* Write disk block ADDR with DATA of LEN bytes, waiting for completion. */ -error_t -dev_write_sync (block_t addr, vm_address_t data, long len) -{ - int written; - assert (!diskfs_readonly); - if (device_write (device_port, 0, device_start + addr, - (io_buf_ptr_t) data, len, &written) - || written != len) - return EIO; - return 0; -} - -/* Read disk block ADDR; put the address of the data in DATA; read LEN - bytes. Always *DATA should be a full page no matter what. */ -error_t -dev_read_sync (block_t addr, vm_address_t *data, long len) -{ - u_int read; - if (device_read (device_port, 0, device_start + addr, len, - (io_buf_ptr_t *)data, &read) - || read != len) - return EIO; - return 0; -} - diff --git a/ext2fs/dir.c b/ext2fs/dir.c index 36fcfd58..d70dbf32 100644 --- a/ext2fs/dir.c +++ b/ext2fs/dir.c @@ -1,8 +1,9 @@ /* Directory management routines - Copyright (C) 1994, 1995, 1996 Free Software Foundation, Inc. + Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2007 + Free Software Foundation, Inc. - Converted for ext2fs by Miles Bader <miles@gnu.ai.mit.edu> + Converted for ext2fs by Miles Bader <miles@gnu.org> This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as @@ -72,39 +73,66 @@ struct dirstat /* Index of this directory block. */ int idx; - - /* For stat COMPRESS, this is the address (inside mapbuf) + + /* For stat COMPRESS, this is the address (inside mapbuf) of the first direct in the directory block to be compressed. */ /* For stat HERE_TIS, SHRINK, and TAKE, this is the entry referenced. */ - struct ext2_dir_entry *entry; + struct ext2_dir_entry_2 *entry; /* For stat HERE_TIS, type REMOVE, this is the address of the immediately previous direct in this directory block, or zero if this is the first. */ - struct ext2_dir_entry *preventry; + struct ext2_dir_entry_2 *preventry; /* For stat COMPRESS, this is the number of bytes needed to be copied in order to undertake the compression. */ size_t nbytes; }; -size_t diskfs_dirstat_size = sizeof (struct dirstat); +const size_t diskfs_dirstat_size = sizeof (struct dirstat); /* Initialize DS such that diskfs_drop_dirstat will ignore it. */ -void +void diskfs_null_dirstat (struct dirstat *ds) { ds->type = LOOKUP; } -static error_t -dirscanblock (vm_address_t blockoff, struct node *dp, int idx, char *name, - int namelen, enum lookup_type type, struct dirstat *ds, - ino_t *inum); +static error_t +dirscanblock (vm_address_t blockoff, struct node *dp, int idx, + const char *name, int namelen, enum lookup_type type, + struct dirstat *ds, ino_t *inum); + +#if 0 /* XXX unused for now */ +static const unsigned char ext2_file_type[EXT2_FT_MAX] = +{ + [EXT2_FT_UNKNOWN] = DT_UNKNOWN, + [EXT2_FT_REG_FILE] = DT_REG, + [EXT2_FT_DIR] = DT_DIR, + [EXT2_FT_CHRDEV] = DT_CHR, + [EXT2_FT_BLKDEV] = DT_BLK, + [EXT2_FT_FIFO] = DT_FIFO, + [EXT2_FT_SOCK] = DT_SOCK, + [EXT2_FT_SYMLINK] = DT_LNK, +}; + +static const unsigned char file_type_ext2[] = +{ + [DT_UNKNOWN] = EXT2_FT_UNKNOWN, + [DT_REG] = EXT2_FT_REG_FILE, + [DT_DIR] = EXT2_FT_DIR, + [DT_CHR] = EXT2_FT_CHRDEV, + [DT_BLK] = EXT2_FT_BLKDEV, + [DT_FIFO] = EXT2_FT_FIFO, + [DT_SOCK] = EXT2_FT_SOCK, + [DT_LNK] = EXT2_FT_SYMLINK, +}; +#endif + /* Implement the diskfs_lookup from the diskfs library. See <hurd/diskfs.h> for the interface specification. */ error_t -diskfs_lookup_hard (struct node *dp, char *name, enum lookup_type type, +diskfs_lookup_hard (struct node *dp, const char *name, enum lookup_type type, struct node **npp, struct dirstat *ds, struct protid *cred) { error_t err; @@ -119,8 +147,9 @@ diskfs_lookup_hard (struct node *dp, char *name, enum lookup_type type, vm_address_t buf = 0; vm_size_t buflen = 0; int blockaddr; - int idx; - + int idx, lastidx; + int looped; + if ((type == REMOVE) || (type == RENAME)) assert (npp); @@ -129,12 +158,16 @@ diskfs_lookup_hard (struct node *dp, char *name, enum lookup_type type, spec_dotdot = type & SPEC_DOTDOT; type &= ~SPEC_DOTDOT; - + namelen = strlen (name); if (namelen > EXT2_NAME_LEN) - return ENAMETOOLONG; - + { + if (ds) + diskfs_null_dirstat (ds); + return ENAMETOOLONG; + } + try_again: if (ds) { @@ -144,7 +177,7 @@ diskfs_lookup_hard (struct node *dp, char *name, enum lookup_type type, } if (buf) { - vm_deallocate (mach_task_self (), buf, buflen); + munmap ((caddr_t) buf, buflen); buf = 0; } if (ds && (type == CREATE || type == RENAME)) @@ -152,6 +185,10 @@ diskfs_lookup_hard (struct node *dp, char *name, enum lookup_type type, /* Map in the directory contents. */ memobj = diskfs_get_filemap (dp, prot); + + if (memobj == MACH_PORT_NULL) + return errno; + buf = 0; /* We allow extra space in case we have to do an EXTEND. */ buflen = round_page (dp->dn_stat.st_size + DIRBLKSIZ); @@ -160,26 +197,45 @@ diskfs_lookup_hard (struct node *dp, char *name, enum lookup_type type, mach_port_deallocate (mach_task_self (), memobj); inum = 0; - - if (!diskfs_check_readonly ()) - dp->dn_set_atime = 1; - - for (blockaddr = buf, idx = 0; - blockaddr - buf < dp->dn_stat.st_size; - blockaddr += DIRBLKSIZ, idx++) + + diskfs_set_node_atime (dp); + + /* Start the lookup at DP->dn->dir_idx. */ + idx = dp->dn->dir_idx; + if (idx * DIRBLKSIZ > dp->dn_stat.st_size) + idx = 0; /* just in case */ + blockaddr = buf + idx * DIRBLKSIZ; + looped = (idx == 0); + lastidx = idx; + if (lastidx == 0) + lastidx = dp->dn_stat.st_size / DIRBLKSIZ; + + while (!looped || idx < lastidx) { err = dirscanblock (blockaddr, dp, idx, name, namelen, type, ds, &inum); if (!err) - break; + { + dp->dn->dir_idx = idx; + break; + } if (err != ENOENT) { - vm_deallocate (mach_task_self (), buf, buflen); + munmap ((caddr_t) buf, buflen); return err; } + + blockaddr += DIRBLKSIZ; + idx++; + if (blockaddr - buf >= dp->dn_stat.st_size && !looped) + { + /* We've gotten to the end; start back at the beginning */ + looped = 1; + blockaddr = buf; + idx = 0; + } } - if (!diskfs_check_readonly ()) - dp->dn_set_atime = 1; + diskfs_set_node_atime (dp); if (diskfs_synchronous) diskfs_node_update (dp, 1); @@ -203,7 +259,7 @@ diskfs_lookup_hard (struct node *dp, char *name, enum lookup_type type, goto out; } } - + /* We are looking up .. */ /* Check to see if this is the root of the filesystem. */ else if (dp->cache_id == 2) @@ -211,7 +267,7 @@ diskfs_lookup_hard (struct node *dp, char *name, enum lookup_type type, err = EAGAIN; goto out; } - + /* We can't just do diskfs_cached_lookup, because we would then deadlock. So we do this. Ick. */ else if (retry_dotdot) @@ -245,11 +301,11 @@ diskfs_lookup_hard (struct node *dp, char *name, enum lookup_type type, retry_dotdot = inum; goto try_again; } - + /* Here below are the spec dotdot cases. */ else if (type == RENAME || type == REMOVE) np = ifind (inum); - + else if (type == LOOKUP) { diskfs_nput (dp); @@ -260,13 +316,13 @@ diskfs_lookup_hard (struct node *dp, char *name, enum lookup_type type, else assert (0); } - + if ((type == CREATE || type == RENAME) && !inum && ds && ds->stat == LOOKING) { /* We didn't find any room, so mark ds to extend the dir */ ds->type = CREATE; ds->stat = EXTEND; - ds->idx = idx; + ds->idx = dp->dn_stat.st_size / DIRBLKSIZ; } /* Return to the user; if we can't, release the reference @@ -277,7 +333,7 @@ diskfs_lookup_hard (struct node *dp, char *name, enum lookup_type type, || !ds || ds->type == LOOKUP) { - vm_deallocate (mach_task_self (), buf, buflen); + munmap ((caddr_t) buf, buflen); if (ds) ds->type = LOOKUP; /* set to be ignored by drop_dirstat */ } @@ -286,7 +342,7 @@ diskfs_lookup_hard (struct node *dp, char *name, enum lookup_type type, ds->mapbuf = buf; ds->mapextent = buflen; } - + if (np) { assert (npp); @@ -320,20 +376,20 @@ diskfs_lookup_hard (struct node *dp, char *name, enum lookup_type type, diskfs_lookup. If found, set *INUM to the inode number, else return ENOENT. */ static error_t -dirscanblock (vm_address_t blockaddr, struct node *dp, int idx, char *name, - int namelen, enum lookup_type type, +dirscanblock (vm_address_t blockaddr, struct node *dp, int idx, + const char *name, int namelen, enum lookup_type type, struct dirstat *ds, ino_t *inum) { int nfree = 0; int needed = 0; vm_address_t currentoff, prevoff; - struct ext2_dir_entry *entry = 0; + struct ext2_dir_entry_2 *entry = 0; int nentries = 0; size_t nbytes = 0; int looking = 0; int countcopies = 0; int consider_compress = 0; - + if (ds && (ds->stat == LOOKING || ds->stat == COMPRESS)) { @@ -346,8 +402,8 @@ dirscanblock (vm_address_t blockaddr, struct node *dp, int idx, char *name, currentoff < blockaddr + DIRBLKSIZ; prevoff = currentoff, currentoff += entry->rec_len) { - entry = (struct ext2_dir_entry *)currentoff; - + entry = (struct ext2_dir_entry_2 *)currentoff; + if (!entry->rec_len || entry->rec_len % EXT2_DIR_PAD || entry->name_len > EXT2_NAME_LEN @@ -355,16 +411,16 @@ dirscanblock (vm_address_t blockaddr, struct node *dp, int idx, char *name, || EXT2_DIR_REC_LEN (entry->name_len) > entry->rec_len || memchr (entry->name, '\0', entry->name_len)) { - ext2_warning ("bad directory entry: inode: %d offset: %ld", + ext2_warning ("bad directory entry: inode: %Ld offset: %zd", dp->cache_id, currentoff - blockaddr + idx * DIRBLKSIZ); return ENOENT; } - + if (looking || countcopies) { int thisfree; - + /* Count how much free space this entry has in it. */ if (entry->inode == 0) thisfree = entry->rec_len; @@ -376,9 +432,9 @@ dirscanblock (vm_address_t blockaddr, struct node *dp, int idx, char *name, number of bytes there too. */ if (countcopies && currentoff != blockaddr) nbytes += EXT2_DIR_REC_LEN (entry->name_len); - + if (ds->stat == COMPRESS && nbytes > ds->nbytes) - /* The previously found compress is better than + /* The previously found compress is better than this one, so don't bother counting any more. */ countcopies = 0; @@ -395,9 +451,9 @@ dirscanblock (vm_address_t blockaddr, struct node *dp, int idx, char *name, nfree += thisfree; if (nfree >= needed) consider_compress = 1; - } + } } - + if (entry->inode) nentries++; @@ -408,17 +464,17 @@ dirscanblock (vm_address_t blockaddr, struct node *dp, int idx, char *name, break; } - if (consider_compress + if (consider_compress && (ds->type == LOOKING || (ds->type == COMPRESS && ds->nbytes > nbytes))) { ds->type = CREATE; ds->stat = COMPRESS; - ds->entry = (struct ext2_dir_entry *) blockaddr; + ds->entry = (struct ext2_dir_entry_2 *) blockaddr; ds->idx = idx; ds->nbytes = nbytes; } - + if (currentoff >= blockaddr + DIRBLKSIZ) { int i; @@ -428,9 +484,9 @@ dirscanblock (vm_address_t blockaddr, struct node *dp, int idx, char *name, down how many entries there were. */ if (!dp->dn->dirents) { - dp->dn->dirents = malloc ((dp->dn_stat.st_size / DIRBLKSIZ + 1) - * sizeof (int)); - for (i = 0; i < dp->dn_stat.st_size/DIRBLKSIZ; i++) + dp->dn->dirents = malloc ((dp->dn_stat.st_size / DIRBLKSIZ) + * sizeof (int)); + for (i = 0; i < dp->dn_stat.st_size/DIRBLKSIZ; i++) dp->dn->dirents[i] = -1; } /* Make sure the count is correct if there is one now. */ @@ -440,7 +496,7 @@ dirscanblock (vm_address_t blockaddr, struct node *dp, int idx, char *name, return ENOENT; } - + /* We have found the required name. */ if (ds && type == CREATE) @@ -451,7 +507,7 @@ dirscanblock (vm_address_t blockaddr, struct node *dp, int idx, char *name, ds->stat = HERE_TIS; ds->entry = entry; ds->idx = idx; - ds->preventry = (struct ext2_dir_entry *) prevoff; + ds->preventry = (struct ext2_dir_entry_2 *) prevoff; } *inum = entry->inode; @@ -465,53 +521,49 @@ dirscanblock (vm_address_t blockaddr, struct node *dp, int idx, char *name, only be made if the directory has been held locked continuously since the preceding lookup call, and only if that call returned ENOENT. */ error_t -diskfs_direnter_hard (struct node *dp, char *name, struct node *np, +diskfs_direnter_hard (struct node *dp, const char *name, struct node *np, struct dirstat *ds, struct protid *cred) { - struct ext2_dir_entry *new; + struct ext2_dir_entry_2 *new; int namelen = strlen (name); int needed = EXT2_DIR_REC_LEN (namelen); int oldneeded; vm_address_t fromoff, tooff; - int totfreed; + int totfreed; error_t err; - off_t oldsize; + size_t oldsize = 0; assert (ds->type == CREATE); - + assert (!diskfs_readonly); dp->dn_set_mtime = 1; + /* Select a location for the new directory entry. Each branch of this + switch is responsible for setting NEW to point to the on-disk + directory entry being written, and setting NEW->rec_len appropriately. */ + switch (ds->stat) { case TAKE: /* We are supposed to consume this slot. */ assert (ds->entry->inode == 0 && ds->entry->rec_len >= needed); - - ds->entry->inode = np->cache_id; - ds->entry->name_len = namelen; - bcopy (name, ds->entry->name, namelen); + new = ds->entry; break; - + case SHRINK: /* We are supposed to take the extra space at the end of this slot. */ oldneeded = EXT2_DIR_REC_LEN (ds->entry->name_len); assert (ds->entry->rec_len - oldneeded >= needed); - - new = (struct ext2_dir_entry *) ((vm_address_t) ds->entry + oldneeded); - new->inode = np->cache_id; + new = (struct ext2_dir_entry_2 *) ((vm_address_t) ds->entry + oldneeded); + new->rec_len = ds->entry->rec_len - oldneeded; - new->name_len = namelen; - bcopy (name, new->name, namelen); - ds->entry->rec_len = oldneeded; - break; - + case COMPRESS: /* We are supposed to move all the entries to the front of the block, giving each the minimum @@ -521,15 +573,15 @@ diskfs_direnter_hard (struct node *dp, char *name, struct node *np, while (fromoff < (vm_address_t) ds->entry + DIRBLKSIZ) { - struct ext2_dir_entry *from = (struct ext2_dir_entry *)fromoff; - struct ext2_dir_entry *to = (struct ext2_dir_entry *) tooff; + struct ext2_dir_entry_2 *from = (struct ext2_dir_entry_2 *)fromoff; + struct ext2_dir_entry_2 *to = (struct ext2_dir_entry_2 *) tooff; int fromreclen = from->rec_len; if (from->inode != 0) { assert (fromoff >= tooff); - bcopy (from, to, fromreclen); + memmove (to, from, fromreclen); to->rec_len = EXT2_DIR_REC_LEN (to->name_len); tooff += to->rec_len; @@ -539,75 +591,105 @@ diskfs_direnter_hard (struct node *dp, char *name, struct node *np, totfreed = (vm_address_t) ds->entry + DIRBLKSIZ - tooff; assert (totfreed >= needed); - - new = (struct ext2_dir_entry *) tooff; - new->inode = np->cache_id; + + new = (struct ext2_dir_entry_2 *) tooff; new->rec_len = totfreed; - new->name_len = namelen; - bcopy (name, new->name, namelen); break; case EXTEND: /* Extend the file. */ assert (needed <= DIRBLKSIZ); - + oldsize = dp->dn_stat.st_size; + if ((off_t)(oldsize + DIRBLKSIZ) != (dp->dn_stat.st_size + DIRBLKSIZ)) + { + /* We can't possibly map the whole directory in. */ + munmap ((caddr_t) ds->mapbuf, ds->mapextent); + return EOVERFLOW; + } while (oldsize + DIRBLKSIZ > dp->allocsize) { err = diskfs_grow (dp, oldsize + DIRBLKSIZ, cred); if (err) { - vm_deallocate (mach_task_self (), ds->mapbuf, ds->mapextent); + munmap ((caddr_t) ds->mapbuf, ds->mapextent); return err; } } - new = (struct ext2_dir_entry *) (ds->mapbuf + oldsize); + new = (struct ext2_dir_entry_2 *) (ds->mapbuf + oldsize); dp->dn_stat.st_size = oldsize + DIRBLKSIZ; dp->dn_set_ctime = 1; - new->inode = np->cache_id; new->rec_len = DIRBLKSIZ; - new->name_len = namelen; - bcopy (name, new->name, namelen); break; - + default: - assert (0); + new = 0; + assert (! "impossible: bogus status field in dirstat"); } + /* NEW points to the directory entry being written, and its + rec_len field is already filled in. Now fill in the rest. */ + + new->inode = np->cache_id; +#if 0 + /* XXX We cannot enable this code because file types can change + (and conceivably quite often) with translator settings. + There is no way for the translator that determines the type of + the virtual node to cause all the directory entries linked to + its underlying inode to reflect the proper type. */ + new->file_type = (EXT2_HAS_INCOMPAT_FEATURE (sblock, + EXT2_FEATURE_INCOMPAT_FILETYPE) + ? file_type_ext2[IFTODT (np->dn_stat.st_mode & S_IFMT)] + : 0); +#else + new->file_type = 0; +#endif + new->name_len = namelen; + memcpy (new->name, name, namelen); + + /* Mark the directory inode has having been written. */ + dp->dn->info.i_flags &= ~EXT2_BTREE_FL; dp->dn_set_mtime = 1; - vm_deallocate (mach_task_self (), ds->mapbuf, ds->mapextent); - + munmap ((caddr_t) ds->mapbuf, ds->mapextent); + if (ds->stat != EXTEND) { - /* If we are keeping count of this block, then keep the count up + /* If we are keeping count of this block, then keep the count up to date. */ if (dp->dn->dirents && dp->dn->dirents[ds->idx] != -1) dp->dn->dirents[ds->idx]++; } else { + int i; /* It's cheap, so start a count here even if we aren't counting anything at all. */ if (dp->dn->dirents) { - dp->dn->dirents = realloc (dp->dn->dirents, - (ds->idx + 1) * sizeof (int)); + dp->dn->dirents = realloc (dp->dn->dirents, + (dp->dn_stat.st_size / DIRBLKSIZ + * sizeof (int))); + for (i = oldsize / DIRBLKSIZ; + i < dp->dn_stat.st_size / DIRBLKSIZ; + i++) + dp->dn->dirents[i] = -1; + dp->dn->dirents[ds->idx] = 1; } else { - int i; - dp->dn->dirents = malloc ((ds->idx + 1) * sizeof (int)); - for (i = 0; i < ds->idx; i++) + dp->dn->dirents = malloc (dp->dn_stat.st_size / DIRBLKSIZ + * sizeof (int)); + for (i = 0; i < dp->dn_stat.st_size / DIRBLKSIZ; i++) dp->dn->dirents[i] = -1; dp->dn->dirents[ds->idx] = 1; } } - + diskfs_file_update (dp, 1); return 0; @@ -616,17 +698,15 @@ diskfs_direnter_hard (struct node *dp, char *name, struct node *np, /* Following a lookup call for REMOVE, this removes the link from the directory. DP is the directory being changed and DS is the cached information returned from lookup. This call is only valid if the - directory has been locked continously since the call to lookup, and + directory has been locked continuously since the call to lookup, and only if that call succeeded. */ error_t diskfs_dirremove_hard (struct node *dp, struct dirstat *ds) { assert (ds->type == REMOVE); assert (ds->stat == HERE_TIS); - - assert (!diskfs_readonly); - dp->dn_set_mtime = 1; + assert (!diskfs_readonly); if (ds->preventry == 0) ds->entry->inode = 0; @@ -638,23 +718,24 @@ diskfs_dirremove_hard (struct node *dp, struct dirstat *ds) } dp->dn_set_mtime = 1; + dp->dn->info.i_flags &= ~EXT2_BTREE_FL; - vm_deallocate (mach_task_self (), ds->mapbuf, ds->mapextent); + munmap ((caddr_t) ds->mapbuf, ds->mapextent); /* If we are keeping count of this block, then keep the count up to date. */ if (dp->dn->dirents && dp->dn->dirents[ds->idx] != -1) dp->dn->dirents[ds->idx]--; - + diskfs_file_update (dp, 1); return 0; } - + /* Following a lookup call for RENAME, this changes the inode number - on a directory entry. DP is the directory being changed; NP is - the new node being linked in; DP is the cached information returned + on a directory entry. DP is the directory being changed; NP is + the new node being linked in; DP is the cached information returned by lookup. This call is only valid if the directory has been locked continuously since the call to lookup, and only if that call succeeded. */ @@ -663,14 +744,15 @@ diskfs_dirrewrite_hard (struct node *dp, struct node *np, struct dirstat *ds) { assert (ds->type == RENAME); assert (ds->stat == HERE_TIS); - + assert (!diskfs_readonly); ds->entry->inode = np->cache_id; dp->dn_set_mtime = 1; + dp->dn->info.i_flags &= ~EXT2_BTREE_FL; + + munmap ((caddr_t) ds->mapbuf, ds->mapextent); - vm_deallocate (mach_task_self (), ds->mapbuf, ds->mapextent); - diskfs_file_update (dp, 1); return 0; @@ -683,23 +765,26 @@ diskfs_dirempty (struct node *dp, struct protid *cred) { error_t err; vm_address_t buf = 0, curoff; - struct ext2_dir_entry *entry; + struct ext2_dir_entry_2 *entry; int hit = 0; /* Found something in the directory. */ memory_object_t memobj = diskfs_get_filemap (dp, VM_PROT_READ); - + + if (memobj == MACH_PORT_NULL) + /* XXX should reflect error properly. */ + return 0; + err = vm_map (mach_task_self (), &buf, dp->dn_stat.st_size, 0, 1, memobj, 0, 0, VM_PROT_READ, VM_PROT_READ, 0); mach_port_deallocate (mach_task_self (), memobj); assert (!err); - if (! diskfs_check_readonly ()) - dp->dn_set_atime = 1; + diskfs_set_node_atime (dp); - for (curoff = buf; + for (curoff = buf; !hit && curoff < buf + dp->dn_stat.st_size; curoff += entry->rec_len) { - entry = (struct ext2_dir_entry *) curoff; + entry = (struct ext2_dir_entry_2 *) curoff; if (entry->inode != 0 && (entry->name_len > 2 @@ -709,12 +794,11 @@ diskfs_dirempty (struct node *dp, struct protid *cred) hit = 1; } - if (! diskfs_check_readonly ()) - dp->dn_set_atime = 1; + diskfs_set_node_atime (dp); if (diskfs_synchronous) diskfs_node_update (dp, 1); - vm_deallocate (mach_task_self (), buf, dp->dn_stat.st_size); + munmap ((caddr_t) buf, dp->dn_stat.st_size); return !hit; } @@ -726,7 +810,7 @@ diskfs_drop_dirstat (struct node *dp, struct dirstat *ds) if (ds->type != LOOKUP) { assert (ds->mapbuf); - vm_deallocate (mach_task_self (), ds->mapbuf, ds->mapextent); + munmap ((caddr_t) ds->mapbuf, ds->mapextent); ds->type = LOOKUP; } return 0; @@ -739,29 +823,29 @@ diskfs_drop_dirstat (struct node *dp, struct dirstat *ds) static error_t count_dirents (struct node *dp, int nb, char *buf) { - int amt; + size_t amt; char *offinblk; - struct ext2_dir_entry *entry; + struct ext2_dir_entry_2 *entry; int count = 0; error_t err; assert (dp->dn->dirents); assert ((nb + 1) * DIRBLKSIZ <= dp->dn_stat.st_size); - + err = diskfs_node_rdwr (dp, buf, nb * DIRBLKSIZ, DIRBLKSIZ, 0, 0, &amt); if (err) return err; assert (amt == DIRBLKSIZ); - + for (offinblk = buf; offinblk < buf + DIRBLKSIZ; offinblk += entry->rec_len) { - entry = (struct ext2_dir_entry *) offinblk; + entry = (struct ext2_dir_entry_2 *) offinblk; if (entry->inode) count++; } - + assert (dp->dn->dirents[nb] == -1 || dp->dn->dirents[nb] == count); dp->dn->dirents[nb] = count; return 0; @@ -771,14 +855,14 @@ count_dirents (struct node *dp, int nb, char *buf) Must be a power of two. */ #define DIRENT_ALIGN 4 -/* Implement the disikfs_get_directs callback as described in +/* Implement the diskfs_get_directs callback as described in <hurd/diskfs.h>. */ error_t -diskfs_get_directs (struct node *dp, - int entry, +diskfs_get_directs (struct node *dp, + int entry, int nentries, char **data, - u_int *datacnt, + size_t *datacnt, vm_size_t bufsiz, int *amt) { @@ -791,11 +875,11 @@ diskfs_get_directs (struct node *dp, error_t err; int i; char *datap; - struct ext2_dir_entry *entryp; + struct ext2_dir_entry_2 *entryp; int allocsize; - int checklen; + size_t checklen; struct dirent *userp; - + nblks = dp->dn_stat.st_size/DIRBLKSIZ; if (!dp->dn->dirents) @@ -805,33 +889,6 @@ diskfs_get_directs (struct node *dp, dp->dn->dirents[i] = -1; } - /* Allocate enough space to hold the maximum we might return */ - if (!bufsiz || bufsiz > dp->dn_stat.st_size) - /* Allocate enough to return the entire directory. Since ext2's - directory format is different than the format used to return the - entries, we allocate enough to hold the on disk directory plus - whatever extra would be necessary in the worst-case. */ - { - /* The minimum size of an ext2fs directory entry. */ - size_t min_entry_size = EXT2_DIR_REC_LEN (0); - /* The minimum size of a returned dirent entry. The +1 is for '\0'. */ - size_t min_dirent_size = offsetof (struct dirent, d_name) + 1; - /* The maximum possible number of ext2fs dir entries in this dir. */ - size_t max_entries = dp->dn_stat.st_size / min_entry_size; - /* The maximum difference in size per directory entry. */ - size_t entry_extra = - DIRENT_ALIGN - + (min_dirent_size > min_entry_size - ? min_dirent_size - min_entry_size : 0); - - allocsize = round_page (dp->dn_stat.st_size + max_entries * entry_extra); - } - else - allocsize = round_page (bufsiz); - - if (allocsize > *datacnt) - vm_allocate (mach_task_self (), (vm_address_t *) data, allocsize, 1); - /* Scan through the entries to find ENTRY. If we encounter a -1 in the process then stop to fill it. When we run off the end, ENTRY is too big. */ @@ -852,17 +909,47 @@ diskfs_get_directs (struct node *dp, break; curentry += dp->dn->dirents[blkno]; - + bufvalid = 0; } - + if (blkno == nblks) { + /* We reached the end of the directory without seeing ENTRY. + This is treated as an EOF condition, meaning we return + success with empty results. */ *datacnt = 0; *amt = 0; return 0; } + /* Allocate enough space to hold the maximum we might return */ + if (!bufsiz || bufsiz > dp->dn_stat.st_size) + /* Allocate enough to return the entire directory. Since ext2's + directory format is different than the format used to return the + entries, we allocate enough to hold the on disk directory plus + whatever extra would be necessary in the worst-case. */ + { + /* The minimum size of an ext2fs directory entry. */ + size_t min_entry_size = EXT2_DIR_REC_LEN (0); + /* The minimum size of a returned dirent entry. The +1 is for '\0'. */ + size_t min_dirent_size = offsetof (struct dirent, d_name) + 1; + /* The maximum possible number of ext2fs dir entries in this dir. */ + size_t max_entries = dp->dn_stat.st_size / min_entry_size; + /* The maximum difference in size per directory entry. */ + size_t entry_extra = + DIRENT_ALIGN + + (min_dirent_size > min_entry_size + ? min_dirent_size - min_entry_size : 0); + + allocsize = round_page (dp->dn_stat.st_size + max_entries * entry_extra); + } + else + allocsize = round_page (bufsiz); + + if (allocsize > *datacnt) + *data = mmap (0, allocsize, PROT_READ|PROT_WRITE, MAP_ANON, 0, 0); + /* Set bufp appropriately */ bufp = buf; if (curentry != entry) @@ -878,13 +965,13 @@ diskfs_get_directs (struct node *dp, assert (checklen == DIRBLKSIZ); bufvalid = 1; } - for (i = 0, bufp = buf; - i < entry - curentry && bufp - buf < DIRBLKSIZ; - bufp += ((struct ext2_dir_entry *)bufp)->rec_len, i++) + for (i = 0, bufp = buf; + i < entry - curentry && bufp - buf < DIRBLKSIZ; + bufp += ((struct ext2_dir_entry_2 *)bufp)->rec_len, i++) ; /* Make sure we didn't run off the end. */ assert (bufp - buf < DIRBLKSIZ); - } + } i = 0; datap = *data; @@ -896,7 +983,7 @@ diskfs_get_directs (struct node *dp, { if (!bufvalid) { - err = diskfs_node_rdwr (dp, buf, blkno * DIRBLKSIZ, DIRBLKSIZ, + err = diskfs_node_rdwr (dp, buf, blkno * DIRBLKSIZ, DIRBLKSIZ, 0, 0, &checklen); if (err) return err; @@ -905,7 +992,7 @@ diskfs_get_directs (struct node *dp, bufp = buf; } - entryp = (struct ext2_dir_entry *)bufp; + entryp = (struct ext2_dir_entry_2 *)bufp; if (entryp->inode) { @@ -934,7 +1021,30 @@ diskfs_get_directs (struct node *dp, userp->d_fileno = entryp->inode; userp->d_reclen = rec_len; userp->d_namlen = name_len; - bcopy (entryp->name, userp->d_name, name_len); + +#if 0 + /* We don't bother to check the EXT2_FEATURE_INCOMPAT_FILETYPE + flag in the superblock, because in old filesystems the + file_type field is the high byte of the length field and is + always zero because names cannot be that long. */ + if (entryp->file_type < EXT2_FT_MAX) + userp->d_type = ext2_file_type[entryp->file_type]; + else + { + ext2_warning ("bad type %d in directory entry: " + "inode: %d offset: %d", + entryp->file_type, + dp->cache_id, + blkno * DIRBLKSIZ + bufp - buf); + userp->d_type = DT_UNKNOWN; + } +#else + /* XXX + For complex reasons it might not be correct to return + the filesystem's d_type value to the user. */ + userp->d_type = DT_UNKNOWN; +#endif + memcpy (userp->d_name, entryp->name, name_len); userp->d_name[name_len] = '\0'; datap += rec_len; @@ -943,7 +1053,7 @@ diskfs_get_directs (struct node *dp, if (entryp->rec_len == 0) { - ext2_warning ("zero length directory entry: inode: %d offset: %d", + ext2_warning ("zero length directory entry: inode: %Ld offset: %zd", dp->cache_id, blkno * DIRBLKSIZ + bufp - buf); return EIO; @@ -957,23 +1067,22 @@ diskfs_get_directs (struct node *dp, } else if (bufp - buf > DIRBLKSIZ) { - ext2_warning ("directory entry too long: inode: %d offset: %d", + ext2_warning ("directory entry too long: inode: %Ld offset: %zd", dp->cache_id, blkno * DIRBLKSIZ + bufp - buf - entryp->rec_len); return EIO; } } - + /* We've copied all we can. If we allocated our own array but didn't fill all of it, then free whatever memory we didn't use. */ if (allocsize > *datacnt) { if (round_page (datap - *data) < allocsize) - vm_deallocate (mach_task_self (), - (vm_address_t) (*data + round_page (datap - *data)), - allocsize - round_page (datap - *data)); + munmap ((caddr_t) (*data + round_page (datap - *data)), + allocsize - round_page (datap - *data)); } - + /* Set variables for return */ *datacnt = datap - *data; *amt = i; diff --git a/ext2fs/ext2_fs.h b/ext2fs/ext2_fs.h index 9934ce85..b1caeefa 100644 --- a/ext2fs/ext2_fs.h +++ b/ext2fs/ext2_fs.h @@ -16,12 +16,7 @@ #ifndef _LINUX_EXT2_FS_H #define _LINUX_EXT2_FS_H -typedef unsigned long u32; -typedef long s32; -typedef unsigned short u16; -typedef short s16; -typedef unsigned char u8; -typedef signed char s8; +/* #include <linux/types.h> */ /* * The second extended filesystem constants/structures @@ -33,40 +28,28 @@ typedef signed char s8; #undef EXT2FS_DEBUG /* - * Define EXT2FS_DEBUG_CACHE to produce cache debug messages - */ -#undef EXT2FS_DEBUG_CACHE - -/* - * Define EXT2FS_CHECK_CACHE to add some checks to the name cache code - */ -#undef EXT2FS_CHECK_CACHE - -/* - * Define EXT2FS_PRE_02B_COMPAT to convert ext 2 fs prior to 0.2b - */ -#undef EXT2FS_PRE_02B_COMPAT - -/* * Define EXT2_PREALLOCATE to preallocate data blocks for expanding files */ #define EXT2_PREALLOCATE +#define EXT2_DEFAULT_PREALLOC_BLOCKS 8 /* * The second extended file system version */ -#define EXT2FS_DATE "95/03/19" -#define EXT2FS_VERSION "0.5a" +#define EXT2FS_DATE "95/08/09" +#define EXT2FS_VERSION "0.5b" /* * Debug code */ #ifdef EXT2FS_DEBUG -extern int ext2_debug_flag; -#define ext2_debug(f, a...) \ - do { if (ext2_debug_flag) printf ("ext2fs: (debug) %s: " f "\n", __FUNCTION__ , ## a); } while (0) +# define ext2_debug(f, a...) { \ + printk ("EXT2-fs DEBUG (%s, %d): %s:", \ + __FILE__, __LINE__, __FUNCTION__); \ + printk (f, ## a); \ + } #else -#define ext2_debug(f, a...) (void)0 +# define ext2_debug(f, a...) /**/ #endif /* @@ -78,12 +61,13 @@ extern int ext2_debug_flag; #define EXT2_ACL_DATA_INO 4 /* ACL inode */ #define EXT2_BOOT_LOADER_INO 5 /* Boot loader inode */ #define EXT2_UNDEL_DIR_INO 6 /* Undelete directory inode */ -#define EXT2_FIRST_INO 11 /* First non reserved inode */ + +/* First non-reserved inode for old ext2 filesystems */ +#define EXT2_GOOD_OLD_FIRST_INO 11 /* * The second extended file system magic number */ -#define EXT2_PRE_02B_MAGIC 0xEF51 #define EXT2_SUPER_MAGIC 0xEF53 /* @@ -103,13 +87,24 @@ extern int ext2_debug_flag; # define EXT2_BLOCK_SIZE(s) (EXT2_MIN_BLOCK_SIZE << (s)->s_log_block_size) #endif #define EXT2_ACLE_PER_BLOCK(s) (EXT2_BLOCK_SIZE(s) / sizeof (struct ext2_acl_entry)) -#define EXT2_ADDR_PER_BLOCK(s) (EXT2_BLOCK_SIZE(s) / sizeof (u32)) +#define EXT2_ADDR_PER_BLOCK(s) (EXT2_BLOCK_SIZE(s) / sizeof (__u32)) #ifdef __KERNEL__ -# define EXT2_BLOCK_SIZE_BITS(s) ((s)->u.ext2_sb.s_es->s_log_block_size + 10) +# define EXT2_BLOCK_SIZE_BITS(s) ((s)->s_blocksize_bits) #else # define EXT2_BLOCK_SIZE_BITS(s) ((s)->s_log_block_size + 10) #endif -#define EXT2_INODES_PER_BLOCK(s) (EXT2_BLOCK_SIZE(s) / sizeof (struct ext2_inode)) +#ifdef __KERNEL__ +#define EXT2_ADDR_PER_BLOCK_BITS(s) ((s)->u.ext2_sb.s_addr_per_block_bits) +#define EXT2_INODE_SIZE(s) ((s)->u.ext2_sb.s_inode_size) +#define EXT2_FIRST_INO(s) ((s)->u.ext2_sb.s_first_ino) +#else +#define EXT2_INODE_SIZE(s) (((s)->s_rev_level == EXT2_GOOD_OLD_REV) ? \ + EXT2_GOOD_OLD_INODE_SIZE : \ + (s)->s_inode_size) +#define EXT2_FIRST_INO(s) (((s)->s_rev_level == EXT2_GOOD_OLD_REV) ? \ + EXT2_GOOD_OLD_FIRST_INO : \ + (s)->s_first_ino) +#endif /* * Macro-instructions used to manage fragments @@ -130,45 +125,36 @@ extern int ext2_debug_flag; */ struct ext2_acl_header /* Header of Access Control Lists */ { - u32 aclh_size; - u32 aclh_file_count; - u32 aclh_acle_count; - u32 aclh_first_acle; + __u32 aclh_size; + __u32 aclh_file_count; + __u32 aclh_acle_count; + __u32 aclh_first_acle; }; struct ext2_acl_entry /* Access Control List Entry */ { - u32 acle_size; - u16 acle_perms; /* Access permissions */ - u16 acle_type; /* Type of entry */ - u16 acle_tag; /* User or group identity */ - u16 acle_pad1; - u32 acle_next; /* Pointer on next entry for the */ + __u32 acle_size; + __u16 acle_perms; /* Access permissions */ + __u16 acle_type; /* Type of entry */ + __u16 acle_tag; /* User or group identity */ + __u16 acle_pad1; + __u32 acle_next; /* Pointer on next entry for the */ /* same inode or on next free entry */ }; /* * Structure of a blocks group descriptor */ -struct ext2_old_group_desc -{ - u32 bg_block_bitmap; /* Blocks bitmap block */ - u32 bg_inode_bitmap; /* Inodes bitmap block */ - u32 bg_inode_table; /* Inodes table block */ - u16 bg_free_blocks_count; /* Free blocks count */ - u16 bg_free_inodes_count; /* Free inodes count */ -}; - struct ext2_group_desc { - u32 bg_block_bitmap; /* Blocks bitmap block */ - u32 bg_inode_bitmap; /* Inodes bitmap block */ - u32 bg_inode_table; /* Inodes table block */ - u16 bg_free_blocks_count; /* Free blocks count */ - u16 bg_free_inodes_count; /* Free inodes count */ - u16 bg_used_dirs_count; /* Directories count */ - u16 bg_pad; - u32 bg_reserved[3]; + __u32 bg_block_bitmap; /* Blocks bitmap block */ + __u32 bg_inode_bitmap; /* Inodes bitmap block */ + __u32 bg_inode_table; /* Inodes table block */ + __u16 bg_free_blocks_count; /* Free blocks count */ + __u16 bg_free_inodes_count; /* Free inodes count */ + __u16 bg_used_dirs_count; /* Directories count */ + __u16 bg_pad; + __u32 bg_reserved[3]; }; /* @@ -178,6 +164,7 @@ struct ext2_group_desc # define EXT2_BLOCKS_PER_GROUP(s) ((s)->u.ext2_sb.s_blocks_per_group) # define EXT2_DESC_PER_BLOCK(s) ((s)->u.ext2_sb.s_desc_per_block) # define EXT2_INODES_PER_GROUP(s) ((s)->u.ext2_sb.s_inodes_per_group) +# define EXT2_DESC_PER_BLOCK_BITS(s) ((s)->u.ext2_sb.s_desc_per_block_bits) #else # define EXT2_BLOCKS_PER_GROUP(s) ((s)->s_blocks_per_group) # define EXT2_DESC_PER_BLOCK(s) (EXT2_BLOCK_SIZE(s) / sizeof (struct ext2_group_desc)) @@ -203,6 +190,41 @@ struct ext2_group_desc #define EXT2_IMMUTABLE_FL 0x00000010 /* Immutable file */ #define EXT2_APPEND_FL 0x00000020 /* writes to file may only append */ #define EXT2_NODUMP_FL 0x00000040 /* do not dump file */ +#define EXT2_NOATIME_FL 0x00000080 /* do not update atime */ +/* Reserved for compression usage... */ +#define EXT2_DIRTY_FL 0x00000100 +#define EXT2_COMPRBLK_FL 0x00000200 /* One or more compressed clusters */ +#define EXT2_NOCOMP_FL 0x00000400 /* Don't compress */ +#define EXT2_ECOMPR_FL 0x00000800 /* Compression error */ +/* End compression flags --- maybe not all used */ +#define EXT2_BTREE_FL 0x00001000 /* btree format dir */ +#define EXT2_RESERVED_FL 0x80000000 /* reserved for ext2 lib */ + +#define EXT2_FL_USER_VISIBLE 0x00001FFF /* User visible flags */ +#define EXT2_FL_USER_MODIFIABLE 0x000000FF /* User modifiable flags */ + +/* Flags that should be inherited by new inodes from their parent. */ +#define EXT2_FL_INHERITED (EXT2_SECRM_FL | EXT2_UNRM_FL | EXT2_COMPR_FL |\ + EXT2_SYNC_FL | EXT2_NODUMP_FL |\ + EXT2_NOATIME_FL | EXT2_COMPRBLK_FL |\ + EXT2_NOCOMP_FL) + +/* Flags that are appropriate for regular files (all but dir-specific ones). */ +#define EXT2_REG_FLMASK (~(0)) + +/* Flags that are appropriate for non-directories/regular files. */ +#define EXT2_OTHER_FLMASK (EXT2_NODUMP_FL | EXT2_NOATIME_FL) + +/* Mask out flags that are inappropriate for the given type of inode. */ +static __inline__ __u32 ext2_mask_flags(mode_t mode, __u32 flags) +{ + if (S_ISDIR(mode)) + return flags; + else if (S_ISREG(mode)) + return flags & EXT2_REG_FLMASK; + else + return flags & EXT2_OTHER_FLMASK; +} /* * ioctl commands @@ -216,71 +238,78 @@ struct ext2_group_desc * Structure of an inode on the disk */ struct ext2_inode { - u16 i_mode; /* File mode */ - u16 i_uid; /* Owner Uid */ - u32 i_size; /* Size in bytes */ - u32 i_atime; /* Access time */ - u32 i_ctime; /* Creation time */ - u32 i_mtime; /* Modification time */ - u32 i_dtime; /* Deletion Time */ - u16 i_gid; /* Group Id */ - u16 i_links_count; /* Links count */ - u32 i_blocks; /* Blocks count */ - u32 i_flags; /* File flags */ + __u16 i_mode; /* File mode */ + __u16 i_uid; /* Low 16 bits of Owner Uid */ + __u32 i_size; /* Size in bytes */ + __u32 i_atime; /* Access time */ + __u32 i_ctime; /* Creation time */ + __u32 i_mtime; /* Modification time */ + __u32 i_dtime; /* Deletion Time */ + __u16 i_gid; /* Low 16 bits of Group Id */ + __u16 i_links_count; /* Links count */ + __u32 i_blocks; /* Blocks count */ + __u32 i_flags; /* File flags */ union { struct { - u32 l_i_reserved1; + __u32 l_i_reserved1; } linux1; struct { - u32 h_i_translator; + __u32 h_i_translator; } hurd1; struct { - u32 m_i_reserved1; + __u32 m_i_reserved1; } masix1; } osd1; /* OS dependent 1 */ - u32 i_block[EXT2_N_BLOCKS];/* Pointers to blocks */ - u32 i_version; /* File version (for NFS) */ - u32 i_file_acl; /* File ACL */ - u32 i_dir_acl; /* Directory ACL */ - u32 i_faddr; /* Fragment address */ + __u32 i_block[EXT2_N_BLOCKS];/* Pointers to blocks */ + __u32 i_generation; /* File version (for NFS) */ + __u32 i_file_acl; /* File ACL */ + __u32 i_dir_acl; /* Directory ACL */ + __u32 i_faddr; /* Fragment address */ union { struct { - u8 l_i_frag; /* Fragment number */ - u8 l_i_fsize; /* Fragment size */ - u16 i_pad1; - u32 l_i_reserved2[2]; + __u8 l_i_frag; /* Fragment number */ + __u8 l_i_fsize; /* Fragment size */ + __u16 i_pad1; + __u16 l_i_uid_high; /* these 2 fields */ + __u16 l_i_gid_high; /* were reserved2[0] */ + __u32 l_i_reserved2; } linux2; struct { - u8 h_i_frag; /* Fragment number */ - u8 h_i_fsize; /* Fragment size */ - u16 h_i_mode_high; - u16 h_i_uid_high; - u16 h_i_gid_high; - u32 h_i_author; + __u8 h_i_frag; /* Fragment number */ + __u8 h_i_fsize; /* Fragment size */ + __u16 h_i_mode_high; + __u16 h_i_uid_high; + __u16 h_i_gid_high; + __u32 h_i_author; } hurd2; struct { - u8 m_i_frag; /* Fragment number */ - u8 m_i_fsize; /* Fragment size */ - u16 m_pad1; - u32 m_i_reserved2[2]; + __u8 m_i_frag; /* Fragment number */ + __u8 m_i_fsize; /* Fragment size */ + __u16 m_pad1; + __u32 m_i_reserved2[2]; } masix2; } osd2; /* OS dependent 2 */ }; +#define i_size_high i_dir_acl + #if defined(__KERNEL__) || defined(__linux__) #define i_reserved1 osd1.linux1.l_i_reserved1 #define i_frag osd2.linux2.l_i_frag #define i_fsize osd2.linux2.l_i_fsize +#define i_uid_low i_uid +#define i_gid_low i_gid +#define i_uid_high osd2.linux2.l_i_uid_high +#define i_gid_high osd2.linux2.l_i_gid_high #define i_reserved2 osd2.linux2.l_i_reserved2 #endif #ifdef __hurd__ #define i_translator osd1.hurd1.h_i_translator -#define i_frag osd2.hurd2.h_i_frag; -#define i_fsize osd2.hurd2.h_i_fsize; +#define i_frag osd2.hurd2.h_i_frag +#define i_fsize osd2.hurd2.h_i_fsize #define i_uid_high osd2.hurd2.h_i_uid_high #define i_gid_high osd2.hurd2.h_i_gid_high -#define i_mode_high osd2.hurd2.h_i_mode_high #define i_author osd2.hurd2.h_i_author #endif @@ -310,6 +339,7 @@ struct ext2_inode { #define EXT2_MOUNT_ERRORS_RO 0x0020 /* Remount fs ro on errors */ #define EXT2_MOUNT_ERRORS_PANIC 0x0040 /* Panic on errors */ #define EXT2_MOUNT_MINIX_DF 0x0080 /* Mimics the Minix statfs */ +#define EXT2_MOUNT_NO_UID32 0x0200 /* Disable 32-bit UIDs */ #define clear_opt(o, opt) o &= ~EXT2_MOUNT_##opt #define set_opt(o, opt) o |= EXT2_MOUNT_##opt @@ -333,40 +363,122 @@ struct ext2_inode { * Structure of the super block */ struct ext2_super_block { - u32 s_inodes_count; /* Inodes count */ - u32 s_blocks_count; /* Blocks count */ - u32 s_r_blocks_count; /* Reserved blocks count */ - u32 s_free_blocks_count; /* Free blocks count */ - u32 s_free_inodes_count; /* Free inodes count */ - u32 s_first_data_block; /* First Data Block */ - u32 s_log_block_size; /* Block size */ - s32 s_log_frag_size; /* Fragment size */ - u32 s_blocks_per_group; /* # Blocks per group */ - u32 s_frags_per_group; /* # Fragments per group */ - u32 s_inodes_per_group; /* # Inodes per group */ - u32 s_mtime; /* Mount time */ - u32 s_wtime; /* Write time */ - u16 s_mnt_count; /* Mount count */ - s16 s_max_mnt_count; /* Maximal mount count */ - u16 s_magic; /* Magic signature */ - u16 s_state; /* File system state */ - u16 s_errors; /* Behaviour when detecting errors */ - u16 s_pad; - u32 s_lastcheck; /* time of last check */ - u32 s_checkinterval; /* max. time between checks */ - u32 s_creator_os; /* OS */ - u32 s_rev_level; /* Revision level */ - u16 s_def_resuid; /* Default uid for reserved blocks */ - u16 s_def_resgid; /* Default gid for reserved blocks */ - u32 s_reserved[235]; /* Padding to the end of the block */ + __u32 s_inodes_count; /* Inodes count */ + __u32 s_blocks_count; /* Blocks count */ + __u32 s_r_blocks_count; /* Reserved blocks count */ + __u32 s_free_blocks_count; /* Free blocks count */ + __u32 s_free_inodes_count; /* Free inodes count */ + __u32 s_first_data_block; /* First Data Block */ + __u32 s_log_block_size; /* Block size */ + __s32 s_log_frag_size; /* Fragment size */ + __u32 s_blocks_per_group; /* # Blocks per group */ + __u32 s_frags_per_group; /* # Fragments per group */ + __u32 s_inodes_per_group; /* # Inodes per group */ + __u32 s_mtime; /* Mount time */ + __u32 s_wtime; /* Write time */ + __u16 s_mnt_count; /* Mount count */ + __s16 s_max_mnt_count; /* Maximal mount count */ + __u16 s_magic; /* Magic signature */ + __u16 s_state; /* File system state */ + __u16 s_errors; /* Behaviour when detecting errors */ + __u16 s_minor_rev_level; /* minor revision level */ + __u32 s_lastcheck; /* time of last check */ + __u32 s_checkinterval; /* max. time between checks */ + __u32 s_creator_os; /* OS */ + __u32 s_rev_level; /* Revision level */ + __u16 s_def_resuid; /* Default uid for reserved blocks */ + __u16 s_def_resgid; /* Default gid for reserved blocks */ + /* + * These fields are for EXT2_DYNAMIC_REV superblocks only. + * + * Note: the difference between the compatible feature set and + * the incompatible feature set is that if there is a bit set + * in the incompatible feature set that the kernel doesn't + * know about, it should refuse to mount the filesystem. + * + * e2fsck's requirements are more strict; if it doesn't know + * about a feature in either the compatible or incompatible + * feature set, it must abort and not try to meddle with + * things it doesn't understand... + */ + __u32 s_first_ino; /* First non-reserved inode */ + __u16 s_inode_size; /* size of inode structure */ + __u16 s_block_group_nr; /* block group # of this superblock */ + __u32 s_feature_compat; /* compatible feature set */ + __u32 s_feature_incompat; /* incompatible feature set */ + __u32 s_feature_ro_compat; /* readonly-compatible feature set */ + __u8 s_uuid[16]; /* 128-bit uuid for volume */ + char s_volume_name[16]; /* volume name */ + char s_last_mounted[64]; /* directory where last mounted */ + __u32 s_algorithm_usage_bitmap; /* For compression */ + /* + * Performance hints. Directory preallocation should only + * happen if the EXT2_COMPAT_PREALLOC flag is on. + */ + __u8 s_prealloc_blocks; /* Nr of blocks to try to preallocate*/ + __u8 s_prealloc_dir_blocks; /* Nr to preallocate for dirs */ + __u16 s_padding1; + __u32 s_reserved[204]; /* Padding to the end of the block */ }; +#ifdef __KERNEL__ +#define EXT2_SB(sb) (&((sb)->u.ext2_sb)) +#else +/* Assume that user mode programs are passing in an ext2fs superblock, not + * a kernel struct super_block. This will allow us to call the feature-test + * macros from user land. */ +#define EXT2_SB(sb) (sb) +#endif + +/* + * Codes for operating systems + */ #define EXT2_OS_LINUX 0 #define EXT2_OS_HURD 1 #define EXT2_OS_MASIX 2 +#define EXT2_OS_FREEBSD 3 +#define EXT2_OS_LITES 4 -#define EXT2_CURRENT_REV 0 +/* + * Revision levels + */ +#define EXT2_GOOD_OLD_REV 0 /* The good old (original) format */ +#define EXT2_DYNAMIC_REV 1 /* V2 format w/ dynamic inode sizes */ + +#define EXT2_CURRENT_REV EXT2_GOOD_OLD_REV +#define EXT2_MAX_SUPP_REV EXT2_DYNAMIC_REV + +#define EXT2_GOOD_OLD_INODE_SIZE 128 + +/* + * Feature set definitions + */ +#define EXT2_HAS_COMPAT_FEATURE(sb,mask) \ + ( EXT2_SB(sb)->s_feature_compat & (mask) ) +#define EXT2_HAS_RO_COMPAT_FEATURE(sb,mask) \ + ( EXT2_SB(sb)->s_feature_ro_compat & (mask) ) +#define EXT2_HAS_INCOMPAT_FEATURE(sb,mask) \ + ( EXT2_SB(sb)->s_feature_incompat & (mask) ) + +#define EXT2_FEATURE_COMPAT_DIR_PREALLOC 0x0001 + +#define EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER 0x0001 +#define EXT2_FEATURE_RO_COMPAT_LARGE_FILE 0x0002 +#define EXT2_FEATURE_RO_COMPAT_BTREE_DIR 0x0004 + +#define EXT2_FEATURE_INCOMPAT_COMPRESSION 0x0001 +#define EXT2_FEATURE_INCOMPAT_FILETYPE 0x0002 + +#define EXT2_FEATURE_COMPAT_SUPP 0 +#define EXT2_FEATURE_INCOMPAT_SUPP EXT2_FEATURE_INCOMPAT_FILETYPE +#define EXT2_FEATURE_RO_COMPAT_SUPP (EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER| \ + EXT2_FEATURE_RO_COMPAT_LARGE_FILE| \ + EXT2_FEATURE_RO_COMPAT_BTREE_DIR) + +/* + * Default values for user and/or group using reserved blocks + */ #define EXT2_DEF_RESUID 0 #define EXT2_DEF_RESGID 0 @@ -376,13 +488,42 @@ struct ext2_super_block { #define EXT2_NAME_LEN 255 struct ext2_dir_entry { - u32 inode; /* Inode number */ - u16 rec_len; /* Directory entry length */ - u16 name_len; /* Name length */ + __u32 inode; /* Inode number */ + __u16 rec_len; /* Directory entry length */ + __u16 name_len; /* Name length */ char name[EXT2_NAME_LEN]; /* File name */ }; /* + * The new version of the directory entry. Since EXT2 structures are + * stored in intel byte order, and the name_len field could never be + * bigger than 255 chars, it's safe to reclaim the extra byte for the + * file_type field. + */ +struct ext2_dir_entry_2 { + __u32 inode; /* Inode number */ + __u16 rec_len; /* Directory entry length */ + __u8 name_len; /* Name length */ + __u8 file_type; + char name[EXT2_NAME_LEN]; /* File name */ +}; + +/* + * Ext2 directory file types. Only the low 3 bits are used. The + * other bits are reserved for now. + */ +#define EXT2_FT_UNKNOWN 0 +#define EXT2_FT_REG_FILE 1 +#define EXT2_FT_DIR 2 +#define EXT2_FT_CHRDEV 3 +#define EXT2_FT_BLKDEV 4 +#define EXT2_FT_FIFO 5 +#define EXT2_FT_SOCK 6 +#define EXT2_FT_SYMLINK 7 + +#define EXT2_FT_MAX 8 + +/* * EXT2_DIR_PAD defines the directory entries boundaries * * NOTE: It must be a multiple of 4 @@ -392,4 +533,119 @@ struct ext2_dir_entry { #define EXT2_DIR_REC_LEN(name_len) (((name_len) + 8 + EXT2_DIR_ROUND) & \ ~EXT2_DIR_ROUND) +#ifdef __KERNEL__ +/* + * Function prototypes + */ + +/* + * Ok, these declarations are also in <linux/kernel.h> but none of the + * ext2 source programs needs to include it so they are duplicated here. + */ +# define NORET_TYPE /**/ +# define ATTRIB_NORET __attribute__((noreturn)) +# define NORET_AND noreturn, + +/* acl.c */ +extern int ext2_permission (struct inode *, int); + +/* balloc.c */ +extern int ext2_group_sparse(int group); +extern int ext2_new_block (const struct inode *, unsigned long, + __u32 *, __u32 *, int *); +extern void ext2_free_blocks (const struct inode *, unsigned long, + unsigned long); +extern unsigned long ext2_count_free_blocks (struct super_block *); +extern void ext2_check_blocks_bitmap (struct super_block *); +extern struct ext2_group_desc * ext2_get_group_desc(struct super_block * sb, + unsigned int block_group, + struct buffer_head ** bh); + +/* bitmap.c */ +extern unsigned long ext2_count_free (struct buffer_head *, unsigned); + +/* dir.c */ +extern int ext2_check_dir_entry (const char *, struct inode *, + struct ext2_dir_entry_2 *, struct buffer_head *, + unsigned long); + +/* file.c */ +extern int ext2_read (struct inode *, struct file *, char *, int); +extern int ext2_write (struct inode *, struct file *, char *, int); + +/* fsync.c */ +extern int ext2_sync_file (struct file *, struct dentry *); + +/* ialloc.c */ +extern struct inode * ext2_new_inode (const struct inode *, int, int *); +extern void ext2_free_inode (struct inode *); +extern unsigned long ext2_count_free_inodes (struct super_block *); +extern void ext2_check_inodes_bitmap (struct super_block *); + +/* inode.c */ +extern long ext2_bmap (struct inode *, long); +extern int ext2_get_block (struct inode *, long, struct buffer_head *, int); + +extern struct buffer_head * ext2_getblk (struct inode *, long, int, int *); +extern int ext2_getblk_block (struct inode *, long, int, int *, int *); +extern struct buffer_head * ext2_bread (struct inode *, int, int, int *); + +extern int ext2_getcluster (struct inode * inode, long block); +extern void ext2_read_inode (struct inode *); +extern void ext2_write_inode (struct inode *); +extern void ext2_put_inode (struct inode *); +extern void ext2_delete_inode (struct inode *); +extern int ext2_sync_inode (struct inode *); +extern void ext2_discard_prealloc (struct inode *); + +/* ioctl.c */ +extern int ext2_ioctl (struct inode *, struct file *, unsigned int, + unsigned long); + +/* namei.c */ +extern void ext2_release (struct inode *, struct file *); +extern struct dentry *ext2_lookup (struct inode *, struct dentry *); +extern int ext2_create (struct inode *,struct dentry *,int); +extern int ext2_mkdir (struct inode *,struct dentry *,int); +extern int ext2_rmdir (struct inode *,struct dentry *); +extern int ext2_unlink (struct inode *,struct dentry *); +extern int ext2_symlink (struct inode *,struct dentry *,const char *); +extern int ext2_link (struct dentry *, struct inode *, struct dentry *); +extern int ext2_mknod (struct inode *, struct dentry *, int, int); +extern int ext2_rename (struct inode *, struct dentry *, + struct inode *, struct dentry *); + +/* super.c */ +extern void ext2_error (struct super_block *, const char *, const char *, ...) + __attribute__ ((format (printf, 3, 4))); +extern NORET_TYPE void ext2_panic (struct super_block *, const char *, + const char *, ...) + __attribute__ ((NORET_AND format (printf, 3, 4))); +extern void ext2_warning (struct super_block *, const char *, const char *, ...) + __attribute__ ((format (printf, 3, 4))); +extern void ext2_put_super (struct super_block *); +extern void ext2_write_super (struct super_block *); +extern int ext2_remount (struct super_block *, int *, char *); +extern struct super_block * ext2_read_super (struct super_block *,void *,int); +extern int ext2_statfs (struct super_block *, struct statfs *, int); + +/* truncate.c */ +extern void ext2_truncate (struct inode *); + +/* + * Inodes and files operations + */ + +/* dir.c */ +extern struct inode_operations ext2_dir_inode_operations; + +/* file.c */ +extern struct inode_operations ext2_file_inode_operations; + +/* symlink.c */ +extern struct inode_operations ext2_symlink_inode_operations; +extern struct inode_operations ext2_fast_symlink_inode_operations; + +#endif /* __KERNEL__ */ + #endif /* _LINUX_EXT2_FS_H */ diff --git a/ext2fs/ext2_fs_i.h b/ext2fs/ext2_fs_i.h index 6c7e0e71..72bcd5c0 100644 --- a/ext2fs/ext2_fs_i.h +++ b/ext2fs/ext2_fs_i.h @@ -20,21 +20,23 @@ * second extended file system inode data in memory */ struct ext2_inode_info { - u32 i_data[15]; - u32 i_flags; - u32 i_faddr; - u8 i_frag_no; - u8 i_frag_size; - u16 i_osync; - u32 i_file_acl; - u32 i_dir_acl; - u32 i_dtime; - u32 i_version; - u32 i_block_group; - u32 i_next_alloc_block; - u32 i_next_alloc_goal; - u32 i_prealloc_block; - u32 i_prealloc_count; + __u32 i_data[15]; + __u32 i_flags; + __u32 i_faddr; + __u8 i_frag_no; + __u8 i_frag_size; + __u16 i_osync; + __u32 i_file_acl; + __u32 i_dir_acl; + __u32 i_dtime; + __u32 not_used_1; /* FIX: not used/ 2.2 placeholder */ + __u32 i_block_group; + __u32 i_next_alloc_block; + __u32 i_next_alloc_goal; + __u32 i_prealloc_block; + __u32 i_prealloc_count; + __u32 i_high_size; + int i_new_inode:1; /* Is a freshly allocated inode */ }; #endif /* _LINUX_EXT2_FS_I */ diff --git a/ext2fs/ext2fs.c b/ext2fs/ext2fs.c index a1bbc22c..0857a749 100644 --- a/ext2fs/ext2fs.c +++ b/ext2fs/ext2fs.c @@ -1,6 +1,6 @@ /* Main entry point for the ext2 file system translator - Copyright (C) 1994, 1995, 1996 Free Software Foundation, Inc. + Copyright (C) 1994,95,96,97,98,99,2002 Free Software Foundation, Inc. Converted for ext2fs by Miles Bader <miles@gnu.ai.mit.edu> @@ -27,11 +27,15 @@ #include <string.h> #include <error.h> #include <argz.h> +#include <argp.h> +#include <hurd/store.h> +#include <version.h> #include "ext2fs.h" /* ---------------------------------------------------------------- */ int diskfs_link_max = EXT2_LINK_MAX; +int diskfs_name_max = EXT2_NAME_LEN; int diskfs_maxsymlinks = 8; int diskfs_shortcut_symlink = 1; int diskfs_shortcut_chrdev = 1; @@ -40,24 +44,33 @@ int diskfs_shortcut_fifo = 1; int diskfs_shortcut_ifsock = 1; char *diskfs_server_name = "ext2fs"; -int diskfs_major_version = 0; -int diskfs_minor_version = 0; -int diskfs_edit_version = 0; +char *diskfs_server_version = HURD_VERSION; +char *diskfs_extra_version = "GNU Hurd; ext2 " EXT2FS_VERSION; -int diskfs_synchronous = 0; -int diskfs_readonly = 0; +int diskfs_synchronous; struct node *diskfs_root_node; + +struct store *store; +struct store_parsed *store_parsed; + +char *diskfs_disk_name; #ifdef EXT2FS_DEBUG - -int ext2_debug_flag = 0; +int ext2_debug_flag; +#endif /* Ext2fs-specific options. */ static const struct argp_option options[] = { - {"debug", 'D', 0, 0, "Toggle debugging output" }, + {"debug", 'D', 0, 0, "Toggle debugging output" +#ifndef EXT2FS_DEBUG + " (not compiled in)" +#endif + }, + {"sblock", 'S', "BLOCKNO", 0, + "Use alternate superblock location (1kb blocks)"}, {0} }; @@ -65,19 +78,50 @@ options[] = static error_t parse_opt (int key, char *arg, struct argp_state *state) { + /* We save our parsed values in this structure, hung off STATE->hook. + Only after parsing all options successfully will we use these values. */ + struct + { + int debug_flag; + unsigned int sb_block; + } *values = state->hook; + switch (key) { case 'D': - state->hook = (void *)1; /* Do it at the end */ + values->debug_flag = 1; + break; + case 'S': + values->sb_block = strtoul (arg, &arg, 0); + if (!arg || *arg != '\0') + { + argp_error (state, "invalid number for --sblock"); + return EINVAL; + } break; case ARGP_KEY_INIT: - state->hook = 0; + state->child_inputs[0] = state->input; + values = malloc (sizeof *values); + if (values == 0) + return ENOMEM; + state->hook = values; + bzero (values, sizeof *values); + values->sb_block = SBLOCK_BLOCK; break; + case ARGP_KEY_SUCCESS: /* All options parse successfully, so implement ours if possible. */ - if (state->hook) - ext2_debug_flag = !ext2_debug_flag; + if (values->debug_flag) + { +#ifdef EXT2FS_DEBUG + ext2_debug_flag = !ext2_debug_flag; +#else + argp_failure (state, 2, 0, "debugging support not compiled in"); + return EINVAL; +#endif + } + break; default: @@ -85,89 +129,59 @@ parse_opt (int key, char *arg, struct argp_state *state) } return 0; } - -/* Add our startup arguments to the standard diskfs set. */ -static const struct argp *startup_parents[] = { &diskfs_std_device_startup_argp, 0}; -static struct argp startup_argp = {options, parse_opt, 0, 0, startup_parents}; - -/* Similarly at runtime. */ -static const struct argp *runtime_parents[] = {&diskfs_std_runtime_argp, 0}; -static struct argp runtime_argp = {options, parse_opt, 0, 0, runtime_parents}; - -struct argp *diskfs_runtime_argp = (struct argp *)&runtime_argp; /* Override the standard diskfs routine so we can add our own output. */ error_t -diskfs_get_options (char **argz, unsigned *argz_len) +diskfs_append_args (char **argz, size_t *argz_len) { error_t err; - *argz = 0; - *argz_len = 0; - /* Get the standard things. */ err = diskfs_append_std_options (argz, argz_len); +#ifdef EXT2FS_DEBUG if (!err && ext2_debug_flag) - { - err = argz_add (argz, argz_len, "--debug"); - if (err) - free (argz); /* Deallocate what diskfs returned. */ - } + err = argz_add (argz, argz_len, "--debug"); +#endif + if (! err) + err = store_parsed_append_args (store_parsed, argz, argz_len); return err; } + +/* Add our startup arguments to the standard diskfs set. */ +static const struct argp_child startup_children[] = + {{&diskfs_store_startup_argp}, {0}}; +static struct argp startup_argp = {options, parse_opt, 0, 0, startup_children}; -#else /* !EXT2FS_DEBUG */ - -#define startup_argp diskfs_std_device_startup_argp +/* Similarly at runtime. */ +static const struct argp_child runtime_children[] = + {{&diskfs_std_runtime_argp}, {0}}; +static struct argp runtime_argp = {options, parse_opt, 0, 0, runtime_children}; -#endif /* EXT2FS_DEBUG */ +struct argp *diskfs_runtime_argp = (struct argp *)&runtime_argp; -void +int main (int argc, char **argv) { error_t err; - mach_port_t bootstrap = MACH_PORT_NULL; - - argp_parse (&startup_argp, argc, argv, 0, 0, 0); + mach_port_t bootstrap; - diskfs_console_stdio (); + /* Initialize the diskfs library, parse arguments, and open the store. + This starts the first diskfs thread for us. */ + store = diskfs_init_main (&startup_argp, argc, argv, + &store_parsed, &bootstrap); - if (! diskfs_boot_flags) - { - task_get_bootstrap_port (mach_task_self (), &bootstrap); - if (bootstrap == MACH_PORT_NULL) - error (2, 0, "Must be started as a translator"); - } - - /* Initialize the diskfs library. This must come before - any other diskfs call. */ - err = diskfs_init_diskfs (); - if (err) - error (4, err, "init"); - - err = diskfs_device_open (); - if (err) - error (3, err, "%s", diskfs_device_arg); - - if ((diskfs_device_size << diskfs_log2_device_block_size) - < SBLOCK_OFFS + SBLOCK_SIZE) - ext2_panic ("superblock won't fit on the device!"); - if (diskfs_log2_device_block_size == 0) - ext2_panic ("device block size (%u) not a power of two", - diskfs_device_block_size); - if (diskfs_log2_device_blocks_per_page < 0) - ext2_panic ("device block size (%u) greater than page size (%d)", - diskfs_device_block_size, vm_page_size); + if (store->size < SBLOCK_OFFS + SBLOCK_SIZE) + ext2_panic ("device too small for superblock (%Ld bytes)", store->size); + if (store->log2_blocks_per_page < 0) + ext2_panic ("device block size (%zu) greater than page size (%zd)", + store->block_size, vm_page_size); /* Map the entire disk. */ create_disk_pager (); - /* Start the first request thread, to handle RPCs and page requests. */ - diskfs_spawn_first_thread (); - - pokel_init (&global_pokel, disk_pager, disk_image); + pokel_init (&global_pokel, diskfs_disk_pager, disk_image); get_hypermetadata(); @@ -188,49 +202,15 @@ main (int argc, char **argv) /* and so we die, leaving others to do the real work. */ cthread_exit (0); + /* NOTREACHED */ + return 0; } error_t diskfs_reload_global_state () { pokel_flush (&global_pokel); - pager_flush (disk_pager, 1); + pager_flush (diskfs_disk_pager, 1); get_hypermetadata (); return 0; } - -/* ---------------------------------------------------------------- */ - -static spin_lock_t free_page_bufs_lock = SPIN_LOCK_INITIALIZER; -static vm_address_t free_page_bufs = 0; - -/* Returns a single page page-aligned buffer. */ -vm_address_t get_page_buf () -{ - vm_address_t buf; - - spin_lock (&free_page_bufs_lock); - - buf = free_page_bufs; - if (buf == 0) - { - spin_unlock (&free_page_bufs_lock); - vm_allocate (mach_task_self (), &buf, vm_page_size, 1); - } - else - { - free_page_bufs = *(vm_address_t *)buf; - spin_unlock (&free_page_bufs_lock); - } - - return buf; -} - -/* Frees a block returned by get_page_buf. */ -void free_page_buf (vm_address_t buf) -{ - spin_lock (&free_page_bufs_lock); - *(vm_address_t *)buf = free_page_bufs; - free_page_bufs = buf; - spin_unlock (&free_page_bufs_lock); -} diff --git a/ext2fs/ext2fs.h b/ext2fs/ext2fs.h index 8a4bde9d..2ad4a9df 100644 --- a/ext2fs/ext2fs.h +++ b/ext2fs/ext2fs.h @@ -1,8 +1,7 @@ /* Common definitions for the ext2 filesystem translator - Copyright (C) 1995, 1996 Free Software Foundation, Inc. - - Written by Miles Bader <miles@gnu.ai.mit.edu> + Copyright (C) 1995, 1996, 1999, 2002, 2004 Free Software Foundation, Inc. + Written by Miles Bader <miles@gnu.org> This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as @@ -27,10 +26,35 @@ #include <hurd/diskfs.h> #include <assert.h> #include <rwlock.h> +#include <sys/mman.h> #define __hurd__ /* Enable some hurd-specific fields. */ + +/* Types used by the ext2 header files. */ +typedef u_int32_t __u32; +typedef int32_t __s32; +typedef u_int16_t __u16; +typedef int16_t __s16; +typedef u_int8_t __u8; +typedef int8_t __s8; + #include "ext2_fs.h" #include "ext2_fs_i.h" + +#define i_mode_high osd2.hurd2.h_i_mode_high /* missing from ext2_fs.h */ + + +/* If ext2_fs.h defined a debug routine, undef it and use our own. */ +#undef ext2_debug + +#ifdef EXT2FS_DEBUG +extern int ext2_debug_flag; +#define ext2_debug(f, a...) \ + do { if (ext2_debug_flag) printf ("ext2fs: (debug) %s: " f "\n", __FUNCTION__ , ## a); } while (0) +#else +#define ext2_debug(f, a...) (void)0 +#endif + #undef __hurd__ /* Define this if memory objects should not be cached by the kernel. @@ -42,7 +66,7 @@ int printf (const char *fmt, ...); /* A block number. */ -typedef u32 block_t; +typedef __u32 block_t; /* ---------------------------------------------------------------- */ @@ -77,64 +101,52 @@ void pokel_flush (struct pokel *pokel); /* Transfer all regions from FROM to POKEL, which must have the same pager. */ void pokel_inherit (struct pokel *pokel, struct pokel *from); +#include <features.h> +#ifdef EXT2FS_DEFINE_EI +#define EXT2FS_EI +#else +#define EXT2FS_EI __extern_inline +#endif + /* ---------------------------------------------------------------- */ /* Bitmap routines. */ +#include <stdint.h> + +extern int test_bit (unsigned num, char *bitmap); + +extern int set_bit (unsigned num, char *bitmap); + +#if defined(__USE_EXTERN_INLINES) || defined(EXT2FS_DEFINE_EI) /* Returns TRUE if bit NUM is set in BITMAP. */ -extern inline int +EXT2FS_EI int test_bit (unsigned num, char *bitmap) { - return bitmap[num >> 3] & (1 << (num & 0x7)); + const uint32_t *const bw = (uint32_t *) bitmap + (num >> 5); + const uint_fast32_t mask = 1 << (num & 31); + return *bw & mask; } /* Sets bit NUM in BITMAP, and returns the previous state of the bit. Unlike the linux version, this function is NOT atomic! */ -extern inline int +EXT2FS_EI int set_bit (unsigned num, char *bitmap) { - char *p = bitmap + (num >> 3); - char byte = *p; - char mask = (1 << (num & 0x7)); - - if (byte & mask) - return 1; - else - { - *p = byte | mask; - return 0; - } + uint32_t *const bw = (uint32_t *) bitmap + (num >> 5); + const uint_fast32_t mask = 1 << (num & 31); + return (*bw & mask) ?: (*bw |= mask, 0); } /* Clears bit NUM in BITMAP, and returns the previous state of the bit. Unlike the linux version, this function is NOT atomic! */ -extern inline int +EXT2FS_EI int clear_bit (unsigned num, char *bitmap) { - char *p = bitmap + (num >> 3); - char byte = *p; - char mask = (1 << (num & 0x7)); - - if (byte & mask) - { - *p = byte & ~mask; - return 1; - } - else - return 0; + uint32_t *const bw = (uint32_t *) bitmap + (num >> 5); + const uint_fast32_t mask = 1 << (num & 31); + return (*bw & mask) ? (*bw &= ~mask, mask) : 0; } - -/* Counts the number of bits unset in MAP, a bitmap NUMCHARS long. */ -unsigned long count_free (char * map, unsigned int numchars); - -extern int find_first_zero_bit(void * addr, unsigned size); - -extern int find_next_zero_bit (void * addr, int size, int offset); - -extern unsigned long ffz(unsigned long word); - -/* Returns a pointer to the first occurence of CH in the buffer BUF of len - LEN, or BUF + LEN if CH doesn't occur. */ -void *memscan(void *buf, unsigned char ch, unsigned len); +#endif /* Use extern inlines. */ /* ---------------------------------------------------------------- */ @@ -156,6 +168,7 @@ struct disknode /* Random extra info used by the ext2 routines. */ struct ext2_inode_info info; + uint32_t info_i_translator; /* That struct from Linux source lacks this. */ /* This file's pager. */ struct pager *pager; @@ -163,6 +176,9 @@ struct disknode /* True if the last page of the file has been made writable, but is only partially allocated. */ int last_page_partially_writable; + + /* Index to start a directory lookup at. */ + int dir_idx; }; struct user_pager_info @@ -197,23 +213,34 @@ void flush_node_pager (struct node *node); /* ---------------------------------------------------------------- */ -/* Our in-core copy of the super-block. */ +/* The physical media. */ +extern struct store *store; +/* What the user specified. */ +extern struct store_parsed *store_parsed; + +/* Mapped image of the disk. */ +extern void *disk_image; + +/* Our in-core copy of the super-block (pointer into the disk_image). */ struct ext2_super_block *sblock; /* True if sblock has been modified. */ int sblock_dirty; /* Where the super-block is located on disk (at min-block 1). */ -#define SBLOCK_BLOCK 1 -#define SBLOCK_OFFS (SBLOCK_BLOCK * EXT2_MIN_BLOCK_SIZE) -#define SBLOCK_SIZE (sizeof (struct ext2_super_block)) +#define SBLOCK_BLOCK 1 /* Default location, second 1k block. */ +#define SBLOCK_SIZE (sizeof (struct ext2_super_block)) +extern unsigned int sblock_block; /* Specified location (in 1k blocks). */ +#define SBLOCK_OFFS (sblock_block << 10) /* Byte offset of superblock. */ /* The filesystem block-size. */ -unsigned long block_size; +unsigned int block_size; /* The log base 2 of BLOCK_SIZE. */ -unsigned log2_block_size; +unsigned int log2_block_size; -/* log2 of the number of device blocks (DISKFS_DEVICE_BLOCK_SIZE) in a - filesystem block (BLOCK_SIZE). */ +/* The number of bits to scale min-blocks to get filesystem blocks. */ +#define BLOCKSIZE_SCALE (sblock->s_log_block_size) + +/* log2 of the number of device blocks in a filesystem block. */ unsigned log2_dev_blocks_per_fs_block; /* log2 of the number of stat blocks (512 bytes) in a filesystem block. */ @@ -226,14 +253,6 @@ vm_address_t zeroblock; void get_hypermetadata (); /* ---------------------------------------------------------------- */ - -/* Returns a single page page-aligned buffer. */ -vm_address_t get_page_buf (); - -/* Frees a block returned by get_page_buf. */ -void free_page_buf (vm_address_t buf); - -/* ---------------------------------------------------------------- */ /* Random stuff calculated from the super block. */ unsigned long frag_size; /* Size of a fragment in bytes */ @@ -277,32 +296,28 @@ unsigned long next_generation; #define bptr_block(ptr) boffs_block(bptr_offs(ptr)) /* Get the descriptor for block group NUM. The block group descriptors are - stored starting in the block following the super block. */ -extern inline struct ext2_group_desc * -group_desc(unsigned long num) -{ - int desc_per_block = EXT2_DESC_PER_BLOCK(sblock); - unsigned long group_desc = num / desc_per_block; - unsigned long desc = num % desc_per_block; - return - ((struct ext2_group_desc *)boffs_ptr(SBLOCK_OFFS + boffs(1 + group_desc))) - + desc; -} + stored starting in the filesystem block following the super block. + We cache a pointer into the disk image for easy lookup. */ +#define group_desc(num) (&group_desc_image[num]) +struct ext2_group_desc *group_desc_image; #define inode_group_num(inum) (((inum) - 1) / sblock->s_inodes_per_group) +extern struct ext2_inode *dino (ino_t inum); + +#if defined(__USE_EXTERN_INLINES) || defined(EXT2FS_DEFINE_EI) /* Convert an inode number to the dinode on disk. */ -extern inline struct ext2_inode * +EXT2FS_EI struct ext2_inode * dino (ino_t inum) { unsigned long inodes_per_group = sblock->s_inodes_per_group; unsigned long bg_num = (inum - 1) / inodes_per_group; unsigned long group_inum = (inum - 1) % inodes_per_group; struct ext2_group_desc *bg = group_desc(bg_num); - unsigned long inodes_per_block = EXT2_INODES_PER_BLOCK(sblock); block_t block = bg->bg_inode_table + (group_inum / inodes_per_block); return ((struct ext2_inode *)bptr(block)) + group_inum % inodes_per_block; } +#endif /* Use extern inlines. */ /* ---------------------------------------------------------------- */ /* inode.c */ @@ -331,11 +346,19 @@ struct pokel global_pokel; char *modified_global_blocks; spin_lock_t modified_global_blocks_lock; +extern int global_block_modified (block_t block); +extern void record_global_poke (void *ptr); +extern void sync_global_ptr (void *bptr, int wait); +extern void record_indir_poke (struct node *node, void *ptr); +extern void sync_global (int wait); +extern void alloc_sync (struct node *np); + +#if defined(__USE_EXTERN_INLINES) || defined(EXT2FS_DEFINE_EI) /* Marks the global block BLOCK as being modified, and returns true if we think it may have been clean before (but we may not be sure). Note that this isn't enough to cause the block to be synced; you must call record_global_poke to do that. */ -extern inline int +EXT2FS_EI int global_block_modified (block_t block) { if (modified_global_blocks) @@ -351,7 +374,7 @@ global_block_modified (block_t block) } /* This records a modification to a non-file block. */ -extern inline void +EXT2FS_EI void record_global_poke (void *ptr) { int boffs = trunc_block (bptr_offs (ptr)); @@ -360,16 +383,16 @@ record_global_poke (void *ptr) } /* This syncs a modification to a non-file block. */ -extern inline void +EXT2FS_EI void sync_global_ptr (void *bptr, int wait) { vm_offset_t boffs = trunc_block (bptr_offs (bptr)); global_block_modified (boffs_block (boffs)); - pager_sync_some (disk_pager, trunc_page (boffs), vm_page_size, wait); + pager_sync_some (diskfs_disk_pager, trunc_page (boffs), vm_page_size, wait); } /* This records a modification to one of a file's indirect blocks. */ -extern inline void +EXT2FS_EI void record_indir_poke (struct node *node, void *ptr) { int boffs = trunc_block (bptr_offs (ptr)); @@ -379,14 +402,14 @@ record_indir_poke (struct node *node, void *ptr) /* ---------------------------------------------------------------- */ -extern inline void +EXT2FS_EI void sync_global (int wait) { pokel_sync (&global_pokel, wait); } /* Sync all allocation information and node NP if diskfs_synchronous. */ -extern inline void +EXT2FS_EI void alloc_sync (struct node *np) { if (diskfs_synchronous) @@ -399,18 +422,21 @@ alloc_sync (struct node *np) diskfs_set_hypermetadata (1, 0); } } +#endif /* Use extern inlines. */ /* ---------------------------------------------------------------- */ /* getblk.c */ void ext2_discard_prealloc (struct node *node); -/* Returns in DISK_BLOCK the disk block correspding to BLOCK in NODE. If - there is no such block yet, but CREATE is true, then it is created, +/* Returns in DISK_BLOCK the disk block corresponding to BLOCK in NODE. + If there is no such block yet, but CREATE is true, then it is created, otherwise EINVAL is returned. */ error_t ext2_getblk (struct node *node, block_t block, int create, block_t *disk_block); -block_t ext2_new_block (block_t goal, block_t *prealloc_count, block_t *prealloc_block); +block_t ext2_new_block (block_t goal, + block_t prealloc_goal, + block_t *prealloc_count, block_t *prealloc_block); void ext2_free_blocks (block_t block, unsigned long count); diff --git a/ext2fs/getblk.c b/ext2fs/getblk.c index ecc69dcc..23ba6459 100644 --- a/ext2fs/getblk.c +++ b/ext2fs/getblk.c @@ -1,8 +1,8 @@ /* File block to disk block mapping routines - Copyright (C) 1995, 1996 Free Software Foundation, Inc. + Copyright (C) 1995,96,99,2000,2004 Free Software Foundation, Inc. - Converted to work under the hurd by Miles Bader <miles@gnu.ai.mit.edu> + Converted to work under the hurd by Miles Bader <miles@gnu.org> This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as @@ -38,14 +38,14 @@ #include <string.h> #include "ext2fs.h" -/* +/* * ext2_discard_prealloc and ext2_alloc_block are atomic wrt. the * superblock in the same manner as are ext2_free_blocks and * ext2_new_block. We just wait on the super rather than locking it * here, since ext2_new_block will do the necessary locking and we * can't block until then. */ -void +void ext2_discard_prealloc (struct node *node) { #ifdef EXT2_PREALLOCATE @@ -78,7 +78,7 @@ ext2_alloc_block (struct node *node, block_t goal, int zero) { result = node->dn->info.i_prealloc_block++; node->dn->info.i_prealloc_count--; - ext2_debug ("preallocation hit (%lu/%lu) => %lu", + ext2_debug ("preallocation hit (%lu/%lu) => %u", ++alloc_hits, ++alloc_attempts, result); } else @@ -86,13 +86,17 @@ ext2_alloc_block (struct node *node, block_t goal, int zero) ext2_debug ("preallocation miss (%lu/%lu)", alloc_hits, ++alloc_attempts); ext2_discard_prealloc (node); - if (S_ISREG (node->dn_stat.st_mode)) - result = - ext2_new_block (goal, - &node->dn->info.i_prealloc_count, - &node->dn->info.i_prealloc_block); - else - result = ext2_new_block (goal, 0, 0); + result = ext2_new_block + (goal, + S_ISREG (node->dn_stat.st_mode) + ? (sblock->s_prealloc_blocks ?: EXT2_DEFAULT_PREALLOC_BLOCKS) + : (S_ISDIR (node->dn_stat.st_mode) + && EXT2_HAS_COMPAT_FEATURE(sblock, + EXT2_FEATURE_COMPAT_DIR_PREALLOC)) + ? sblock->s_prealloc_dir_blocks + : 0, + &node->dn->info.i_prealloc_count, + &node->dn->info.i_prealloc_block); } #else result = ext2_new_block (goal, 0, 0); @@ -150,7 +154,7 @@ inode_getblk (struct node *node, int nr, int create, int zero, *result = ext2_alloc_block (node, goal, zero); - ext2_debug ("%screate, hint = %lu, goal = %lu => %lu", + ext2_debug ("%screate, hint = %u, goal = %u => %u", create ? "" : "no", hint, goal, *result); if (!*result) @@ -221,8 +225,8 @@ block_getblk (struct node *node, block_t block, int nr, int create, int zero, return 0; } -/* Returns in DISK_BLOCK the disk block correspding to BLOCK in NODE. If - there is no such block yet, but CREATE is true, then it is created, +/* Returns in DISK_BLOCK the disk block corresponding to BLOCK in NODE. + If there is no such block yet, but CREATE is true, then it is created, otherwise EINVAL is returned. */ error_t ext2_getblk (struct node *node, block_t block, int create, block_t *disk_block) @@ -235,7 +239,7 @@ ext2_getblk (struct node *node, block_t block, int create, block_t *disk_block) addr_per_block * addr_per_block + addr_per_block * addr_per_block * addr_per_block) { - ext2_warning ("block > big: %lu", block); + ext2_warning ("block > big: %u", block); return EIO; } /* @@ -244,7 +248,7 @@ ext2_getblk (struct node *node, block_t block, int create, block_t *disk_block) * allocations use the same goal zone */ - ext2_debug ("block = %lu, next = %lu, goal = %lu", block, + ext2_debug ("block = %u, next = %u, goal = %u", block, node->dn->info.i_next_alloc_block, node->dn->info.i_next_alloc_goal); diff --git a/ext2fs/hyper.c b/ext2fs/hyper.c index f46d0fcb..bee4175f 100644 --- a/ext2fs/hyper.c +++ b/ext2fs/hyper.c @@ -1,8 +1,7 @@ /* Fetching and storing the hypermetadata (superblock and bg summary info) - Copyright (C) 1994, 1995, 1996 Free Software Foundation, Inc. - - Written by Miles Bader <miles@gnu.ai.mit.edu> + Copyright (C) 1994,95,96,99,2001,02 Free Software Foundation, Inc. + Written by Miles Bader <miles@gnu.org> This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as @@ -21,20 +20,20 @@ #include <string.h> #include <stdio.h> #include <error.h> +#include <hurd/store.h> #include "ext2fs.h" -vm_address_t zeroblock = 0; -char *modified_global_blocks = 0; +vm_address_t zeroblock; +char *modified_global_blocks; static void allocate_mod_map (void) { - static vm_size_t mod_map_size = 0; + static vm_size_t mod_map_size; if (modified_global_blocks && mod_map_size) /* Get rid of the old one. */ - vm_deallocate (mach_task_self (), - (vm_address_t)modified_global_blocks, mod_map_size); + munmap (modified_global_blocks, mod_map_size); if (!diskfs_readonly && block_size < vm_page_size) /* If the block size is too small, we have to take extra care when @@ -42,18 +41,18 @@ allocate_mod_map (void) on any file pager blocks. In this case use a bitmap to record which global blocks are actually modified so the pager can write only them. */ { - error_t err; /* One bit per filesystem block. */ mod_map_size = sblock->s_blocks_count >> 3; - err = - vm_allocate (mach_task_self (), - (vm_address_t *)&modified_global_blocks, mod_map_size, 1); - assert_perror (err); + modified_global_blocks = mmap (0, mod_map_size, PROT_READ|PROT_WRITE, + MAP_ANON, 0, 0); + assert (modified_global_blocks != (void *) -1); } else modified_global_blocks = 0; } +unsigned int sblock_block = SBLOCK_BLOCK; /* in 1k blocks */ + static int ext2fs_clean; /* fs clean before we started writing? */ void @@ -63,10 +62,7 @@ get_hypermetadata (void) if (err) ext2_panic ("can't read superblock: %s", strerror (err)); - if (zeroblock) - vm_deallocate (mach_task_self (), zeroblock, block_size); - - sblock = (struct ext2_super_block *)boffs_ptr (SBLOCK_OFFS); + sblock = (struct ext2_super_block *) boffs_ptr (SBLOCK_OFFS); if (sblock->s_magic != EXT2_SUPER_MAGIC #ifdef EXT2FS_PRE_02B_COMPAT @@ -76,38 +72,39 @@ get_hypermetadata (void) ext2_panic ("bad magic number %#x (should be %#x)", sblock->s_magic, EXT2_SUPER_MAGIC); - block_size = EXT2_MIN_BLOCK_SIZE << sblock->s_log_block_size; - - if (block_size > 8192) - ext2_panic ("block size %ld is too big (max is 8192 bytes)", block_size); + log2_block_size = EXT2_MIN_BLOCK_LOG_SIZE + sblock->s_log_block_size; + block_size = 1 << log2_block_size; - log2_block_size = ffs (block_size) - 1; - if ((1 << log2_block_size) != block_size) - ext2_panic ("block size %ld isn't a power of two!", block_size); + if (block_size > EXT2_MAX_BLOCK_SIZE) + ext2_panic ("block size %d is too big (max is %d bytes)", + block_size, EXT2_MAX_BLOCK_SIZE); - log2_dev_blocks_per_fs_block - = log2_block_size - diskfs_log2_device_block_size; + log2_dev_blocks_per_fs_block = log2_block_size - store->log2_block_size; if (log2_dev_blocks_per_fs_block < 0) - ext2_panic ("block size %ld isn't a power-of-two multiple of the device" - " block size (%d)!", - block_size, diskfs_device_block_size); + ext2_panic ("block size %d isn't a power-of-two multiple of the device" + " block size (%zd)!", + block_size, store->block_size); log2_stat_blocks_per_fs_block = 0; while ((512 << log2_stat_blocks_per_fs_block) < block_size) log2_stat_blocks_per_fs_block++; if ((512 << log2_stat_blocks_per_fs_block) != block_size) - ext2_panic ("block size %ld isn't a power-of-two multiple of 512!", + ext2_panic ("block size %d isn't a power-of-two multiple of 512!", block_size); - if (diskfs_device_size - < (sblock->s_blocks_count << log2_dev_blocks_per_fs_block)) - ext2_panic ("disk size (%ld blocks) too small " - "(superblock says we need %ld)", - diskfs_device_size, - sblock->s_blocks_count << log2_dev_blocks_per_fs_block); + if ((store->size >> log2_block_size) < sblock->s_blocks_count) + ext2_panic ("disk size (%qd bytes) too small; superblock says we need %qd", + (long long int) store->size, + (long long int) sblock->s_blocks_count << log2_block_size); + if (log2_dev_blocks_per_fs_block != 0 + && (store->size & ((1 << log2_dev_blocks_per_fs_block) - 1)) != 0) + ext2_warning ("%Ld (%zd byte) device blocks " + " unused after last filesystem (%d byte) block", + (store->size & ((1 << log2_dev_blocks_per_fs_block) - 1)), + store->block_size, block_size); /* Set these handy variables. */ - inodes_per_block = block_size / sizeof (struct ext2_inode); + inodes_per_block = block_size / EXT2_INODE_SIZE (sblock); frag_size = EXT2_MIN_FRAG_SIZE << sblock->s_log_frag_size; if (frag_size) @@ -115,6 +112,23 @@ get_hypermetadata (void) else ext2_panic ("frag size is zero!"); + if (sblock->s_rev_level > EXT2_GOOD_OLD_REV) + { + if (sblock->s_feature_incompat & ~EXT2_FEATURE_INCOMPAT_SUPP) + ext2_panic ("could not mount because of unsupported optional features" + " (0x%x)", + sblock->s_feature_incompat & ~EXT2_FEATURE_INCOMPAT_SUPP); + if (sblock->s_feature_ro_compat & ~EXT2_FEATURE_RO_COMPAT_SUPP) + { + ext2_warning ("mounted readonly because of" + " unsupported optional features (0x%x)", + sblock->s_feature_ro_compat & ~EXT2_FEATURE_RO_COMPAT_SUPP); + diskfs_readonly = 1; + } + if (sblock->s_inode_size != EXT2_GOOD_OLD_INODE_SIZE) + ext2_panic ("inode size %d isn't supported", sblock->s_inode_size); + } + groups_count = ((sblock->s_blocks_count - sblock->s_first_data_block + sblock->s_blocks_per_group - 1) @@ -140,11 +154,16 @@ get_hypermetadata (void) diskfs_end_catch_exception (); + /* Cache a convenient pointer to the block group descriptors for allocation. + These are stored in the filesystem blocks following the superblock. */ + group_desc_image = (struct ext2_group_desc *) bptr (bptr_block (sblock) + 1); + /* A handy source of page-aligned zeros. */ - vm_allocate (mach_task_self (), &zeroblock, block_size, 1); + if (zeroblock == 0) + zeroblock = (vm_address_t) mmap (0, block_size, PROT_READ, MAP_ANON, 0, 0); } -void +error_t diskfs_set_hypermetadata (int wait, int clean) { if (clean && ext2fs_clean && !(sblock->s_state & EXT2_VALID_FS)) @@ -168,6 +187,9 @@ diskfs_set_hypermetadata (int wait, int clean) } sync_global (wait); + + /* Should check writability here and return EROFS if necessary. XXX */ + return 0; } void @@ -175,10 +197,9 @@ diskfs_readonly_changed (int readonly) { allocate_mod_map (); - vm_protect (mach_task_self (), - (vm_address_t)disk_image, - diskfs_device_size << diskfs_log2_device_block_size, - 0, VM_PROT_READ | (readonly ? 0 : VM_PROT_WRITE)); + (*(readonly ? store_set_flags : store_clear_flags)) (store, STORE_READONLY); + + mprotect (disk_image, store->size, PROT_READ | (readonly ? 0 : PROT_WRITE)); if (!readonly && !(sblock->s_state & EXT2_VALID_FS)) ext2_warning ("UNCLEANED FILESYSTEM NOW WRITABLE"); diff --git a/ext2fs/ialloc.c b/ext2fs/ialloc.c index f2d9348e..15c17a4e 100644 --- a/ext2fs/ialloc.c +++ b/ext2fs/ialloc.c @@ -1,8 +1,8 @@ /* Inode allocation routines. - Copyright (C) 1995, 1996 Free Software Foundation, Inc. + Copyright (C) 1995,96,99,2000,02 Free Software Foundation, Inc. - Converted to work under the hurd by Miles Bader <miles@gnu.ai.mit.edu> + Converted to work under the hurd by Miles Bader <miles@gnu.org> This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as @@ -18,7 +18,7 @@ along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ -/* +/* * linux/fs/ext2/ialloc.c * * Copyright (C) 1992, 1993, 1994, 1995 @@ -26,7 +26,7 @@ * Laboratoire MASI - Institut Blaise Pascal * Universite Pierre et Marie Curie (Paris VI) * - * BSD ufs-inspired inode and directory allocation by + * BSD ufs-inspired inode and directory allocation by * Stephen Tweedie (sct@dcs.ed.ac.uk), 1993 */ @@ -42,10 +42,11 @@ */ #include "ext2fs.h" +#include "bitmap.c" /* ---------------------------------------------------------------- */ -/* Free node NP; the on disk copy has already been synced with +/* Free node NP; the on disk copy has already been synced with diskfs_node_update (where NP->dn_stat.st_mode was 0). It's mode used to be OLD_MODE. */ void @@ -63,9 +64,9 @@ diskfs_free_node (struct node *np, mode_t old_mode) spin_lock (&global_lock); - if (inum < EXT2_FIRST_INO || inum > sblock->s_inodes_count) + if (inum < EXT2_FIRST_INO (sblock) || inum > sblock->s_inodes_count) { - ext2_error ("reserved inode or nonexistent inode: %u", inum); + ext2_error ("reserved inode or nonexistent inode: %Ld", inum); spin_unlock (&global_lock); return; } @@ -77,7 +78,7 @@ diskfs_free_node (struct node *np, mode_t old_mode) bh = bptr (gdp->bg_inode_bitmap); if (!clear_bit (bit, bh)) - ext2_warning ("bit already cleared for inode %u", inum); + ext2_warning ("bit already cleared for inode %Ld", inum); else { record_global_poke (bh); @@ -236,7 +237,7 @@ repeat: } inum += i * sblock->s_inodes_per_group + 1; - if (inum < EXT2_FIRST_INO || inum > sblock->s_inodes_count) + if (inum < EXT2_FIRST_INO (sblock) || inum > sblock->s_inodes_count) { ext2_error ("reserved inode or inode > inodes count - " "block_group = %d,inode=%d", i, inum); @@ -290,7 +291,6 @@ diskfs_alloc_node (struct node *dir, mode_t mode, struct node **node) if (st->st_blocks) { - ext2_warning("Free inode %d had %d blocks", inum, st->st_blocks); st->st_blocks = 0; np->dn_set_ctime = 1; } @@ -301,15 +301,23 @@ diskfs_alloc_node (struct node *dir, mode_t mode, struct node **node) np->dn->info.i_data[block] = 0; np->dn_set_ctime = 1; } - np->istranslated = 0; + if (np->dn->info_i_translator != 0) + { + np->dn->info_i_translator = 0; + np->dn_set_ctime = 1; + } + st->st_mode &= ~S_IPTRANS; if (np->allocsize) { - ext2_warning ("Free inode %d had a size of %ld", inum, st->st_size); st->st_size = 0; np->allocsize = 0; np->dn_set_ctime = 1; } + /* Propagate initial inode flags from the directory, as Linux does. */ + np->dn->info.i_flags = + ext2_mask_flags(mode, dir->dn->info.i_flags & EXT2_FL_INHERITED); + st->st_flags = 0; /* @@ -330,7 +338,7 @@ diskfs_alloc_node (struct node *dir, mode_t mode, struct node **node) /* ---------------------------------------------------------------- */ -unsigned long +unsigned long ext2_count_free_inodes () { #ifdef EXT2FS_DEBUG @@ -353,7 +361,7 @@ ext2_count_free_inodes () i, gdp->bg_free_inodes_count, x); bitmap_count += x; } - ext2_debug ("stored = %lu, computed = %lu, %lu", + ext2_debug ("stored = %u, computed = %lu, %lu", sblock->s_free_inodes_count, desc_count, bitmap_count); spin_unlock (&global_lock); return desc_count; @@ -364,7 +372,7 @@ ext2_count_free_inodes () /* ---------------------------------------------------------------- */ -void +void ext2_check_inodes_bitmap () { int i; diff --git a/ext2fs/inode.c b/ext2fs/inode.c index 7704edbd..f25cc1fa 100644 --- a/ext2fs/inode.c +++ b/ext2fs/inode.c @@ -1,8 +1,9 @@ /* Inode management routines - Copyright (C) 1994, 1995, 1996 Free Software Foundation, Inc. + Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2007 + Free Software Foundation, Inc. - Converted for ext2fs by Miles Bader <miles@gnu.ai.mit.edu> + Converted for ext2fs by Miles Bader <miles@gnu.org> This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as @@ -22,6 +23,21 @@ #include <string.h> #include <unistd.h> #include <stdio.h> +#include <sys/stat.h> +#include <sys/statfs.h> +#include <sys/statvfs.h> + +/* these flags aren't actually defined by a header file yet, so temporarily + disable them if necessary. */ +#ifndef UF_APPEND +#define UF_APPEND 0 +#endif +#ifndef UF_NODUMP +#define UF_NODUMP 0 +#endif +#ifndef UF_IMMUTABLE +#define UF_IMMUTABLE 0 +#endif #define INOHSZ 512 #if ((INOHSZ&(INOHSZ-1)) == 0) @@ -32,7 +48,7 @@ static struct node *nodehash[INOHSZ]; -static error_t read_disknode (struct node *np); +static error_t read_node (struct node *np); spin_lock_t generation_lock = SPIN_LOCK_INITIALIZER; @@ -45,10 +61,10 @@ inode_init () nodehash[n] = 0; } -/* Fetch inode INUM, set *NPP to the node structure; +/* Fetch inode INUM, set *NPP to the node structure; gain one user reference and lock the node. */ -error_t -diskfs_cached_lookup (int inum, struct node **npp) +error_t +diskfs_cached_lookup (ino_t inum, struct node **npp) { error_t err; struct node *np; @@ -64,7 +80,7 @@ diskfs_cached_lookup (int inum, struct node **npp) *npp = np; return 0; } - + /* Format specific data for the new node. */ dn = malloc (sizeof (struct disknode)); if (! dn) @@ -73,9 +89,10 @@ diskfs_cached_lookup (int inum, struct node **npp) return ENOMEM; } dn->dirents = 0; + dn->dir_idx = 0; dn->pager = 0; rwlock_init (&dn->alloc_lock); - pokel_init (&dn->indir_pokel, disk_pager, disk_image); + pokel_init (&dn->indir_pokel, diskfs_disk_pager, disk_image); /* Create the new node. */ np = diskfs_make_node (dn); @@ -91,10 +108,10 @@ diskfs_cached_lookup (int inum, struct node **npp) nodehash[INOHASH(inum)] = np; spin_unlock (&diskfs_node_refcnt_lock); - + /* Get the contents of NP off disk. */ - err = read_disknode (np); - + err = read_node (np); + if (!diskfs_check_readonly () && !np->dn_stat.st_gen) { spin_lock (&generation_lock); @@ -104,7 +121,7 @@ diskfs_cached_lookup (int inum, struct node **npp) spin_unlock (&generation_lock); np->dn_set_ctime = 1; } - + if (err) return err; else @@ -120,13 +137,13 @@ struct node * ifind (ino_t inum) { struct node *np; - + spin_lock (&diskfs_node_refcnt_lock); for (np = nodehash[INOHASH(inum)]; np; np = np->dn->hnext) { if (np->cache_id != inum) continue; - + assert (np->references); spin_unlock (&diskfs_node_refcnt_lock); return np; @@ -136,7 +153,7 @@ ifind (ino_t inum) /* The last reference to a node has gone away; drop it from the hash table and clean all state in the dn structure. */ -void +void diskfs_node_norefs (struct node *np) { *np->dn->hprevp = np->dn->hnext; @@ -179,65 +196,77 @@ diskfs_new_hardrefs (struct node *np) /* Read stat information out of the ext2_inode. */ static error_t -read_disknode (struct node *np) +read_node (struct node *np) { error_t err; - unsigned offset; - static int fsid, fsidset; struct stat *st = &np->dn_stat; struct disknode *dn = np->dn; struct ext2_inode *di = dino (np->cache_id); struct ext2_inode_info *info = &dn->info; - + err = diskfs_catch_exception (); if (err) return err; - np->istranslated = sblock->s_creator_os == EXT2_OS_HURD && di->i_translator; - - if (!fsidset) - { - fsid = getpid (); - fsidset = 1; - } - st->st_fstype = FSTYPE_EXT2FS; - st->st_fsid = fsid; + st->st_fsid = getpid (); /* This call is very cheap. */ st->st_ino = np->cache_id; st->st_blksize = vm_page_size * 2; st->st_nlink = di->i_links_count; st->st_size = di->i_size; - st->st_gen = di->i_version; + st->st_gen = di->i_generation; - st->st_atime = di->i_atime; - st->st_mtime = di->i_mtime; - st->st_ctime = di->i_ctime; - -#ifdef XXX - st->st_atime_usec = di->i_atime.ts_nsec / 1000; - st->st_mtime_usec = di->i_mtime.ts_nsec / 1000; - st->st_ctime_usec = di->i_ctime.ts_nsec / 1000; + st->st_atim.tv_sec = di->i_atime; +#ifdef not_yet + /* ``struct ext2_inode'' doesn't do better than sec. precision yet. */ +#else + st->st_atim.tv_nsec = 0; +#endif + st->st_mtim.tv_sec = di->i_mtime; +#ifdef not_yet + /* ``struct ext2_inode'' doesn't do better than sec. precision yet. */ +#else + st->st_mtim.tv_nsec = 0; +#endif + st->st_ctim.tv_sec = di->i_ctime; +#ifdef not_yet + /* ``struct ext2_inode'' doesn't do better than sec. precision yet. */ +#else + st->st_ctim.tv_nsec = 0; #endif st->st_blocks = di->i_blocks; - st->st_flags = di->i_flags; - + + st->st_flags = 0; + if (di->i_flags & EXT2_APPEND_FL) + st->st_flags |= UF_APPEND; + if (di->i_flags & EXT2_NODUMP_FL) + st->st_flags |= UF_NODUMP; + if (di->i_flags & EXT2_IMMUTABLE_FL) + st->st_flags |= UF_IMMUTABLE; + if (sblock->s_creator_os == EXT2_OS_HURD) { st->st_mode = di->i_mode | (di->i_mode_high << 16); + st->st_mode &= ~S_ITRANS; + if (di->i_translator) + st->st_mode |= S_IPTRANS; + st->st_uid = di->i_uid | (di->i_uid_high << 16); st->st_gid = di->i_gid | (di->i_gid_high << 16); + st->st_author = di->i_author; if (st->st_author == -1) st->st_author = st->st_uid; } else { - st->st_mode = di->i_mode; + st->st_mode = di->i_mode & ~S_ITRANS; st->st_uid = di->i_uid; st->st_gid = di->i_gid; st->st_author = st->st_uid; + np->author_tracks_uid = 1; } /* Setup the ext2fs auxiliary inode info. */ @@ -248,32 +277,57 @@ read_disknode (struct node *np) info->i_frag_size = di->i_fsize; info->i_osync = 0; info->i_file_acl = di->i_file_acl; - info->i_dir_acl = di->i_dir_acl; - info->i_version = di->i_version; + if (S_ISDIR (st->st_mode)) + info->i_dir_acl = di->i_dir_acl; + else + { + info->i_dir_acl = 0; + info->i_high_size = di->i_size_high; + if (info->i_high_size) /* XXX */ + { + ext2_warning ("cannot handle large file inode %Ld", np->cache_id); + return EFBIG; + } + } info->i_block_group = inode_group_num (np->cache_id); info->i_next_alloc_block = 0; info->i_next_alloc_goal = 0; info->i_prealloc_count = 0; - if (S_ISCHR(st->st_mode) || S_ISBLK(st->st_mode)) + /* Set to a conservative value. */ + dn->last_page_partially_writable = 0; + + if (S_ISCHR (st->st_mode) || S_ISBLK (st->st_mode)) st->st_rdev = di->i_block[0]; else { - int block; - for (block = 0; block < EXT2_N_BLOCKS; block++) - info->i_data[block] = di->i_block[block]; + memcpy (info->i_data, di->i_block, + EXT2_N_BLOCKS * sizeof info->i_data[0]); st->st_rdev = 0; } + dn->info_i_translator = di->i_translator; diskfs_end_catch_exception (); - /* Set these to conservative values. */ - dn->last_page_partially_writable = 0; - - np->allocsize = np->dn_stat.st_size; - offset = np->allocsize & ((1 << log2_block_size) - 1); - if (offset > 0) - np->allocsize += block_size - offset; + if (S_ISREG (st->st_mode) || S_ISDIR (st->st_mode) + || (S_ISLNK (st->st_mode) && st->st_blocks)) + { + unsigned offset; + + np->allocsize = np->dn_stat.st_size; + + /* Round up to a block multiple. */ + offset = np->allocsize & ((1 << log2_block_size) - 1); + if (offset > 0) + np->allocsize += block_size - offset; + } + else + /* Allocsize should be zero for anything except directories, files, and + long symlinks. These are the only things allowed to have any blocks + allocated as well, although st_size may be zero for any type (cases + where st_blocks=0 and st_size>0 include fast symlinks, and, under + linux, some devices). */ + np->allocsize = 0; return 0; } @@ -285,8 +339,17 @@ check_high_bits (struct node *np, long l) { if (sblock->s_creator_os == EXT2_OS_HURD) return 0; - else - return ((l & ~0xFFFF) == 0) ? 0 : EINVAL; + + /* Linux 2.3.42 has a mount-time option (not a bit stored on disk) + NO_UID32 to ignore the high 16 bits of uid and gid, but by default + allows them. It also does this check for "interoperability with old + kernels". Note that our check refuses to change the values, while + Linux 2.3.42 just silently clears the high bits in an inode it updates, + even if it was updating it for an unrelated reason. */ + if (np->dn->info.i_dtime != 0) + return 0; + + return ((l & ~0xFFFF) == 0) ? 0 : EINVAL; } /* Return 0 if NP's owner can be changed to UID; otherwise return an error @@ -322,10 +385,22 @@ diskfs_validate_author_change (struct node *np, uid_t author) if (sblock->s_creator_os == EXT2_OS_HURD) return 0; else - /* For non-hurd filesystems, the auther & owner are the same. */ + /* For non-hurd filesystems, the author & owner are the same. */ return (author == np->dn_stat.st_uid) ? 0 : EINVAL; } +/* The user may define this function. Return 0 if NP's flags can be + changed to FLAGS; otherwise return an error code. It must always + be possible to clear the flags. */ +error_t +diskfs_validate_flags_change (struct node *np, int flags) +{ + if (flags & ~(UF_NODUMP | UF_IMMUTABLE | UF_APPEND)) + return EINVAL; + else + return 0; +} + /* Writes everything from NP's inode to the disk image, and returns a pointer to it, or NULL if nothing need be done. */ static struct ext2_inode * @@ -337,10 +412,11 @@ write_node (struct node *np) if (np->dn->info.i_prealloc_count) ext2_discard_prealloc (np); - - assert (!np->dn_set_ctime && !np->dn_set_atime && !np->dn_set_mtime); + if (np->dn_stat_dirty) { + struct ext2_inode_info *info = &np->dn->info; + assert (!diskfs_readonly); ext2_debug ("writing inode %d to disk", np->cache_id); @@ -348,8 +424,8 @@ write_node (struct node *np) err = diskfs_catch_exception (); if (err) return NULL; - - di->i_version = st->st_gen; + + di->i_generation = st->st_gen; /* We happen to know that the stat mode bits are the same as the ext2fs mode bits. */ @@ -357,14 +433,14 @@ write_node (struct node *np) /* Only the low 16 bits of these fields are standard across all ext2 implementations. */ - di->i_mode = st->st_mode & 0xFFFF; + di->i_mode = st->st_mode & 0xFFFF & ~S_ITRANS; di->i_uid = st->st_uid & 0xFFFF; di->i_gid = st->st_gid & 0xFFFF; if (sblock->s_creator_os == EXT2_OS_HURD) /* If this is a hurd-compatible filesystem, write the high bits too. */ { - di->i_mode_high = (st->st_mode >> 16) & 0xffff; + di->i_mode_high = (st->st_mode >> 16) & 0xffff & ~S_ITRANS; di->i_uid_high = st->st_uid >> 16; di->i_gid_high = st->st_gid >> 16; di->i_author = st->st_author; @@ -375,39 +451,57 @@ write_node (struct node *np) assert ((st->st_uid & ~0xFFFF) == 0); assert ((st->st_gid & ~0xFFFF) == 0); assert ((st->st_mode & ~0xFFFF) == 0); - assert (st->st_author == st->st_uid); + assert (np->author_tracks_uid && st->st_author == st->st_uid); } di->i_links_count = st->st_nlink; - di->i_size = st->st_size; - - di->i_atime = st->st_atime; - di->i_mtime = st->st_mtime; - di->i_ctime = st->st_ctime; -#ifdef XXX - di->i_atime.ts_nsec = st->st_atime_usec * 1000; - di->i_mtime.ts_nsec = st->st_mtime_usec * 1000; - di->i_ctime.ts_nsec = st->st_ctime_usec * 1000; -#endif - di->i_blocks = st->st_blocks; - di->i_flags = st->st_flags; - - if (!np->istranslated && sblock->s_creator_os == EXT2_OS_HURD) - di->i_translator = 0; + di->i_atime = st->st_atim.tv_sec; +#ifdef not_yet + /* ``struct ext2_inode'' doesn't do better than sec. precision yet. */ + di->i_atime.tv_nsec = st->st_atim.tv_nsec; +#endif + di->i_mtime = st->st_mtim.tv_sec; +#ifdef not_yet + di->i_mtime.tv_nsec = st->st_mtim.tv_nsec; +#endif + di->i_ctime = st->st_ctim.tv_sec; +#ifdef not_yet + di->i_ctime.tv_nsec = st->st_ctim.tv_nsec; +#endif - /* Set dtime non-zero to indicate a deleted file. */ - di->i_dtime = (st->st_mode ? 0 : di->i_mtime); + /* Convert generic flags in ST->st_flags to ext2-specific flags in DI + (but don't mess with ext2 flags we don't know about). The original + set was copied from DI into INFO by read_node, but might have been + modified for ext2fs-specific reasons; so we use INFO->i_flags + to start with, and then apply the flags in ST->st_flags. */ + info->i_flags &= ~(EXT2_APPEND_FL | EXT2_NODUMP_FL | EXT2_IMMUTABLE_FL); + if (st->st_flags & UF_APPEND) + info->i_flags |= EXT2_APPEND_FL; + if (st->st_flags & UF_NODUMP) + info->i_flags |= EXT2_NODUMP_FL; + if (st->st_flags & UF_IMMUTABLE) + info->i_flags |= EXT2_IMMUTABLE_FL; + di->i_flags = info->i_flags; + + if (st->st_mode == 0) + /* Set dtime non-zero to indicate a deleted file. + We don't clear i_size, i_blocks, and i_translator in this case, + to give "undeletion" utilities a chance. */ + di->i_dtime = di->i_mtime; + else + { + di->i_dtime = 0; + di->i_size = st->st_size; + di->i_blocks = st->st_blocks; + } if (S_ISCHR(st->st_mode) || S_ISBLK(st->st_mode)) di->i_block[0] = st->st_rdev; else - { - int block; - for (block = 0; block < EXT2_N_BLOCKS; block++) - di->i_block[block] = np->dn->info.i_data[block]; - } - + memcpy (di->i_block, np->dn->info.i_data, + EXT2_N_BLOCKS * sizeof di->i_block[0]); + diskfs_end_catch_exception (); np->dn_stat_dirty = 0; @@ -431,7 +525,7 @@ diskfs_node_reload (struct node *node) } pokel_flush (&dn->indir_pokel); flush_node_pager (node); - read_disknode (node); + read_node (node); return 0; } @@ -445,7 +539,7 @@ diskfs_node_iterate (error_t (*fun)(struct node *)) error_t err = 0; int n, num_nodes = 0; struct node *node, **node_list, **p; - + spin_lock (&diskfs_node_refcnt_lock); /* We must copy everything from the hash table into another data structure @@ -493,12 +587,12 @@ write_all_disknodes () { struct ext2_inode *di; - diskfs_set_node_times (node); - /* Sync the indirect blocks here; they'll all be done before any inodes. Waiting for them shouldn't be too bad. */ pokel_sync (&node->dn->indir_pokel, 1); + diskfs_set_node_times (node); + /* Update the inode image. */ di = write_node (node); if (di) @@ -518,10 +612,12 @@ diskfs_write_disknode (struct node *np, int wait) { struct ext2_inode *di = write_node (np); if (di) - if (wait) - sync_global_ptr (di, 1); - else - record_global_poke (di); + { + if (wait) + sync_global_ptr (di, 1); + else + record_global_poke (di); + } } /* Set *ST with appropriate values to reflect the current state of the @@ -534,17 +630,21 @@ diskfs_set_statfs (struct statfs *st) st->f_blocks = sblock->s_blocks_count; st->f_bfree = sblock->s_free_blocks_count; st->f_bavail = st->f_bfree - sblock->s_r_blocks_count; + if (st->f_bfree < sblock->s_r_blocks_count) + st->f_bavail = 0; st->f_files = sblock->s_inodes_count; st->f_ffree = sblock->s_free_inodes_count; st->f_fsid = getpid (); st->f_namelen = 0; + st->f_favail = st->f_ffree; + st->f_frsize = frag_size; return 0; } - + /* Implement the diskfs_set_translator callback from the diskfs library; see <hurd/diskfs.h> for the interface description. */ error_t -diskfs_set_translator (struct node *np, char *name, unsigned namelen, +diskfs_set_translator (struct node *np, const char *name, unsigned namelen, struct protid *cred) { daddr_t blkno; @@ -563,10 +663,10 @@ diskfs_set_translator (struct node *np, char *name, unsigned namelen, err = diskfs_catch_exception (); if (err) return err; - + di = dino (np->cache_id); blkno = di->i_translator; - + if (namelen && !blkno) { /* Allocate block for translator */ @@ -574,14 +674,15 @@ diskfs_set_translator (struct node *np, char *name, unsigned namelen, ext2_new_block ((np->dn->info.i_block_group * EXT2_BLOCKS_PER_GROUP (sblock)) + sblock->s_first_data_block, - 0, 0); + 0, 0, 0); if (blkno == 0) { diskfs_end_catch_exception (); return ENOSPC; } - + di->i_translator = blkno; + np->dn->info_i_translator = blkno; record_global_poke (di); np->dn_stat.st_blocks += 1 << log2_stat_blocks_per_fs_block; @@ -591,14 +692,15 @@ diskfs_set_translator (struct node *np, char *name, unsigned namelen, { /* Clear block for translator going away. */ di->i_translator = 0; + np->dn->info_i_translator = 0; record_global_poke (di); ext2_free_blocks (blkno, 1); np->dn_stat.st_blocks -= 1 << log2_stat_blocks_per_fs_block; - np->istranslated = 0; + np->dn_stat.st_mode &= ~S_IPTRANS; np->dn_set_ctime = 1; } - + if (namelen) { buf[0] = namelen & 0xFF; @@ -608,10 +710,10 @@ diskfs_set_translator (struct node *np, char *name, unsigned namelen, bcopy (buf, bptr (blkno), block_size); record_global_poke (bptr (blkno)); - np->istranslated = 1; + np->dn_stat.st_mode |= S_IPTRANS; np->dn_set_ctime = 1; } - + diskfs_end_catch_exception (); return err; } @@ -621,10 +723,10 @@ diskfs_set_translator (struct node *np, char *name, unsigned namelen, error_t diskfs_get_translator (struct node *np, char **namep, unsigned *namelen) { - error_t err; + error_t err = 0; daddr_t blkno; unsigned datalen; - void *transloc; + const void *transloc; assert (sblock->s_creator_os == EXT2_OS_HURD); @@ -635,18 +737,76 @@ diskfs_get_translator (struct node *np, char **namep, unsigned *namelen) blkno = (dino (np->cache_id))->i_translator; assert (blkno); transloc = bptr (blkno); - + datalen = ((unsigned char *)transloc)[0] + (((unsigned char *)transloc)[1] << 8); - *namep = malloc (datalen); - bcopy (transloc + 2, *namep, datalen); + if (datalen > block_size - 2) + err = EFTYPE; /* ? */ + else + { + *namep = malloc (datalen); + if (!*namep) + err = ENOMEM; + else + memcpy (*namep, transloc + 2, datalen); + } diskfs_end_catch_exception (); *namelen = datalen; + return err; +} + +/* The maximum size of a symlink store in the inode (including '\0'). */ +#define MAX_INODE_SYMLINK \ + (EXT2_N_BLOCKS * sizeof (((struct ext2_inode *)0)->i_block[0])) + +/* Write an in-inode symlink, or return EINVAL if we can't. */ +static error_t +write_symlink (struct node *node, const char *target) +{ + size_t len = strlen (target) + 1; + + if (len > MAX_INODE_SYMLINK) + return EINVAL; + + assert (node->dn_stat.st_blocks == 0); + + bcopy (target, node->dn->info.i_data, len); + node->dn_stat.st_size = len - 1; + node->dn_set_ctime = 1; + node->dn_set_mtime = 1; + return 0; } +/* Read an in-inode symlink, or return EINVAL if we can't. */ +static error_t +read_symlink (struct node *node, char *target) +{ + if (node->dn_stat.st_blocks) + return EINVAL; + + assert (node->dn_stat.st_size < MAX_INODE_SYMLINK); + + bcopy (node->dn->info.i_data, target, node->dn_stat.st_size); + return 0; +} + +/* If this function is nonzero (and diskfs_shortcut_symlink is set) it + is called to set a symlink. If it returns EINVAL or isn't set, + then the normal method (writing the contents into the file data) is + used. If it returns any other error, it is returned to the user. */ +error_t (*diskfs_create_symlink_hook)(struct node *np, const char *target) = + write_symlink; + +/* If this function is nonzero (and diskfs_shortcut_symlink is set) it + is called to read the contents of a symlink. If it returns EINVAL or + isn't set, then the normal method (reading from the file data) is + used. If it returns any other error, it is returned to the user. */ +error_t (*diskfs_read_symlink_hook)(struct node *np, char *target) = + read_symlink; + /* Called when all hard ports have gone away. */ void diskfs_shutdown_soft_ports () diff --git a/ext2fs/msg.c b/ext2fs/msg.c index ba295070..727d926d 100644 --- a/ext2fs/msg.c +++ b/ext2fs/msg.c @@ -49,8 +49,7 @@ void _ext2_error (const char * function, const char * fmt, ...) vsprintf (error_buf, fmt, args); va_end (args); - fprintf (stderr, "ext2fs: %s: %s: %s\n", - diskfs_device_arg, function, error_buf); + fprintf (stderr, "ext2fs: %s: %s: %s\n", diskfs_disk_name, function, error_buf); mutex_unlock(&printf_lock); } @@ -66,7 +65,7 @@ void _ext2_panic (const char * function, const char * fmt, ...) va_end (args); fprintf(stderr, "ext2fs: %s: panic: %s: %s\n", - diskfs_device_arg, function, error_buf); + diskfs_disk_name, function, error_buf); mutex_unlock(&printf_lock); @@ -83,7 +82,7 @@ void ext2_warning (const char * fmt, ...) vsprintf (error_buf, fmt, args); va_end (args); - fprintf (stderr, "ext2fs: %s: warning: %s\n", diskfs_device_arg, error_buf); + fprintf (stderr, "ext2fs: %s: warning: %s\n", diskfs_disk_name, error_buf); mutex_unlock(&printf_lock); } diff --git a/ext2fs/pager.c b/ext2fs/pager.c index dfc43a9f..0136f9b1 100644 --- a/ext2fs/pager.c +++ b/ext2fs/pager.c @@ -1,8 +1,8 @@ /* Pager for ext2fs - Copyright (C) 1994, 1995, 1996 Free Software Foundation, Inc. + Copyright (C) 1994,95,96,97,98,99,2000,02 Free Software Foundation, Inc. - Converted for ext2fs by Miles Bader <miles@gnu.ai.mit.edu> + Converted for ext2fs by Miles Bader <miles@gnu.org> This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as @@ -18,12 +18,17 @@ along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ -#include <strings.h> +#include <string.h> +#include <errno.h> +#include <hurd/store.h> #include "ext2fs.h" /* A ports bucket to hold pager ports. */ struct port_bucket *pager_bucket; +/* Mapped image of the disk. */ +void *disk_image; + spin_lock_t node_to_page_lock = SPIN_LOCK_INITIALIZER; #ifdef DONT_CACHE_MEMORY_OBJECTS @@ -32,13 +37,91 @@ spin_lock_t node_to_page_lock = SPIN_LOCK_INITIALIZER; #define MAY_CACHE 1 #endif -/* ---------------------------------------------------------------- */ +#define STATS + +#ifdef STATS +struct ext2fs_pager_stats +{ + spin_lock_t lock; + + unsigned long disk_pageins; + unsigned long disk_pageouts; + + unsigned long file_pageins; + unsigned long file_pagein_reads; /* Device reads done by file pagein */ + unsigned long file_pagein_freed_bufs; /* Discarded pages */ + unsigned long file_pagein_alloced_bufs; /* Allocated pages */ + + unsigned long file_pageouts; + + unsigned long file_page_unlocks; + unsigned long file_grows; +}; + +static struct ext2fs_pager_stats ext2s_pager_stats; + +#define STAT_INC(field) \ +do { spin_lock (&ext2s_pager_stats.lock); \ + ext2s_pager_stats.field++; \ + spin_unlock (&ext2s_pager_stats.lock); } while (0) +#else /* !STATS */ +#define STAT_INC(field) /* nop */0 +#endif /* STATS */ + +#define FREE_PAGE_BUFS 24 + +/* Returns a single page page-aligned buffer. */ +static void * +get_page_buf () +{ + static struct mutex free_page_bufs_lock = MUTEX_INITIALIZER; + static void *free_page_bufs; + static int num_free_page_bufs; + void *buf; + + mutex_lock (&free_page_bufs_lock); + if (num_free_page_bufs > 0) + { + buf = free_page_bufs; + num_free_page_bufs --; + if (num_free_page_bufs > 0) + free_page_bufs += vm_page_size; +#ifndef NDEBUG + else + free_page_bufs = 0; +#endif /* ! NDEBUG */ + } + else + { + assert (free_page_bufs == 0); + buf = mmap (0, vm_page_size * FREE_PAGE_BUFS, + PROT_READ|PROT_WRITE, MAP_ANON, 0, 0); + if (buf == MAP_FAILED) + buf = 0; + else + { + free_page_bufs = buf + vm_page_size; + num_free_page_bufs = FREE_PAGE_BUFS - 1; + } + } + + mutex_unlock (&free_page_bufs_lock); + return buf; +} + +/* Frees a block returned by get_page_buf. */ +static inline void +free_page_buf (void *buf) +{ + munmap (buf, vm_page_size); +} + /* Find the location on disk of page OFFSET in NODE. Return the disk block - in BLOCK (if unallocated, then return 0). If *LOCK is 0, then it a reader - lock is aquired on NODE's ALLOC_LOCK before doing anything, and left - locked after return -- even if an error is returned. 0 on success or an - error code otherwise is returned. */ + in BLOCK (if unallocated, then return 0). If *LOCK is 0, then a reader + lock is acquired on NODE's ALLOC_LOCK before doing anything, and left + locked after the return -- even if an error is returned. 0 is returned + on success otherwise an error code. */ static error_t find_block (struct node *node, vm_offset_t offset, block_t *block, struct rwlock **lock) @@ -65,14 +148,12 @@ find_block (struct node *node, vm_offset_t offset, return err; } -/* ---------------------------------------------------------------- */ - /* Read one page for the pager backing NODE at offset PAGE, into BUF. This may need to read several filesystem blocks to satisfy one page, and tries to consolidate the i/o if possible. */ static error_t file_pager_read_page (struct node *node, vm_offset_t page, - vm_address_t *buf, int *writelock) + void **buf, int *writelock) { error_t err; int offs = 0; @@ -91,30 +172,47 @@ file_pager_read_page (struct node *node, vm_offset_t page, if (num_pending_blocks > 0) { block_t dev_block = pending_blocks << log2_dev_blocks_per_fs_block; - int length = num_pending_blocks << log2_block_size; - vm_address_t new_buf; + size_t amount = num_pending_blocks << log2_block_size; + /* The buffer we try to read into; on the first read, we pass in a + size of zero, so that the read is guaranteed to allocate a new + buffer, otherwise, we try to read directly into the tail of the + buffer we've already got. */ + void *new_buf = *buf + offs; + size_t new_len = offs == 0 ? 0 : vm_page_size - offs; + + STAT_INC (file_pagein_reads); - err = diskfs_device_read_sync (dev_block, &new_buf, length); + err = store_read (store, dev_block, amount, &new_buf, &new_len); if (err) return err; + else if (amount != new_len) + return EIO; - if (offs == 0) - /* First read, make the returned page be our buffer. */ - *buf = new_buf; - else + if (new_buf != *buf + offs) { - /* We've already got some buffer, so copy into it. */ - bcopy ((char *)new_buf, (char *)*buf + offs, length); - vm_deallocate (mach_task_self (), new_buf, length); + /* The read went into a different buffer than the one we + passed. */ + if (offs == 0) + /* First read, make the returned page be our buffer. */ + *buf = new_buf; + else + /* We've already got some buffer, so copy into it. */ + { + bcopy (new_buf, *buf + offs, new_len); + free_page_buf (new_buf); /* Return NEW_BUF to our pool. */ + STAT_INC (file_pagein_freed_bufs); + } } - offs += length; + offs += new_len; num_pending_blocks = 0; } return 0; } + STAT_INC (file_pageins); + *writelock = 0; if (page >= node->allocsize) @@ -151,11 +249,12 @@ file_pager_read_page (struct node *node, vm_offset_t page, if (offs == 0) /* No page allocated to read into yet. */ { - err = vm_allocate (mach_task_self (), buf, vm_page_size, 1); - if (err) + *buf = get_page_buf (); + if (! *buf) break; + STAT_INC (file_pagein_alloced_bufs); } - bzero ((char *)*buf + offs, block_size); + bzero (*buf + offs, block_size); offs += block_size; } else @@ -177,16 +276,14 @@ file_pager_read_page (struct node *node, vm_offset_t page, return err; } -/* ---------------------------------------------------------------- */ - struct pending_blocks { /* The block number of the first of the blocks. */ block_t block; /* How many blocks we have. */ - int num; + off_t num; /* A (page-aligned) buffer pointing to the data we're dealing with. */ - vm_address_t buf; + void *buf; /* And an offset into BUF. */ int offs; }; @@ -199,22 +296,24 @@ pending_blocks_write (struct pending_blocks *pb) { error_t err; block_t dev_block = pb->block << log2_dev_blocks_per_fs_block; - int length = pb->num << log2_block_size; + size_t length = pb->num << log2_block_size, amount; - ext2_debug ("writing block %lu[%d]", pb->block, pb->num); + ext2_debug ("writing block %u[%ld]", pb->block, pb->num); if (pb->offs > 0) /* Put what we're going to write into a page-aligned buffer. */ { - vm_address_t page_buf = get_page_buf (); - bcopy ((char *)pb->buf + pb->offs, (void *)page_buf, length); - err = diskfs_device_write_sync (dev_block, page_buf, length); + void *page_buf = get_page_buf (); + bcopy (pb->buf + pb->offs, (void *)page_buf, length); + err = store_write (store, dev_block, page_buf, length, &amount); free_page_buf (page_buf); } else - err = diskfs_device_write_sync (dev_block, pb->buf, length); + err = store_write (store, dev_block, pb->buf, length, &amount); if (err) return err; + else if (amount != length) + return EIO; pb->offs += length; pb->num = 0; @@ -224,7 +323,7 @@ pending_blocks_write (struct pending_blocks *pb) } static void -pending_blocks_init (struct pending_blocks *pb, vm_address_t buf) +pending_blocks_init (struct pending_blocks *pb, void *buf) { pb->buf = buf; pb->block = 0; @@ -258,32 +357,34 @@ pending_blocks_add (struct pending_blocks *pb, block_t block) return 0; } -/* ---------------------------------------------------------------- */ - /* Write one page for the pager backing NODE, at offset PAGE, into BUF. This may need to write several filesystem blocks to satisfy one page, and tries to consolidate the i/o if possible. */ static error_t -file_pager_write_page (struct node *node, vm_offset_t offset, vm_address_t buf) +file_pager_write_page (struct node *node, vm_offset_t offset, void *buf) { error_t err = 0; struct pending_blocks pb; - struct rwlock *lock = 0; + struct rwlock *lock = &node->dn->alloc_lock; block_t block; int left = vm_page_size; pending_blocks_init (&pb, buf); + /* Holding NODE->dn->alloc_lock effectively locks NODE->allocsize, + at least for the cases we care about: pager_unlock_page, + diskfs_grow and diskfs_truncate. */ + rwlock_reader_lock (&node->dn->alloc_lock); + if (offset >= node->allocsize) - { - err = EIO; - left = 0; - } + left = 0; else if (offset + left > node->allocsize) left = node->allocsize - offset; ext2_debug ("writing inode %d page %d[%d]", node->cache_id, offset, left); + STAT_INC (file_pageouts); + while (left > 0) { err = find_block (node, offset, &block, &lock); @@ -298,26 +399,24 @@ file_pager_write_page (struct node *node, vm_offset_t offset, vm_address_t buf) if (!err) pending_blocks_write (&pb); - if (lock) - rwlock_reader_unlock (lock); + rwlock_reader_unlock (&node->dn->alloc_lock); return err; } -/* ---------------------------------------------------------------- */ - static error_t -disk_pager_read_page (vm_offset_t page, vm_address_t *buf, int *writelock) +disk_pager_read_page (vm_offset_t page, void **buf, int *writelock) { error_t err; - int length = vm_page_size; - vm_size_t dev_end = diskfs_device_size << diskfs_log2_device_block_size; + size_t length = vm_page_size, read = 0; + vm_size_t dev_end = store->size; if (page + vm_page_size > dev_end) length = dev_end - page; - err = diskfs_device_read_sync (page >> diskfs_log2_device_block_size, - (void *)buf, length); + err = store_read (store, page >> store->log2_block_size, length, buf, &read); + if (read != length) + return EIO; if (!err && length != vm_page_size) bzero ((void *)(*buf + length), vm_page_size - length); @@ -327,17 +426,19 @@ disk_pager_read_page (vm_offset_t page, vm_address_t *buf, int *writelock) } static error_t -disk_pager_write_page (vm_offset_t page, vm_address_t buf) +disk_pager_write_page (vm_offset_t page, void *buf) { error_t err = 0; - int length = vm_page_size; - vm_size_t dev_end = diskfs_device_size << diskfs_log2_device_block_size; + size_t length = vm_page_size, amount; + vm_size_t dev_end = store->size; if (page + vm_page_size > dev_end) length = dev_end - page; ext2_debug ("writing disk page %d[%d]", page, length); + STAT_INC (disk_pageouts); + if (modified_global_blocks) /* Be picky about which blocks in a page that we write. */ { @@ -374,26 +475,27 @@ disk_pager_write_page (vm_offset_t page, vm_address_t buf) err = pending_blocks_write (&pb); } else - err = - diskfs_device_write_sync (page >> diskfs_log2_device_block_size, - buf, length); + { + err = store_write (store, page >> store->log2_block_size, + buf, length, &amount); + if (!err && length != amount) + err = EIO; + } return err; } -/* ---------------------------------------------------------------- */ - /* Satisfy a pager read request for either the disk pager or file pager PAGER, to the page at offset PAGE into BUF. WRITELOCK should be set if the pager should make the page writeable. */ error_t pager_read_page (struct user_pager_info *pager, vm_offset_t page, - vm_address_t *buf, int *writelock) + vm_address_t *buf, int *writelock) { if (pager->type == DISK) - return disk_pager_read_page (page, buf, writelock); + return disk_pager_read_page (page, (void **)buf, writelock); else - return file_pager_read_page (pager->node, page, buf, writelock); + return file_pager_read_page (pager->node, page, (void **)buf, writelock); } /* Satisfy a pager write request for either the disk pager or file pager @@ -403,13 +505,11 @@ pager_write_page (struct user_pager_info *pager, vm_offset_t page, vm_address_t buf) { if (pager->type == DISK) - return disk_pager_write_page (page, buf); + return disk_pager_write_page (page, (void *)buf); else - return file_pager_write_page (pager->node, page, buf); + return file_pager_write_page (pager->node, page, (void *)buf); } -/* ---------------------------------------------------------------- */ - /* Make page PAGE writable, at least up to ALLOCSIZE. This function and diskfs_grow are the only places that blocks are actually added to the file. */ @@ -465,20 +565,20 @@ pager_unlock_page (struct user_pager_info *pager, vm_offset_t page) page, vm_page_size, node->cache_id); #endif + STAT_INC (file_page_unlocks); + rwlock_writer_unlock (&dn->alloc_lock); if (err == ENOSPC) ext2_warning ("This filesystem is out of space, and will now crash. Bye!"); else if (err) - ext2_warning ("inode=%d, page=0x%x: %s", + ext2_warning ("inode=%Ld, page=0x%zx: %s", node->cache_id, page, strerror (err)); return err; } } -/* ---------------------------------------------------------------- */ - /* Grow the disk allocated to locked node NODE to be at least SIZE bytes, and set NODE->allocsize to the actual allocated size. (If the allocated size is already SIZE bytes, do nothing.) CRED identifies the user responsible @@ -530,8 +630,8 @@ diskfs_grow (struct node *node, off_t size, struct protid *cred) ? new_end_block : old_page_end_block); - ext2_debug ("extending writable page %u by %ld blocks" - "; first new block = %lu", + ext2_debug ("extending writable page %u by %d blocks" + "; first new block = %u", trunc_page (old_size), writable_end - end_block, end_block); @@ -544,9 +644,9 @@ diskfs_grow (struct node *node, off_t size, struct protid *cred) } diskfs_end_catch_exception (); - if (err) + if (! err) /* Reflect how much we allocated successfully. */ - new_size = (end_block - 1) << log2_block_size; + new_size = end_block << log2_block_size; else /* See if it's still valid to say this. */ dn->last_page_partially_writable = @@ -554,11 +654,13 @@ diskfs_grow (struct node *node, off_t size, struct protid *cred) } } + STAT_INC (file_grows); + ext2_debug ("new size: %ld%s.", new_size, dn->last_page_partially_writable ? " (last page writable)": ""); if (err) - ext2_warning ("inode=%d, target=%ld: %s", + ext2_warning ("inode=%Ld, target=%Ld: %s", node->cache_id, new_size, strerror (err)); node->allocsize = new_size; @@ -571,8 +673,6 @@ diskfs_grow (struct node *node, off_t size, struct protid *cred) return 0; } -/* ---------------------------------------------------------------- */ - /* This syncs a single file (NODE) to disk. Wait for all I/O to complete if WAIT is set. NODE->lock must be held. */ void @@ -618,8 +718,6 @@ flush_node_pager (struct node *node) } -/* ---------------------------------------------------------------- */ - /* Return in *OFFSET and *SIZE the minimum valid address the pager will accept and the size of the object. */ inline error_t @@ -631,7 +729,7 @@ pager_report_extent (struct user_pager_info *pager, *offset = 0; if (pager->type == DISK) - *size = diskfs_device_size << diskfs_log2_device_block_size; + *size = store->size; else *size = pager->node->allocsize; @@ -667,27 +765,17 @@ pager_dropweak (struct user_pager_info *p __attribute__ ((unused))) { } -/* ---------------------------------------------------------------- */ - -/* A top-level function for the paging thread that just services paging - requests. */ -static void -service_paging_requests (any_t foo __attribute__ ((unused))) -{ - for (;;) - ports_manage_port_operations_multithread (pager_bucket, pager_demuxer, - 1000 * 60 * 2, 1000 * 60 * 10, - 1, MACH_PORT_NULL); -} - /* Create the DISK pager. */ void create_disk_pager (void) { struct user_pager_info *upi = malloc (sizeof (struct user_pager_info)); - + if (!upi) + ext2_panic ("can't create disk pager: %s", strerror (errno)); upi->type = DISK; - disk_pager_setup (upi, MAY_CACHE); + pager_bucket = ports_create_bucket (); + diskfs_start_disk_pager (upi, pager_bucket, MAY_CACHE, store->size, + &disk_image); } /* Call this to create a FILE_DATA pager and return a send right. @@ -723,11 +811,19 @@ diskfs_get_filemap (struct node *node, vm_prot_t prot) malloc (sizeof (struct user_pager_info)); upi->type = FILE_DATA; upi->node = node; - upi->max_prot = 0; + upi->max_prot = prot; diskfs_nref_light (node); node->dn->pager = pager_create (upi, pager_bucket, MAY_CACHE, MEMORY_OBJECT_COPY_DELAY); + if (node->dn->pager == 0) + { + diskfs_nrele_light (node); + free (upi); + spin_unlock (&node_to_page_lock); + return MACH_PORT_NULL; + } + right = pager_get_port (node->dn->pager); ports_port_deref (node->dn->pager); } @@ -755,7 +851,10 @@ drop_pager_softrefs (struct node *node) spin_unlock (&node_to_page_lock); if (MAY_CACHE && pager) - pager_change_attributes (pager, 0, MEMORY_OBJECT_COPY_DELAY, 0); + { + pager_sync (pager, 0); + pager_change_attributes (pager, 0, MEMORY_OBJECT_COPY_DELAY, 0); + } if (pager) ports_port_deref (pager); } @@ -798,7 +897,7 @@ diskfs_shutdown_pager () error_t shutdown_one (void *v_p) { struct pager *p = v_p; - if (p != disk_pager) + if (p != diskfs_disk_pager) pager_shutdown (p); return 0; } @@ -821,7 +920,7 @@ diskfs_sync_everything (int wait) error_t sync_one (void *v_p) { struct pager *p = v_p; - if (p != disk_pager) + if (p != diskfs_disk_pager) pager_sync (p, wait); return 0; } @@ -833,8 +932,6 @@ diskfs_sync_everything (int wait) sync_global (wait); } -/* ---------------------------------------------------------------- */ - static void disable_caching () { diff --git a/ext2fs/storeinfo.c b/ext2fs/storeinfo.c index 419809c9..395ab5ca 100644 --- a/ext2fs/storeinfo.c +++ b/ext2fs/storeinfo.c @@ -19,7 +19,7 @@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include <string.h> -#include <netinet/in.h> /* htonl */ +#include <hurd/store.h> #include "ext2fs.h" @@ -34,142 +34,97 @@ diskfs_S_file_get_storage_info (struct protid *cred, char **data, mach_msg_type_number_t *data_len) { error_t err = 0; - size_t name_len = - (diskfs_device_name && *diskfs_device_name) - ? strlen (diskfs_device_name) + 1 : 0; - /* True when we've allocated memory for the corresponding vector. */ - int al_ports = 0, al_ints = 0, al_offsets = 0, al_data = 0; - - if (! cred) - return EOPNOTSUPP; - -#define ENSURE_MEM(v, vl, alp, num) \ - if (!err && *vl < num) \ - { \ - err = vm_allocate (mach_task_self (), \ - (vm_address_t *)v, num * sizeof (**v), 1); \ - if (! err) \ - { \ - *vl = num; \ - alp = 1; \ - } \ - } - - /* Two longs. */ -#define MISC_LEN (sizeof (long) * 2) - - ENSURE_MEM (ports, num_ports, al_ports, 1); - ENSURE_MEM (ints, num_ints, al_ints, 6); - ENSURE_MEM (data, data_len, al_data, name_len + MISC_LEN); - /* OFFSETS is more complex, and done below. */ - - if (! err) + unsigned num_fs_blocks; + struct store *file_store; + struct store_run *runs, *run = 0; + block_t index = 0; + size_t num_runs = 0, runs_alloced = 10; + struct node *node = cred->po->np; + + runs = malloc (runs_alloced * sizeof (struct store_run)); + if (! runs) + return ENOMEM; + + mutex_lock (&node->lock); + + /* NUM_FS_BLOCKS counts down the blocks in the file that we've not + enumerated yet; when it hits zero, we can stop. */ + if (node->dn_stat.st_size < node->dn_stat.st_blocks * 512) + /* The value indicated by st_blocks is too big (because it includes + indirect blocks), so use the size of the file. */ + num_fs_blocks = + (node->dn_stat.st_size + block_size - 1) >> log2_block_size; + else + num_fs_blocks = node->dn_stat.st_blocks >> log2_stat_blocks_per_fs_block; + + while (num_fs_blocks-- > 0) { - block_t index = 0; - unsigned num_fs_blocks; - off_t *run = *num_offsets ? *offsets : 0; - struct node *node = cred->po->np; - - mutex_lock (&node->lock); - - num_fs_blocks = node->dn_stat.st_blocks >> log2_stat_blocks_per_fs_block; - while (num_fs_blocks > 0) + block_t block; + + err = ext2_getblk (node, index++, 0, &block); + if (err == EINVAL) + /* Either a hole, or past the end of the file. + A hole can't be mapped in runs since we don't know + where the blocks will be allocated, so we can't return the + underlying storage. */ + err = EOPNOTSUPP; + if (err) + break; + + block <<= log2_dev_blocks_per_fs_block; + if (num_runs == 0 + || block != run->start + run->length) /* BLOCK doesn't follow RUN */ + /* Add a new run. */ { - block_t block; - - err = ext2_getblk (node, index++, 0, &block); - if (err == EINVAL) - /* Either a hole, or past the end of the file. */ - { - block = 0; - err = 0; - } - else if (err) - break; - - block <<= log2_dev_blocks_per_fs_block; - if (!run - || ((block && run[0] >= 0) /* Neither is a hole and... */ - ? (block != run[0] + run[1]) /* BLOCK doesn't follow RUN */ - : (block || run[0] >= 0))) /* or one is, but not both */ - /* Add a new run. */ + if (num_runs == runs_alloced) + /* Make some more space in RUNS. */ { - run += 2; - if (!run || run >= *offsets + *num_offsets) - if (al_offsets) - /* We've already allocated space for offsets; add a new - page to the end of it. */ - { - err = - vm_allocate (mach_task_self (), - (vm_address_t *)&run, vm_page_size, 0); - if (err) - break; - *num_offsets += vm_page_size / sizeof (off_t); - } - else - /* We've run out the space passed for inline offsets by - the caller, so allocate our own memory and copy - anything we've already stored. */ - { - off_t *old = *offsets; - size_t old_len = *num_offsets; - err = - vm_allocate (mach_task_self (), - (vm_address_t *)offsets, - old_len * sizeof (off_t) + vm_page_size, 1); - if (err) - break; - if (old_len) - bcopy (old, *offsets, old_len * sizeof (off_t)); - *num_offsets = old_len + vm_page_size / sizeof (off_t); - run = *offsets; - al_offsets = 1; - } - - run[0] = block ?: -1; /* -1 means a hole in OFFSETS */ - run[1] = 0; /* will get extended just below */ + struct store_run *new; + runs_alloced *= 2; + new = realloc (runs, runs_alloced * sizeof (struct store_run)); + if (! new) + { + err = ENOMEM; + break; + } + runs = new; } - /* Increase the size of the current run by one filesystem block. */ - run[1] += 1 << log2_dev_blocks_per_fs_block; - - num_fs_blocks--; + run = runs + num_runs++; + run->start = block; + /* The length will get extended just below. */ + run->length = 0; } - /* Fill in PORTS. Root gets device port, everyone else, nothing. */ - (*ports)[0] = diskfs_isuid (0, cred) ? diskfs_device : MACH_PORT_NULL; - *ports_type = MACH_MSG_TYPE_COPY_SEND; - - /* Fill in INTS. */ - (*ints)[0] = STORAGE_DEVICE; /* type */ - (*ints)[1] = 0; /* flags */ - (*ints)[2] = diskfs_device_block_size; /* block size */ - (*ints)[3] = (run - *offsets) / 2; /* num runs */ - (*ints)[4] = name_len; - (*ints)[5] = MISC_LEN; - - /* Fill in DATA. */ - if (name_len) - strcpy (*data, diskfs_device_name); - /* The following must be kept in sync with MISC_LEN. */ - ((long *)(*data + name_len))[0] = htonl (node->cache_id); - ((long *)(*data + name_len))[1] = - htonl (dino (node->cache_id)->i_translator); - - mutex_unlock (&node->lock); + /* Increase the size of the current run by one filesystem block. */ + run->length += 1 << log2_dev_blocks_per_fs_block; } - if (err) + mutex_unlock (&node->lock); + + if (! err) + err = store_clone (store, &file_store); + if (! err) { -#define DISCARD_MEM(v, vl, alp) \ - if (alp) \ - vm_deallocate (mach_task_self (), (vm_address_t)*v, *vl * sizeof **v); - DISCARD_MEM (ports, num_ports, al_ports); - DISCARD_MEM (ints, num_ints, al_ints); - DISCARD_MEM (offsets, num_offsets, al_offsets); - DISCARD_MEM (data, data_len, al_data); + err = store_remap (file_store, runs, num_runs, &file_store); + if (!err + && !idvec_contains (cred->user->uids, 0) + && !store_is_securely_returnable (file_store, cred->po->openstat)) + { + err = store_set_flags (file_store, STORE_INACTIVE); + if (err == EINVAL) + err = EACCES; + } + if (! err) + { + *ports_type = MACH_MSG_TYPE_COPY_SEND; + err = store_return (file_store, ports, num_ports, ints, num_ints, + offsets, num_offsets, data, data_len); + } + store_free (file_store); } + free (runs); + return err; } diff --git a/ext2fs/truncate.c b/ext2fs/truncate.c index ebe8f374..077225b0 100644 --- a/ext2fs/truncate.c +++ b/ext2fs/truncate.c @@ -1,8 +1,8 @@ /* File truncation - Copyright (C) 1995, 1996 Free Software Foundation, Inc. + Copyright (C) 1995,96,97,99,2000 Free Software Foundation, Inc. - Written by Miles Bader <miles@gnu.ai.mit.edu> + Written by Miles Bader <miles@gnu.org> This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as @@ -102,7 +102,7 @@ trunc_direct (struct node *node, block_t end, struct free_block_run *fbr) { block_t *blocks = node->dn->info.i_data; - ext2_debug ("truncating direct blocks from %ld", end); + ext2_debug ("truncating direct blocks from %d", end); while (end < EXT2_NDIR_BLOCKS) free_block_run_free_ptr (fbr, blocks + end++); @@ -138,7 +138,10 @@ trunc_indirect (struct node *node, block_t end, } if (first == 0 && all_freed) - free_block_run_free_ptr (fbr, p); + { + pager_flush_some (diskfs_disk_pager, boffs (*p), block_size, 1); + free_block_run_free_ptr (fbr, p); + } else if (modified) record_indir_poke (node, ind_bh); } @@ -205,7 +208,7 @@ poke_pages (memory_object_t obj, vm_offset_t start, vm_offset_t end) vm_address_t poke; for (poke = addr; poke < addr + len; poke += vm_page_size) *(volatile int *)poke = *(volatile int *)poke; - vm_deallocate (mach_task_self (), addr, len); + munmap ((caddr_t) addr, len); } start += len; @@ -226,16 +229,22 @@ force_delayed_copies (struct node *node, off_t length) if (pager) ports_port_ref (pager); spin_unlock (&node_to_page_lock); - + if (pager) { mach_port_t obj; - + pager_change_attributes (pager, MAY_CACHE, MEMORY_OBJECT_COPY_NONE, 1); obj = diskfs_get_filemap (node, VM_PROT_READ); - poke_pages (obj, round_page (length), round_page (node->allocsize)); - mach_port_deallocate (mach_task_self (), obj); - pager_flush_some (pager, round_page(length), node->allocsize - length, 1); + if (obj != MACH_PORT_NULL) + { + /* XXX should cope with errors from diskfs_get_filemap */ + poke_pages (obj, round_page (length), round_page (node->allocsize)); + mach_port_deallocate (mach_task_self (), obj); + pager_flush_some (pager, round_page(length), + node->allocsize - length, 1); + } + ports_port_deref (pager); } } @@ -263,7 +272,7 @@ enable_delayed_copies (struct node *node) /* The user must define this function. Truncate locked node NODE to be SIZE bytes long. (If NODE is already less than or equal to SIZE bytes long, do nothing.) If this is a symlink (and diskfs_shortcut_symlink - is set) then this should clear the symlink, even if + is set) then this should clear the symlink, even if diskfs_create_symlink_hook stores the link target elsewhere. */ error_t diskfs_truncate (struct node *node, off_t length) @@ -277,13 +286,25 @@ diskfs_truncate (struct node *node, off_t length) if (length >= node->dn_stat.st_size) return 0; + if (! node->dn_stat.st_blocks) + /* There aren't really any blocks allocated, so just frob the size. This + is true for fast symlinks, and also apparently for some device nodes + in linux. */ + { + node->dn_stat.st_size = length; + node->dn_set_mtime = 1; + node->dn_set_ctime = 1; + diskfs_node_update (node, 1); + return 0; + } + /* * If the file is not being truncated to a block boundary, the * contents of the partial block following the end of the file must be * zeroed in case it ever becomes accessible again because of * subsequent file growth. */ - offset = length % block_size; + offset = length & (block_size - 1); if (offset > 0) { diskfs_node_rdwr (node, (void *)zeroblock, length, block_size - offset, @@ -291,7 +312,7 @@ diskfs_truncate (struct node *node, off_t length) diskfs_file_update (node, 1); } - ext2_discard_prealloc(node); + ext2_discard_prealloc (node); force_delayed_copies (node, length); @@ -304,7 +325,7 @@ diskfs_truncate (struct node *node, off_t length) node->dn_set_ctime = 1; diskfs_node_update (node, 1); - err = diskfs_catch_exception(); + err = diskfs_catch_exception (); if (!err) { block_t end = boffs_block (round_block (length)), offs; @@ -330,6 +351,8 @@ diskfs_truncate (struct node *node, off_t length) won't hurt if is wrong. */ node->dn->last_page_partially_writable = trunc_page (node->allocsize) != node->allocsize; + + diskfs_end_catch_exception (); } node->dn_set_mtime = 1; diff --git a/ext2fs/xinl.c b/ext2fs/xinl.c new file mode 100644 index 00000000..9f37e166 --- /dev/null +++ b/ext2fs/xinl.c @@ -0,0 +1,2 @@ +#define EXT2FS_DEFINE_EI +#include "ext2fs.h" |